Introduction to Sockets

Sockets provide a mechanism for interprocess communication (IPC) and network communication. They act as endpoints for sending and receiving data across a network. The socket API, often referred to as the Berkeley Sockets API, is a standard interface for network programming that has been adopted by many operating systems, including Windows through the Winsock API.

A socket can be thought of as an abstraction that allows applications to use network protocols like TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) without needing to understand the intricate details of the underlying network stack.

Socket Types

Sockets are typically characterized by their communication domain and socket type. The most common domain is the Internet domain (AF_INET for IPv4, AF_INET6 for IPv6). The primary socket types are:

  • Stream Sockets (SOCK_STREAM): Provide a reliable, ordered, and bidirectional byte stream. TCP is the protocol typically used with stream sockets. Data is sent as a stream of bytes, and the protocol ensures delivery and order.
  • Datagram Sockets (SOCK_DGRAM): Provide a fixed-size message, connectionless service. UDP is the protocol typically used with datagram sockets. Data is sent in discrete packets (datagrams), and delivery is not guaranteed.

Address Structures

To communicate, sockets need addresses that specify the network interface, port number, and IP address. Common structures include:

  • sockaddr_in (for IPv4)
  • sockaddr_in6 (for IPv6)

These structures typically contain:


struct sockaddr_in {
    sa_family_t    sin_family; /* Address family: AF_INET */
    in_port_t      sin_port;   /* Port number */
    struct in_addr sin_addr;   /* Internet address */
    char           sin_zero[8]; /* Not used */
};
                

Creating a Socket

The socket() function is used to create a new socket.

int socket(int domain, int type, int protocol);
domain: Address family (e.g., AF_INET for IPv4).
type: Socket type (e.g., SOCK_STREAM or SOCK_DGRAM).
protocol: Protocol specified by the type (usually 0 to let the system choose).
Returns a socket descriptor (a small integer) on success, or -1 on error.

Connecting

For connection-oriented sockets (like SOCK_STREAM), connect() establishes a connection to a remote host.

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: The socket descriptor returned by socket().
addr: Pointer to a sockaddr structure specifying the remote address.
addrlen: The size of the address structure.
Returns 0 on success, or -1 on error.

Listening

Server applications use bind() to associate a socket with a specific local address and port, and then listen() to prepare the socket to accept incoming connections.

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: The socket descriptor.
addr: Pointer to a sockaddr structure specifying the local address and port.
addrlen: The size of the address structure.
Returns 0 on success, or -1 on error.
int listen(int sockfd, int backlog);
sockfd: The socket descriptor.
backlog: The maximum length of the queue of pending connections.
Returns 0 on success, or -1 on error.

Accepting Connections

The accept() function is used by a server to retrieve a pending connection request. It creates a new socket for the communication with the client.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd: The listening socket descriptor.
addr: (Output) Pointer to a sockaddr structure to store the client's address.
addrlen: (Input/Output) Pointer to the size of the address structure.
Returns a new socket descriptor for the established connection on success, or -1 on error.

Sending and Receiving Data

Data is exchanged using send() (or write()) and recv() (or read()) for stream sockets, or sendto() and recvfrom() for datagram sockets.

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd: The socket descriptor.
buf: Pointer to the buffer containing data to send.
len: Number of bytes to send.
flags: Control flags.
Returns the number of bytes sent on success, or -1 on error.
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd: The socket descriptor.
buf: Pointer to the buffer where received data will be stored.
len: Maximum number of bytes to receive.
flags: Control flags.
Returns the number of bytes received on success, or -1 on error.

Closing a Socket

The close() (or closesocket() in Winsock) function deallocates the socket descriptor and frees associated resources.

int close(int sockfd);
sockfd: The socket descriptor to close.
Returns 0 on success, or -1 on error.

Full API Reference (Key Functions)

This section provides a brief overview of commonly used socket functions. For a complete list and detailed parameters, please refer to the platform-specific Winsock documentation.

Initialization and Cleanup (Winsock Specific)

int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
wVersionRequired: The requested Winsock version.
lpWSAData: Pointer to a WSADATA structure to receive details about the Winsock implementation.
Returns 0 on success, or WSANOTINITIALISED if successful initialization has not yet occurred.
int WSACleanup(void);
Performs any necessary cleanup for Winsock DLLs. Returns 0 on success, or WSANOTINITIALISED if successful initialization has not yet occurred.
int closesocket(SOCKET s);
s: A descriptor identifying an unimpeded socket.
If no error occurs, closesocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error number may be retrieved by calling WSAGetLastError.

Socket Creation and Configuration

SOCKET socket(int af, int type, int protocol);
af: Address family (e.g., AF_INET, AF_INET6).
type: Socket type (e.g., SOCK_STREAM, SOCK_DGRAM).
protocol: Protocol type (usually 0).
If no error occurs, socket returns a value of type SOCKET which is a descriptor for the new socket. Otherwise, SOCKET_ERROR is returned.
int bind(SOCKET s, const struct sockaddr *name, int namelen);
s: Descriptor identifying an unbound socket.
name: Pointer to a sockaddr structure that specifies the local address and port for the socket.
namelen: Length of the sockaddr structure.
If no error occurs, bind returns zero. Otherwise, a value of SOCKET_ERROR is returned.
int listen(SOCKET s, int backlog);
s: Descriptor identifying a bound, unconnected socket.
backlog: The maximum length of the queue of pending connections.
If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned.
SOCKET accept(SOCKET s, struct sockaddr *addr, int *addrlen);
s: Descriptor identifying a listening socket.
addr: (Output) Pointer to buffer that will receive the address of the connecting entity.
addrlen: (Input/Output) Pointer to the length of the address structure.
If no error occurs, accept returns a descriptor for a newly created socket that is connected to s through the specified address. Otherwise, SOCKET_ERROR is returned.

Data Transfer

int connect(SOCKET s, const struct sockaddr *name, int namelen);
s: Descriptor identifying an unconnected socket.
name: Pointer to a sockaddr structure that specifies the remote address and port for the connection.
namelen: Length of the sockaddr structure.
If the connection is successfully established, connect returns zero. Otherwise, it returns SOCKET_ERROR.
int send(SOCKET s, const char *buf, int len, int flags);
s: Descriptor identifying a socket.
buf: Buffer containing the data to be sent.
len: Number of bytes to send from the buffer.
flags: Flags that control the behavior of the send operation.
If no error occurs, send returns the total number of bytes sent. Otherwise, a value of SOCKET_ERROR is returned.
int recv(SOCKET s, char *buf, int len, int flags);
s: Descriptor identifying the socket.
buf: Buffer to receive incoming data.
len: Maximum number of bytes to receive.
flags: Flags that control the behavior of the receive operation.
If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned.
int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen);
s: Descriptor identifying a socket.
buf: Buffer containing the data to be sent.
len: Number of bytes to send from the buffer.
flags: Flags controlling the send operation.
to: Pointer to a sockaddr structure specifying the destination address.
tolen: Length of the destination address structure.
Returns the number of bytes sent, or SOCKET_ERROR if an error occurred.
int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen);
s: Descriptor identifying the socket.
buf: Buffer to receive incoming data.
len: Maximum number of bytes to receive.
flags: Flags controlling the receive operation.
from: (Output) Pointer to a sockaddr structure that will receive the address of the sending entity.
fromlen: (Input/Output) Pointer to the length of the address structure.
Returns the number of bytes received, or SOCKET_ERROR if an error occurred.