Overview

Repository

For this project, I wrote a TCP client/server pair in C. Sockets are the standard interface for sending and receiving data over a network. Even though this is project is tested on a single machine (localhost), the same APIs are used for communication across different machines.

When you use 127.0.0.1, your OS still creates a real TCP connection. The same TCP/IP stack handles connection setup, buffering, and delivery. The only difference is that the packets loop back inside your machine instead of going out over a physical network.

Basic terminal concepts

You will use the terminal to run programs. Two important concepts:

stdin (standard input)

  • This is the input data your program reads
  • By default, stdin comes from the keyboard

For example, when we use a pipe like this:

printf "Hello\n" | ./client 127.0.0.1 12345

The output of printf becomes the stdin of ./client.

stdout (standard output)

  • This is the output your program writes
  • By default, stdout is shown on the screen

stdout can be redirected to a file, for example:

./server 12345 > server_output.txt

The server writes received bytes to stdout, so you can see what arrived or redirect it to a file for comparison.

What is implemented

Client requirements client.c

  • Create a TCP socket
  • Parse server ip/port, build sockaddr_in, and connect
  • Read from stdin until EOF and send data in chunks
  • Handle partial send (send may send fewer bytes than requested)
  • Handle EINTR for read/send properly
  • Close the socket and exit cleanly

Server requirements server.c

  • Create a TCP listening socket
  • Set SO_REUSEADDR to allow quick restarts
  • Bind to INADDR_ANY and the given port
  • Listen with a small backlog (e.g., 5-10)
  • Accept client in a loop
  • For each client: read until EOF and write bytes to stdout
  • Handle partial writes and EINTR
  • Keep running unless terminated by an external signal

What is required

Client sending rules

  • Read from standard input using read()
  • read(fd, buf, count) takes:
    • fd: the file descriptor to read from. Use STDIN_FILENO for standard input
    • buf: pointer to the buffer to fill with incoming bytes
    • count: maximum number of bytes to read into that buffer
  • For each chunk read, send exactly those bytes to the server
  • send() can send fewer bytes than requested, so you must loop until the entire chunk is transmitted
  • If you want to feed a file as input, you can redirect or use cat:
./client 127.0.0.1 12345 < input.txt
cat input.txt | ./client 127.0.0.1 12345

Server output rules

  • Output is raw bytes only as sent from the client
  • When you receive bytes from a client into a buffer, write those same bytes to standard output using write()
  • write(fd, buf, count) takes:
    • fd: the file descriptor to write to. use STDOUT_FILENO for standard output
    • buf: pointer to the buffer holding the bytes you received
    • count: number of bytes to write from that buffer
  • write() can write fewer bytes than requested, so you must loop until all bytes from the received chunk are written
  • Save server output to a file by redirecting stdout:
./server 12345 > server_output.txt

Running

Manual

  1. Open Terminal A and start the server:
./server 12345

Keep this terminal running.

  1. Open Terminal B and send a message with the client:
printf "Hello\n" | ./client 127.0.0.1 12345
  1. Look at Terminal A. You should see:
Hello

Notes

The OS assigns a temporary client port automatically. The connection is identified by:

client_ip:client_port -> server_ip:server_port

So both sides don’t use the same port

Automated

Automated tests included:

  1. Short text message
  2. Long alphanumeric text payload
  3. Long binary payload
  4. Sequential short messages from separate clients
  5. Concurrent clients sending the same message

Reference tutorial

Beej’s Guide to Network Programming

1 item under this folder.