Skip to Content
Course content

214: Building a Simple TCP Server

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a developer I mentored a few years back who was building a custom telemetry system for a fleet of small drones. He was trying to use a heavy-duty web framework to collect the coordinates, but the overhead was killing the drones' limited battery life and causing massive latency spikes. We spent a weekend stripping everything away and writing a raw TCP server in C. The moment he saw the data streaming in with sub-millisecond latency, he realized that while high-level libraries are great for websites, sometimes you need to talk directly to the network stack to get the performance you actually need.

When we talk about "sockets" in C, it's helpful to stop thinking about "networking" for a second and start thinking about "files." In Unix-like systems, a socket is essentially a file descriptor. You open it, you write to it, you read from it, and you close it. The only difference is that instead of pointing to a spot on your hard drive, it points to a network interface and a specific port.

Setting Up the Listening Post

To get a server running, you can't just start reading data; you have to prepare the OS to receive connections. First, you call socket() to create the endpoint. You'll use AF_INET for IPv4 and SOCK_STREAM because we want TCP—which ensures our data arrives in order and intact. If you used SOCK_DGRAM, you'd be dealing with UDP, which is faster but doesn't guarantee delivery (not what we want for a stable server).

Once you have the socket, you have to bind() it. This is where you tell the OS, "I want this specific program to own port 8080." You'll wrap your IP and port into a sockaddr_in structure. I've seen plenty of people forget to set sin_family to AF_INET, which leads to some very confusing "Invalid Argument" errors. After binding, you call listen(). This doesn't actually "do" the connecting; it just tells the kernel to start queuing up incoming connection requests in a backlog.

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // Listen on all interfaces
address.sin_port = htons(8080);       // Host-to-Network Short

bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3); // Queue up to 3 pending connections

Handshaking and Data Exchange

Now comes the part where the server actually interacts with a client. The accept() function is a "blocking" call. Your program will literally stop right there and wait until a client tries to connect. When a connection happens, accept() returns a new file descriptor. This is a crucial distinction: the original server_fd stays open to listen for more people, while the new descriptor is your private line to that specific client.

With that private line, you can use read() to get data and write() to send it back. In a real-world scenario, you'd probably wrap this in a loop or use threads to handle multiple clients, but for a simple server, a synchronous read-write cycle is the best way to understand the flow. Once you're done, you must close() the client socket, or you'll leak file descriptors until the OS refuses to let you open any more.

int addrlen = sizeof(address);
int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);

char buffer[1024] = {0};
read(new_socket, buffer, 1024);
printf("Client said: %s\n", buffer);

char *response = "Message received. System Online.";
write(new_socket, response, strlen(response));

close(new_socket);
close(server_fd);



📋 Practical Task

Build a Remote System-Status Query Server

Your task is to create a TCP server that acts as a basic status monitor. Instead of just echoing back what the client says, the server should listen for a specific "command" and respond with a status report.

Requirements:

  • The server must listen on port 9000.
  • If the client sends the string "STATUS", the server should respond with "SYSTEM_OK: CPU 12%, MEM 45%".
  • If the client sends anything else, the server should respond with "UNKNOWN_COMMAND".
  • The server should handle one request and then gracefully shut down (close both the client and server sockets).

Testing Tip: You don't need to write a separate C client to test this. Once your server is running, open a terminal and use the nc (netcat) command: nc localhost 9000. Then type "STATUS" and hit enter.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.