Getting Started with Socket Programming

Socket Programming

What is a socket?

A socket is a way to speak to other programs using standard UNIX file descriptors.

As you know, everything in Unix is a file. So when you want to communicate with another program you’re gonna do it through a file descriptor.

Socket Descriptor

A file descriptor is simply an integer associated with an open file, it can be a network connection, a FIFO, a pipe, a terminal, a real on-the-disk file, or just about anything else.

Since a socket descriptor is a file descriptor, it can be used in the same way as a file descriptor. For example, it can be passed to the read and write functions to read and write data to the socket.

It used by the operating system to keep track of the different sockets that are open and to identify which socket a particular piece of data should be sent to or received from.

Socket() function

Syntax

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
// returns: socket descriptor if OK, -1 on error

Parameters

1. Domain

The domain argument specifies a communication domain within which communication will take place. (The domains specified by POSIX.1)

The most common are:

  • AF_INET for IPv4 Internet protocols
  • AF_INET6 for IPv6 Internet protocols
  • AF_UNIX for UNIX domain protocols
  • AF_UNSPEC for unspecified

Note:

  • AF_ is short for Address Family.

  • PF_ is short for Protocol Family.

  • The AF_INET and AF_INET6 domains are used for communication over the Internet.

  • The AF_UNIX domain is used for communication within the same machine, and some systems also define the AF_LOCAL domain for communication within the same machine. AF_LOCAL is alias for AF_UNIX.

  • The AF_UNSPEC domain is used to specify that the socket can be used with any of the other domains.

2. Type

The type argument specifies the type of communication to be used by the socket. (The types specified by POSIX.1)

The most common are:

TypeDescription
SOCK_STREAMProvides sequenced, reliable, two-way, connection-based byte streams.
SOCK_DGRAMProvides fixed-length, connectionless, unreliable messages.
SOCK_SEQPACKETProvides fixed-length, sequenced, reliable, connection-oriented messages.
SOCK_RAWProvides raw network protocol access.

Note:

  • The SOCK_SEQPACKET is similar to SOCK_DGRAM, but provides additional features such as error checking and flow control to ensure that data is delivered reliably and in the correct order.

3. Protocol

The protocol argument specifies a particular protocol to be used with the socket. It’s usually 0, which select the default protocol for the given domain and socket type.

When multiple protocols are supported for the same domain and socket type, we can use the protocol argument to select a particular protocol.

Note:

  • The default protocol for the SOCK_STREAM in the AF_INET domain is TCP (RFC 793). For example: telnet, ftp, ssh, web browsers, etc.

  • The default protocol for the SOCK_DGRAM in the AF_INET domain is UDP (RFC 768). For example: DNS, DHCP, NTP, etc.

    ProtocolDescription
    IPPROTO_IPIPv4 Internet Protocol
    IPPROTO_IPV6IPv6 Internet Protocol (optional in POSIX.1)
    IPPROTO_ICMPInternet Control Message Protocol
    IPPROTO_RAWRaw IP packets protocol (optional in POSIX.1)
    IPPROTO_TCPTransmission Control Protocol
    IPPROTO_UDPUser Datagram Protocol

How socket() works?

Calling socket is similar to calling open. Both return a file descriptor that can be used for I/O.

When you done with the socket, you can close it by calling close and free the file descriptor for reuse.

Although the socket is actually a file descriptor, but you can’t use it with any function that accepts a file descriptor as an argument. Such as lseek since a socket doesn’t have a file offset.

The following table lists some common file descriptor operations, and whether they are supported by a socket.

FunctionBehavior
dupduplicate the socket
closedeallocate the socket
readread data from the socket, equivalent to recv
writewrite data to the socket, equivalent to send
selectwait for the socket to become ready for I/O
mmapUnsupported
lseekUnsupported

Socket Addressing

In order to identify the particular process that we want to communicate with, we need to specify the address of the process.

Identifying a process is done by specifying the machine’s network address and port number of the process.

Byte Order

image (Source: Hackaday - Don’t Let Endianness Flip You Around)

When we communicate with a process on the same machine, we don’t need to worry about the byte order of the data.

However, when we communicate with a process on a different machine, we need to make sure that the byte order of the data is the same on both machines.

big-endian vs little-endian

Big-endian: the most significant byte is stored in the smallest address.

The big-endian byte order is the same as the way humans write numbers.

big-endian example:
| n   | n + 1 | n + 2 | n +3 |
|-----|-------|-------|------|
| MSB |       |       | LSB  |

Little-endian: the least significant byte is stored in the smallest address.

Little-endian is the opposite of big-endian, but it’s more suitable for computers, because the CPU reads data from memory from low address to high address.

| n + 3 | n + 2 | n + 1 | n   |
|-------|-------|-------|-----|
| MSB   |       |       | LSB |

Usually, the Network protocols specify a byte ordering so that the data can be exchanged between different machines without confusing the byte order.

  • The TCP/IP protocol suite uses big-endian byte order.

byte order conversion

FunctionDescription
htons()host to network short
htonl()host to network long
ntohs()network to host short
ntohl()network to host long

h is for host and n is for network.

l is for long and s is for short.

Snytax
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

Address Structure

An address identifies a socket endpoint in a particular protocol communication domain.

Different OS may have different implementation of the address structure.

On FreeBSD, the address structure is defined as follows:

struct sockaddr {
    unsigned char   sa_len;     /* total length */
    sa_family_t     sa_family;  /* address family */
    char            sa_data[14];/* variable-length address */
};

The address structure on the Linux as follows:

struct sockaddr {
    sa_family_t sa_family;      /* address family. AF_xxx */
    char        sa_data[14];    /* 14 bytes of protocol address */
};

sa_family can be a variety of things, but it’ll be AF_INET (IPv4) or AF_INET6 (IPv6).

sa_data contains a destination address and port number for the socket.

IPv4 Address Structure

sockaddr structure for IPv4 is defined as follows: (“in” for “internet”)

// Defined in <sys/socket.h>
typedef uint16_t sa_family_t;   /* short, 2 bytes */

// Defined in <netinet/in.h>
typedef uint16_t in_port_t;     /* unsigned short, 2 bytes */
typedef uint32_t in_addr_t;     /* unsigned long, 4 bytes */

struct in_addr {
    in_addr_t s_addr;           /* 32-bit IPv4 address, network byte order, load with inet_aton() */
};
// Defined in <netinet/in.h>
struct sockaddr_in {
    sa_family_t    sin_family;  /* address family: AF_INET */
    in_port_t      sin_port;    /* port number, htons(3490), network byte order */
    struct in_addr sin_addr;    /* IPv4 address, network byte order */
    unsigned char  sin_zero[8]; /* filler */
};
// total size: 16 bytes

IPv6 Address Structure

sockaddr structure for IPv6 is defined as follows: (“in” for “internet”)

// Defined in <sys/socket.h>
typedef uint16_t sa_family_t;      /* short, 2 bytes */

// Defined in <netinet/in.h>
typedef uint16_t in_port_t;        /* unsigned short, 2 bytes */

// Defined in <netinet/in.h>
struct in6_addr {
    unsigned char   s6_addr[16];   /* IPv6 address, 16 bytes */
};

// Defined in <netinet/in.h>
struct sockaddr_in6 {
    sa_family_t     sin6_family;   /* address family: AF_INET6, 2 bytes */
    in_port_t       sin6_port;     /* port number, 2 bytes */
    uint32_t        sin6_flowinfo; /* traffic class and flow info, 4 bytes */
    struct in6_addr sin6_addr;     /* IPv6 address, 16 bytes */
    uint32_t        sin6_scope_id; /* set of interfaces for scope, 4 bytes */
};
// total: 28 bytes

Although the sockaddr_in and sockaddr_in6 structures are different, they both passed to the socket routines cast to a sockaddr structure.

sockaddr_storage

The sockaddr_storage structure is designed to be large enough to hold any address structure. Since you don’t know what address structure you’ll be dealing with, you can use this structure to hold any address structure, and then cast it to the appropriate type when you need to use it.

It is defined as follows:

// Defined in <sys/socket.h>
struct sockaddr_storage {
    sa_family_t ss_family;  /* address family */
    /* Following fields are implementation-defined */
    char        __ss_pad1[_SS_PAD1SIZE];
    int64_t     __ss_align;
    char        __ss_pad2[_SS_PAD2SIZE];
};
// total: 128 bytes

Convert between binary and string

The readable IP address like 198.168.0.1 is a numbers-and-dots notation, but stored in the computer in binary format. In order to print that readable address format, we use inet_ntop functions to convert the binary address to numbers-and-dots notation. ntop stands for “network to presentation”.

To store the IP address like 198.168.0.1 in the computer for further use, we use inet_pton functions to convert the numbers-and-dots notation to binary. pton stands for “presentation to network”.

Snytax

The inet_ntop and inet_pton functions are defined as follows:

#include <arpa/inet.h>
const char *inet_ntop(int domain, const void *restrict src, char *restrict dst, socklen_t size);
int inet_pton(int domain, const char *restrict src, void *restrict dst);
Return Value
  • inet_ntop: return a pointer to the resulting string, or NULL if an error occurred.
  • inet_pton: return 1 if the address was successfully converted, 0 if the input is not a valid network address in the specified address family, or -1 if there was an error.
Parameters
  • domain: the address family, either AF_INET or AF_INET6.
  • src: the source binary address.
  • dst: the destination text string.
  • size: the size of the buffer pointed to by str. Two constants are defined to make our job easier:
    1. INET_ADDRSTRLEN is large enough to hold a text string representing an IPv4 address
    2. INET6_ADDRSTRLEN is large enough to hold a text string representing an IPv6 address.

For example:

struct sockaddr_in sa;      // IPv4
struct sockaddr_in6 sa6;    // IPv6
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6

// IPv4 example:
char ip4[INET_ADDRSTRLEN];  // space to hold the IPv4 string
struct sockaddr_in sa;      // pretend this is loaded with something
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);

// IPv6 example:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6;    // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);

Note: The above code is not robust because there is no error checking. The inet_pton function returns -1 on error, 0 if the input is not a valid address in the specified address family, and 1 on success. So check the return value is greater than 0 to make sure the conversion is successful.

Address Lookup

getaddrinfo()

There are two functions to convert a host name to an IP address and vice versa. They are gethostbyname and gethostbyaddr.

However, they are OUT-OF-DATE and should NOT be used in new programs.

The getaddrinfo and getnameinfo functions are recommended instead.

syntax

The getaddrinfo function is defined as follows:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node,               // e.g. "www.example.com" or IP
                const char *service,            // e.g. "http" or port number
                const struct addrinfo *hints,
                struct addrinfo **res);

void freeaddrinfo(struct addrinfo *res);
Return Value

You give getaddrinfo() function three input parameters, and it gives you a pointer to a linked-list, res, of results.

Parameters

The first parameter, node, is the host name or IP address. If it is NULL, the IP address is set to INADDR_ANY for IPv4 or INADDR6_ANY_INIT for IPv6.

The second parameter, service, is the service name or port number. If it is NULL, the port number is set to 0. It can be a decimal number or a service name such as “http” or “ftp”. Here is a list of well-known port numbers.

The third parameter, hints, is a pointer to a struct addrinfo. It is used to specify the type of socket we want. If it is NULL, the default is SOCK_STREAM for TCP and AF_INET for IPv4.

The fourth parameter, res, is a pointer to a linked-list of struct addrinfo. It is used to store the host name and IP address.

Example

The following is an example of hints:

struct addrinfo{
    int ai_flags;               // AI_PASSIVE, AI_CANONNAME, etc.
    int ai_family;              // AF_INET, AF_INET6, AF_UNSPEC
    int ai_socktype;            // SOCK_STREAM, SOCK_DGRAM
    int ai_protocol;            // use 0 for "any"
    size_t ai_addrlen;          // size of ai_addr in bytes
    struct sockaddr *ai_addr;   // struct sockaddr_in or _in6
    char *ai_canonname;         // full canonical hostname
    struct addrinfo *ai_next;   // linked list, next node
}; 

int status;
struct addrinfo hints;
struct addrinfo *servinfo;       // will point to the results

memset(&hints, 0, sizeof(struct addrinfo)); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // Don't care if IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE;     // Fill in my IP for me

if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    exit(1);
}
// servinfo now points to a linked list of 1 or more struct addrinfos

// ... do everything until you don't need servinfo anymore ....
freeaddrinfo(servinfo);          // free the linked-list

Here is an example of a client who wants to connect to a server (www.example.com) on port 3490: (This does not actually connect to the server, but it sets up the structure for future connection)

int status;
struct addrinfo hints;
struct addrinfo *servinfo;       // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

// get ready to connect
status = getaddrinfo("www.example.com", "3490", &hints, &servinfo);

getnameinfo()

The getnameinfo() function is the inverse of getaddrinfo(3): it converts a socket address to a corresponding host and service, in a protocol-independent manner.

Syntax
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *serv, size_t servlen, int flags);
Return Value

Returns: 0 on success, or a non-zero error code on failure. If the return value is non-zero, it can be passed to gai_strerror() to get a human-readable string.

Parameters
  • sa, is a pointer to a struct sockaddr.
  • salen, is the length of the socket address.
  • host, is a pointer to a buffer that will contain the host name or IP address.
  • hostlen, is the length of the host buffer.
  • serv, is a pointer to a buffer that will contain the service name or port number.
  • servlen, is the length of the service buffer.
Example
struct sockaddr_in6 sa; // could be IPv4 if you want
char host[1024];
char service[20];

// pretend sa is full of good information about the host and port...
getnameinfo(&sa, sizeof sa, host, sizeof host, service, sizeof service, 0);

printf("host: %s\n", host);         // e.g. "www.example.com"
printf("service: %s\n", service);   // e.g. "http"

bind() function

Purpose

Once a socket is created, it must be bound to an address and port number so that other processes can connect to it.

The bind function is called by the server to bind a socket to a specific port number and IP address.

Syntax

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

Parameters

  • sockfd: the socket file descriptor
  • my_addr: a pointer to a struct sockaddr. It contains the port number and IP address of the server.
  • addrlen: the length of the struct sockaddr

Return value

0 on success, -1 on error and sets errno.

Example

// !!! THIS IS THE OLD WAY !!!
int sockfd;
struct sockaddr_in my_addr;

sockfd = socket(PF_INET, SOCK_STREAM, 0);

my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT); // short, network byte order
my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);

bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr);

Example with getaddrinfo()

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me (IP of the host it’s running on)

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():
bind(sockfd, res->ai_addr, res->ai_addrlen);

connect() function

Purpose

The connect function is called by the client to connect to a server.

Syntax

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Parameters

  • sockfd: the socket file descriptor
  • serv_addr: a pointer to a struct sockaddr. It contains the port number and IP address of the server.
  • addrlen: the length of the struct sockaddr

Return value

0 on success, -1 on error and set the variable errno.

Example

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; 
hints.ai_socktype = SOCK_STREAM;

// make a socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect!
connect(sockfd, res->ai_addr, res->ai_addrlen);

Note:

  • check the return value of connect to see if it was successful. -1 on error and set the variable errno.

  • In the above example, it does call bind() before connect(). This is because we don’t care about our local port number. We only care where we are connecting to. The kernel will automatically assign a port number for us, and the remote server will automatically get that port number from us.

listen() function

Purpose

The listen function is called by the server to listen for incoming connections.

Syntax

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

Parameters

  • sockfd: the socket file descriptor
  • backlog: the maximum number of pending connections in the queue until accept() called. If the queue is full, the client will receive an error with an indication of ECONNREFUSED.

Return value

0 on success, -1 on error and set the variable errno.

Example

getaddrinfo();
socket();
bind();
listen();
/* accept() goes here */

Call bind before listen to make service runnning on a specific port number.

accept() function

Purpose

The accept function is called by the server to accept an incoming connection request from a client at specified port number.

It returns a new socket file descriptor for the new connection.

Syntax

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Parameters

  • sockfd: the socket file descriptor
  • addr: a pointer to a struct sockaddr. It contains the port number and IP address of the client.
  • addrlen: the length of the struct sockaddr

Return value

On success, it returns a new socket file descriptor for the new connection. On error, it returns -1 and set the variable errno.

Example

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#define MYPORT "3490" // the port users will be connecting to
#define BACKLOG 10    // how many pending connections queue will hold
int main(void){

    struct sockaddr_storage their_addr;
    socklen_t addr_size;
    struct addrinfo hints, *res;
    int sockfd, new_fd;

    // first, load up address structs with getaddrinfo():
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // fill in my IP for me

    getaddrinfo(NULL, MYPORT, &hints, &res);

    // make a socket, bind it, and listen on it:

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    listen(sockfd, BACKLOG);

    // now accept an incoming connection:

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

    // ready to communicate on socket descriptor new_fd!
}

Note:

  • The all send() and recv() functions are called on the new socket file descriptor returned by accept().

  • If you only want to accept one connection, you can call close(sockfd) after accept() to close the listening socket to prevent other connections.

send() and recv() functions

Purpose

The send and recv functions are for communication between client and server over the stream socket or the connected datagram socket.

For unconnected datagram socket, use sendto and recvfrom functions instead.

Syntax for send()

#include <sys/types.h>
#include <sys/socket.h>

int send(int sockfd, const void *msg, int len, int flags);

Parameters for send()

  • sockfd: the socket file descriptor
  • msg: a pointer to a buffer containing the message to be sent.
  • len: the length of the message in bytes.
  • flags: a set of flags that modify the behavior of the send() call.

Return value for send()

Returns the number of bytes actually sent out. (It may be less than the number requested to be sent in the len parameter.)

On error, it returns -1 and set the variable errno.

Example for send()

char *msg = "Hello, world!";
int len;
int bytes_sent;

len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);

Syntax for recv()

#include <sys/types.h>
#include <sys/socket.h>

int recv(int sockfd, void *buf, size_t len, int flags);

Parameters for recv()

  • sockfd: the socket file descriptor to read from.
  • buf: a pointer to a buffer where the message should be stored.
  • len: the maximum length of the buffer.
  • flags: a set of flags that modify the behavior of the recv() call.

Return value for recv()

Returns the number of bytes actually read into the buffer.

-1 if an error occurred and set the variable errno.

0 if the server side has closed the connection on the socket.

Example for recv()

char buf[100];
int bytes_received;

bytes_received = recv(sockfd, buf, 100, 0);

sendto() and recvfrom() functions

Purpose

The datagram socket are not connected to each other. So we need to provide the address of the destination before sending the message.

The sendto and recvfrom functions are for communication between client and server over the unconnected datagram socket.

Syntax for sendto()

#include <sys/types.h>
#include <sys/socket.h>

int sendto(int sockfd, const void *msg, int len, unsigned int flags,
           const struct sockaddr *to, socklen_t tolen);

Parameters for sendto()

  • sockfd: the socket file descriptor
  • msg: a pointer to a buffer containing the message to be sent.
  • len: the length of the message in bytes.
  • flags: a set of flags that modify the behavior of the send() call.
  • to: a pointer to a struct sockaddr that contains the address of the destination. It can be either a struct sockaddr_in or a struct sockaddr_in6.
  • tolen: the length of the struct sockaddr. An int deep-down, can simply be set to sizeof(struct sockaddr_storage).

Return value for sendto()

sendto() returns the number of bytes actually sent (might be less than the number of bytes you told it to send!), or -1 on error.

Example for sendto()

char *msg = "Hello, world!";
int len;
int bytes_sent;
struct sockaddr_storage their_addr;
socklen_t addr_len;

len = strlen(msg);
bytes_sent = sendto(sockfd, msg, len, 0, (struct sockaddr *)&their_addr, addr_len);

Syntax for recvfrom()

#include <sys/types.h>
#include <sys/socket.h>

int recvfrom(int sockfd, void *buf, size_t len, unsigned int flags,
             struct sockaddr *from, socklen_t *fromlen);

Parameters for recvfrom()

  • sockfd: the socket file descriptor to read from.
  • buf: a pointer to a buffer where the message should be stored.
  • len: the maximum length of the buffer.
  • flags: a set of flags that modify the behavior of the recv() call.
  • from: from is a pointer to a local struct sockaddr_storage that will be filled with the IP address and port of the originating machine.
  • fromlen: fromlen is a pointer to a local int that should be initialized to sizeof *from or sizeof(struct sockaddr_storage). After the function returns, it contain the length of the address actually stored in from.

Return value for recvfrom()

Returns the number of bytes actually read into the buffer.

-1 on error and set the variable errno.

Example for recvfrom()

char buf[100];
int bytes_received;
struct sockaddr_storage their_addr;
socklen_t addr_len;

bytes_received = recvfrom(sockfd, buf, 100, 0, (struct sockaddr *)&their_addr, &addr_len);

Summary of send() and recv() functions

FunctionConnectedUnconnected
send()YesNo
recv()YesNo
sendto()NoYes
recvfrom()NoYes

If your program conect() a datagram socket, you can simply use send() and recv() functions instead of sendto() and recvfrom(). The socket itself is still a datagram socket and the packets still use UDP, but the socket interface will automatically add the destination and source information for you.

close() function

Purpose

The close() function closes a file descriptor, so that it no longer refers to any file and may be reused.

This will prevent any more reads and writes to the socket. Anyone attempting to read or write the socket on the remote end will receive an error.

Syntax for close()

int close(int sockfd);

Parameters for close()

  • sockfd: the socket file descriptor to close.

shutdown() function

Purpose

The shutdown() function disables further send or receive operations on a socket.

Syntax for shutdown()

#include <sys/socket.h>
int shutdown(int sockfd, int how);
// returns: 0 if OK, -1 on error

Parameters

  • sockfd: the socket file descriptor to shutdown.
  • how: the type of operation to disable.

The how argument specifies what types of operation will no longer be allowed.

ValueConstantDescription
0SHUT_RDDisables further receive operations.
1SHUT_WRDisables further send operations.
2SHUT_RDWRDisables further send and receive operations.

Return value

Returns 0 on success, or -1 on error and set the variable errno.

Difference between close() and shutdown()

Why do we need to shutdown a socket when we can just close it?

  • The close function will deallocate the socket descriptor only when all references to the socket are closed. Especially, if we dup the socket descriptor, the socket will not be closed until all references to the socket are closed.

  • The shutdown function will allow us to shut a socket down in one direction or both directions. For example, if we want to close the socket for receiving data, but still want to send data to the peer, we can use shutdown to close the socket for receiving data, but still keep the socket open for sending data.

  • The shutdown function will not deallocate the socket descriptor. To free the socket descriptor, we still need to call the close() function.

getpeername() function

Purpose

The getpeername() function retrieves the name of the peer connected to the socket.

Syntax for getpeername()

#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// returns: 0 if OK, -1 on error

gethostname() function

Purpose

The gethostname() function returns the null-terminated hostname in the buffer pointed to by hostname, which has a length of len bytes.

Syntax for gethostname()

#include <unistd.h>
int gethostname(char **hostname, size_t len);
// returns: 0 if OK, -1 on error

References


Last modified on 2022-12-30


Thank you for your support!