C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
149: Socket Programming in C++
I've been meaning to show you how sockets actually work. Most people treat them like magic black boxes provided by a library, but if you've ever wondered why a connection "hangs" or why data sometimes arrives in fragments, you have to go down to the Berkeley sockets API. It's a bit clunky—it's essentially a C API—but that's where the real understanding happens.
I want to build a tiny "System Health Daemon." The idea is simple: a server that sits on my machine, and when a client sends the word STATUS, the server replies with the current system uptime. Let's try to get a basic listener running.
Getting a handle on the network
First, I need a socket. In C++, especially on Unix-like systems, a socket is essentially just a file descriptor. I'll start by calling socket(). I'm going for TCP, so I'll use SOCK_STREAM.
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("Socket failed");
return -1;
}
Okay, that worked. But a socket is just a generic endpoint. It doesn't know where it lives on the network. I need to bind it to an IP address and a port. I'll use INADDR_ANY because I don't care which network interface the request comes through—WiFi, Ethernet, whatever.
The port permission trap
Here is where I usually trip up when I'm rushing. I tried to bind this to port 80 because, well, it's the standard HTTP port. I ran the code and... bind() returned -1. I checked errno and it was EACCES (Permission Denied).
Right. I forgot that on Linux/macOS, you can't bind to any port below 1024 unless you're running as root. I'm not running this as sudo, so I'll bump it up to 8080. Let's see if that clears it.
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080); // htons is critical; it handles Big Endian vs Little Endian
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
// Now it actually works!
}
Note that htons() call. If you forget "host-to-network-short," your port number will be byte-swapped, and you'll spend an hour wondering why your client can't connect to a port that looks correct in your code but is actually something completely different in the eyes of the kernel.
Waiting for someone to talk back
Now the socket is bound, but it's not actually "listening." It's just a bound file descriptor. I need to put it in passive mode using listen(), and then call accept().
I ran my code, and then it just... stopped. The terminal froze. I thought I'd crashed the program, but then I realized: accept() is a blocking call. It's literally putting my thread to sleep until a client attempts to connect. This is a crucial realization. If you want a server that does other things while waiting, you'll eventually need threads or select()/poll()/epoll(), but for our health daemon, we can just wait.
listen(server_fd, 3); // Allow a backlog of 3 connections
int addrlen = sizeof(address);
int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
// The moment a client connects, we land here.
The stream vs. packet realization
Now for the actual logic. I'll read from new_socket. I'm expecting the string "STATUS". I'll use read() into a buffer. Here's the thing that bites everyone: TCP is a stream protocol, not a packet protocol. If the client sends "STATUS", there is no guarantee that read() will return all 6 bytes in one go. It might return "STAT" and then "US" in the next call.
For this tiny example, I'll keep it simple and assume it arrives together, but in a production system, you'd need a loop and a delimiter (like a newline \n) to know when a message is actually complete. Let's try to send the response back.
char buffer[1024] = {0};
read(new_socket, buffer, 1024);
if (std::string(buffer) == "STATUS") {
const char* response = "System OK - Uptime: 14 hours\n";
send(new_socket, response, strlen(response), 0);
}
close(new_socket);
close(server_fd);
I tested this with telnet localhost 8080 (or nc localhost 8080 if you're on Linux), typed "STATUS", and there it was. It's not fancy, and it's certainly not thread-safe, but we've just bypassed the high-level abstractions and talked directly to the OS network stack.
📋 Practical Task
Build a Remote Command-Response Daemon
Your task is to expand on the System Health Daemon. Instead of just responding to "STATUS", create a server that supports three specific commands:
TIME: The server should respond with the current system time (usestd::timeandstd::ctime).PING: The server should respond with "PONG".QUIT: The server should send a "Goodbye" message and then shut down the server entirely.
Requirements:
- The server must listen on port 9000.
- You must handle the
read()buffer carefully to ensure you don't have trailing null characters or garbage interfering with your string comparisons. - The server should be able to handle multiple clients sequentially (meaning it stays in a loop, accepting one client, processing their command, closing that connection, and then waiting for the next client) until the
QUITcommand is received.
There are no comments for now.