Coding with TCP Sockets

TCP Sockets represent a basic reliable communication channel between two processes. Most commonly those processes are on separate computers, reached by routing the TCP connection over IP. This page highlights some of the most common ideas needed to program using TCP/IP sockets.

1 Address and Port

An IP address identifies a computer. A TCP port identifies a connection in one process on that computer. A TCP/IP socket is a bi-directional communication channel with an IP address and TCP port number on each end.

IP addresses are registered and controlled by a hierarchy of agencies; yours was most likely assigned by your Internet Service Provider using DHCP when you connected to the internet.

Port numbers are partially specified by IANA. Unless you are running your computer’s main implementation of one of those assigned ports, code you write should use an unassigned or ephemeral port number, such as a number in the range 49152–65535.

2 Client-Server Sockets

The most common design of a socket-based application is to have one process that is designated as the server. It first opens one socket and waits for any process to send it a message on that socket. When it gets such a message from a client the server opens a second socket on a different port and uses the first connection to tell the client about the second connection, to be used for future communication.

  1. server opens a socket S1 to listen for connection attempts, using three functions:

    1. To set up the socket with the OS, use bind
    2. To tell the OS this will be a server socket, use listen
    3. To wait for a connection, use accept
  2. client opens a socket C1 and uses it to send a connection attempt to S1, using two functions

    1. To set up the socket with the OS, use socket
    2. To connect to the server, use connect
  3. server opens a socket S2 to be the other end of the C1 connection

    This new socket is the return value of accept

  4. both read and write data to their connection socket (S2 and C1, respectively) to communicate, using two function families1 Should you use recv or read?
    In Python, the socket object only has recv, not read.
    In C, read is more versatile, working with files, pipes, and sockets, while recv only works for sockets but has a flags argument supporting additional features.
    Both also have a recvfrom which provides additional information along with the data.
    The same points apply to send and write.

    1. recv and its relatives are read-like functions with additional options for sockets.
    2. send and its relatives are write-like functions optimized for sockets.

    Note that for both of these you should avoid using very large buffers. Network packets are generally 4096 bytes or smaller, and it is more efficient to handle each packet as it arrives.

C code server
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

unsigned short s1port = /* ... */

// ... inside some function

  // prepare address information of the listening socket
  struct sockaddr_in ipOfServer;
  memset(&ipOfServer, 0, sizeof(struct sockaddr_in));
  ipOfServer.sin_family = AF_INET;
  ipOfServer.sin_addr.s_addr = htonl(INADDR_ANY);
  ipOfServer.sin_port = htons(s1port);

  // create listening socket and connect it to its address
  int listener = socket(AF_INET, SOCK_STREAM, 0);
  bind(listener, (void *)&ipOfServer , sizeof(ipOfServer));

  // use socket to wait for connections
  // if several arrive, suggest the OS queue up 20
  listen(listener, 20);

  // repeat to allow multiple clients to connect
  while(1) {
    // wait for a client connection
    int connection = accept(listener, NULL, NULL);
    
    // ... read (or recv) from and write (or send) to connection
    // ... then close(connection)
  }

  close(listener);

// ...
C code client
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

unsigned short s1port = /* ... */
const char * serverIP = /* ... */

// ... inside some function

  // prepare address information of the socket
  struct sockaddr_in ipOfServer;
  memset(&ipOfServer, 0, sizeof(struct sockaddr_in));
  ipOfServer.sin_family = AF_INET;
  ipOfServer.sin_addr.s_addr = inet_addr(serverIP);
  ipOfServer.sin_port = htons(s1port);

  // create listening socket and connect it to its address
  int connection = socket(AF_INET, SOCK_STREAM, 0);
  connect(connection, (void *)&ipOfServer, sizeof(struct sockaddr_in));
    
  // ... read (or recv) from and write (or send) to connection
  // ... then close(connection)

// ...
Python code server
from socket import socket, AF_INET, SOCK_STREAM

s1port = # ...

# create the listening socket
with socket(AF_INET, SOCK_STREAM) as listener:
  listener.bind(('', s1port))

  # use socket to wait for connections
  # if several arrive, suggest the OS queue up 20
  listener.listen(20);

  # repeat to allow multiple clients to connect
  while True:
    connection, addr = listener.accept()
    
    # ... send to and recv from connection
    # ... then connection.close()
Python code client
from socket import socket, AF_INET, SOCK_STREAM

s1port = # ...
serverIP = # ...

# create the listening socket
with socket(AF_INET, SOCK_STREAM) as connection:
  connection.connect((serverIP, s1port))

  # ... send to and recv from connection
  # (automatically closed by with block)