Overview
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,
stdincomes from the keyboard
For example, when we use a pipe like this:
printf "Hello\n" | ./client 127.0.0.1 12345The output of printf becomes the stdin of ./client.
stdout (standard output)
- This is the output your program writes
- By default,
stdoutis shown on the screen
stdout can be redirected to a file, for example:
./server 12345 > server_output.txtThe 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, buildsockaddr_in, and connect - Read from
stdinuntil EOF and send data in chunks - Handle partial send (send may send fewer bytes than requested)
- Handle
EINTRfor read/send properly - Close the socket and exit cleanly
Server requirements server.c
- Create a TCP listening socket
- Set
SO_REUSEADDRto allow quick restarts - Bind to
INADDR_ANYand the given port - Listen with a small backlog (e.g., 5-10)
- Accept client in a loop
- For each client: read until
EOFand write bytes tostdout - 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. UseSTDIN_FILENOfor standard inputbuf: pointer to the buffer to fill with incoming bytescount: 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 12345Server 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. useSTDOUT_FILENOfor standard outputbuf: pointer to the buffer holding the bytes you receivedcount: 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.txtRunning
Manual
- Open Terminal A and start the server:
./server 12345Keep this terminal running.
- Open Terminal B and send a message with the client:
printf "Hello\n" | ./client 127.0.0.1 12345- Look at Terminal A. You should see:
HelloNotes
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:
- Short text message
- Long alphanumeric text payload
- Long binary payload
- Sequential short messages from separate clients
- Concurrent clients sending the same message