TCP server
The sample demonstrates how to create a TCP server using the BSD socket API. The server binds to a local port, listens for incoming connections, and accepts clients one at a time. For each client it reads one message, prints it to stdout, and closes the connection.
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 9000
#define BUFFER_SIZE 256
#define BACKLOG 5
int main() {
// create a TCP socket (IPv4, stream-based, default protocol)
int serverFd = socket(AF_INET, SOCK_STREAM, 0);
if (serverFd == -1) {
std::cerr << "Socket failed: " << strerror(errno) << std::endl;
return errno;
}
// allow the port to be reused immediately after the server exits;
// without this, bind() would fail for ~60 seconds after the previous run
int opt = 1;
int reuseResult = setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (reuseResult == -1) {
std::cerr << "Setsockopt failed: " << strerror(errno) << std::endl;
close(serverFd);
return errno;
}
// describe the address the server will bind to:
// accept connections on any network interface, on PORT
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT); // htons converts to network byte order
// bind the socket to the local address and port
int bindResult = bind(serverFd, (struct sockaddr*) &serverAddr, sizeof(serverAddr));
if (bindResult == -1) {
std::cerr << "Bind failed: " << strerror(errno) << std::endl;
close(serverFd);
return errno;
}
// mark the socket as passive — it will only accept incoming connections
// BACKLOG is the maximum number of pending connections to queue
int listenResult = listen(serverFd, BACKLOG);
if (listenResult == -1) {
std::cerr << "Listen failed: " << strerror(errno) << std::endl;
close(serverFd);
return errno;
}
std::cout << "[Server] Listening on port " << PORT << std::endl;
// keep accepting clients until the program is interrupted
while (true) {
// this structure will be filled with the connecting client's address
struct sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);
// block here until a client connects; accept() returns a NEW socket
// dedicated to that one client — serverFd keeps listening
int clientFd = accept(serverFd, (struct sockaddr*) &clientAddr, &clientLen);
if (clientFd == -1) {
std::cerr << "Accept failed: " << strerror(errno) << std::endl;
continue;
}
// convert the binary client IP to a human-readable string
char clientIp[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIp, sizeof(clientIp));
std::cout << "[Server] Client connected: "
<< clientIp << ":" << ntohs(clientAddr.sin_port) << std::endl;
// read up to BUFFER_SIZE-1 bytes sent by the client
char buffer[BUFFER_SIZE];
memset(buffer, 0, sizeof(buffer));
ssize_t bytesRead = recv(clientFd, buffer, sizeof(buffer) - 1, 0);
if (bytesRead > 0) {
std::cout << "[Server] Received (" << bytesRead << " bytes): " << buffer << std::endl;
} else if (bytesRead == 0) {
// the client closed the connection before sending any data
std::cout << "[Server] Client disconnected without sending data." << std::endl;
} else {
std::cerr << "[Server] Recv failed: " << strerror(errno) << std::endl;
}
// close the per-client socket; the listening socket stays open
close(clientFd);
std::cout << "[Server] Connection closed." << std::endl;
}
// close the listening socket (not reached in this example)
close(serverFd);
return 0;
}The file can be compiled and executed as follows:
g++ tcp-server.cpp -o tcp-server
./tcp-serverConnect with nc from another terminal to test:
echo "Hello, server!" | nc 127.0.0.1 9000The server output should look like:
[Server] Listening on port 9000
[Server] Client connected: 127.0.0.1:54312
[Server] Received (15 bytes): Hello, server!
[Server] Connection closed.