Linux Socket Programming

A socket is just a logical endpoint for communication. They exist on the transport layer. You can send and receive things on a socket, you can bind and listen to a socket. A socket is specific to a protocol, machine, and port, and is addressed as such in the header of a packet.

Sockets allow communication between two different processes on the same or different machines.

Once a socket is setup the application can :

  • Pass data to the socket for network transmission
  • Receive data from the socket (transmitted through the network, received from some other host)

We mainly use two types of sockets:

  •  TCP socket
    • Type: SOCK_STREAM
    • reliable delivery
    • in-order guaranteed
    • connection-oriented
    • bidirectional
  •   UDP socket
    • Type: SOCK_DGRAM
    • unreliable delivery
    • no order guarantees
    • no notion of “connection” – app
    • indicates destination for each
    • packet
    • can send or receive

See my gihub repo for Codes: logo

Client Server Model

Client Server

SOCKET programming in C
please see man pages for more information
Header Files

#include <stdio.h> // general purpose
#include <stdlib.h> // general purpose
#include <string.h> // general purpose
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> // for the structures
#include <errno.h> // for perror
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include <netinet/in.h>
#include <arpa/inet.h>

The Socket API

int socket(int domain, int type, int protocol);

socket() creates an endpoint for communication and returns a descriptor.

The domain argument specifies a communication domain; this selects the protocol family which will be used for communication. These families are defined in <sys/socket.h>. The currently understood formats include:

Name Purpose
AF_UNIX, AF_LOCAL Local communication unix
AF_INET IPv4 Internet protocols ip
AF_INET6 IPv6 Internet protocols ipv6
AF_NETLINK Kernel user interface device netlink

The socket has the indicated type, which specifies the communication
semantics. Currently defined types are:

SOCK_STREAM Provides sequenced, reliable, two-way, connection-based
byte streams. An out-of-band data transmission mechanism
may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.

Bind Function

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

When a socket is created with socket(), it exists in a name space (address family) but has no address assigned to it. bind() assigns the address specified by addr to the socket referred to by the file descriptor sockfd. addrlen specifies the size, in bytes, of the address structure pointed to by addr. Traditionally, this operation is called “assigning a name to a socket”.

It is normally necessary to assign a local address using bind() before a SOCK_STREAM socket may receive connections.

The actual structure passed for the addr argument will depend on the address family. The sockaddr structure is defined as something like:

struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

sin_family = AF_INET // Specifies the address family
sin_port: // Specifies the port #(0-65535)
sin_addr: // Specifies the IP address
sin_zero: unused // unused!

The only purpose of this structure is to cast the structure pointer passed in addr in order to avoid compiler warnings.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

int status = listen(sock, queuelen);

status: 0 if listening, -1 if error
sock: integer, socket descriptor
queuelen: integer, # of active participants that can “wait” for a connection
listen is non-blocking: returns immediately

int s = accept(sock, &addr, &addrlen);

s: integer, the new socket (used for data-transfer)==
sock: integer, the orig. socket (being listened on)
addr: struct sockaddr, address of the active participant
addrlen: sizeof(addr): value/result parameter
must be set appropriately before call adjusted by OS upon return
**accept is blocking: waits for connection before returning**

int status = connect(sock, &addr, addrlen);

status: 0 if successful connect, -1 otherwise
sock: integer, socket to be used in connection
addr: struct sockaddr: address of passive participant
addrlen: integer, sizeof(addr)
**connect is blocking**

int count = send(sock, &buf, len, flags);

count: # bytes transmitted (-1 if error)
buf: void*, buffer to be transmitted
len: integer, length of buffer (in bytes) to transmit
flags: integer, special options, usually just 0

int count = recv(sock, &buf, len, flags);

count: # bytes received (-1 if error)
buf: void*, stores received bytes
len: # bytes received
flags: integer, special options, usually just 0
Calls are blocking [returns only after data is sent (to socket buf) / received]

When finished using a socket, the socket should be closed:

status = close(s);

status: 0 if successful, -1 if error
s: the file descriptor (socket being closed)
Closing a socket
closes a connection
frees up the port used by the socket

The byte ordering on the Network and the
NETWORK BYTE-ORDERING

u_long htonl(u_long x);
u_short htons(u_short x);
u_long ntohl(u_long x);
u_short ntohs(u_short x);

The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.

3 thoughts on “Linux Socket Programming

Leave a comment