TCP Handshake

Example of a basic tcp client server in C compatible with both IPv4 and IPv6. Added a bit more details on how the tcp handshake behave and how to observe it using tcpdump.

sequence diagram for the tcp handshake of our sample client server

Basic client server

Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
  int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
  if (sockfd == -1) {
    perror("Creating socket");
    return EXIT_FAILURE;
  }

  struct sockaddr_in6 addr = {
    .sin6_family = AF_INET6,
    .sin6_port = htons(9000),
    .sin6_addr = { .s6_addr = INADDR_ANY }
  };
  if (bind(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
    perror("Binding socket");
    return EXIT_FAILURE;
  }

  if (listen(sockfd, 1) < 0) {
    perror("Listening");
    return EXIT_FAILURE;
  }
  printf("Server is listening on port 9000\n");

  int clientfd = accept(sockfd, NULL, 0);
  if (clientfd == -1) {
    perror("Accepting client");
    return EXIT_FAILURE;
  }

  char* msg = "Hello there";
  if (write(clientfd, msg, strlen(msg)) < 0) {
    perror("Writing to client");
    return EXIT_FAILURE;
  }

  if (close(clientfd) < 0) {
    perror("Closing client");
    return EXIT_FAILURE;
  }

  if (close(sockfd) < 0) {
    perror("Closing server");
    return EXIT_FAILURE;
  }

  return 0;
}

Client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
  int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
  if (sockfd == -1) {
    perror("Creating socket");
    return EXIT_FAILURE;
  }

  struct sockaddr_in6 addr = { .sin6_family = AF_INET6, .sin6_port = htons(9000) };
  inet_pton(AF_INET6, "::1", &addr.sin6_addr);
  if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
    perror("Binding socket");
    return EXIT_FAILURE;
  }

  char buf[8192];
  if (recv(sockfd, buf, sizeof(buf), 0) < 0) {
    perror("Receiving data");
    return EXIT_FAILURE;
  }
  printf("%s\n", buf);

  if (close(sockfd) < 0) {
    perror("Closing client");
    return EXIT_FAILURE;
  }

  return 0;
}

TCP server

$ gcc -std=c99 -o server server.c
$ gcc -std=c99 -o client client.c
$ ./server&
[1] 16305
Server is listening on port 9000
$ ./client
Hello there
[1]  + 16305 done       ./server

tcpdump

Flags notation definition can be found in the tcpdump manual, under the chapter TCP Packets. Refer to iana rfc for more information.

$ sudo tcpdump -i lo -w /tmp/captures port 9000
dropped privs to tcpdump
tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
8 packets captured
16 packets received by filter
0 packets dropped by kernel
$ tcpdump -r /tmp/captures
reading from file /tmp/captures, link-type EN10MB (Ethernet), snapshot length 262144
14:57:51.416604 IP6 localhost.43556 > localhost.cslistener: Flags [S], seq 601017080, win 65476, options [mss 65476,sackOK,TS val 3137478573 ecr 0,nop,wscale 7], length 0
14:57:51.416625 IP6 localhost.cslistener > localhost.43556: Flags [S.], seq 3001355905, ack 601017081, win 65464, options [mss 65476,sackOK,TS val 3137478573 ecr 3137478573,nop,wscale 7], length 0
14:57:51.416636 IP6 localhost.43556 > localhost.cslistener: Flags [.], ack 1, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 0
14:57:51.416714 IP6 localhost.cslistener > localhost.43556: Flags [P.], seq 1:12, ack 1, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 11
14:57:51.416723 IP6 localhost.43556 > localhost.cslistener: Flags [.], ack 12, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 0
14:57:51.416733 IP6 localhost.cslistener > localhost.43556: Flags [F.], seq 12, ack 1, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 0
14:57:51.416754 IP6 localhost.43556 > localhost.cslistener: Flags [F.], seq 1, ack 13, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 0
14:57:51.416765 IP6 localhost.cslistener > localhost.43556: Flags [.], ack 2, win 512, options [nop,nop,TS val 3137478573 ecr 3137478573], length 0