Master Linux TCP: System Calls, Handshakes, and Real‑World Code
This article provides a comprehensive guide to Linux TCP development, explaining the role of system calls, the three‑way handshake and four‑way termination, detailing core socket functions such as socket, bind, listen, accept, connect, read/write, recv/send, and includes complete example code for building a simple TCP server and client with troubleshooting tips.
Linux TCP Development and System Calls
System calls provide the bridge between user space and the kernel, exposing standardized interfaces for creating sockets, binding, listening, accepting, connecting, and transferring data. TCP relies on these calls to establish reliable, connection‑oriented communication.
Key System Call Functions
socket Creates a network endpoint.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
/* Example */
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // returns a non‑negative descriptor or -1 on error (e.g., EACCES)bind Associates a socket with a local address and port.
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/* sockaddr_in layout */
struct sockaddr_in {
sa_family_t sin_family; // AF_INET
in_port_t sin_port; // htons(port)
struct in_addr sin_addr; // inet_pton(...)
char sin_zero[8];
};listen Marks a bound socket as passive, ready to accept connections.
#include <sys/socket.h>
int listen(int sockfd, int backlog); // backlog commonly 128 or SOMAXCONNaccept Accepts an incoming connection and returns a new descriptor for the client.
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/* Returns a new fd; original sockfd continues listening */connect Initiates a connection from the client side (performs the TCP three‑way handshake internally).
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);read / write Generic I/O functions that also work on sockets.
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);recv / send Socket‑specific I/O with flags (e.g., MSG_DONTWAIT, MSG_NOSIGNAL).
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);close Releases a file descriptor.
#include <unistd.h>
int close(int fd);For half‑close, shutdown(fd, SHUT_WR) disables further writes while still allowing reads.
TCP Handshake Overview
The three‑way handshake consists of:
Client sends SYN (random Seq=x).
Server replies with SYN+ACK (Seq=y, Ack=x+1).
Client sends ACK (Ack=y+1). Both sides enter ESTABLISHED state.
The four‑step termination (FIN/ACK exchange) ensures each direction is closed independently, preventing data loss.
Practical Example – Simple TCP Server
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8888
#define MAX_BUFFER_SIZE 1024
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket creation failed"); return 1; }
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
perror("bind failed"); close(sockfd); return 1; }
if (listen(sockfd, 128) == -1) { perror("listen failed"); close(sockfd); return 1; }
struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr);
int connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
if (connfd == -1) { perror("accept failed"); close(sockfd); return 1; }
char buffer[MAX_BUFFER_SIZE] = {0};
ssize_t n = recv(connfd, buffer, MAX_BUFFER_SIZE-1, 0);
if (n <= 0) { perror("recv failed"); close(connfd); close(sockfd); return 1; }
buffer[n] = '\0';
printf("Received from client: %s
", buffer);
const char *resp = "Message received successfully!";
if (send(connfd, resp, strlen(resp), 0) == -1) { perror("send failed"); }
close(connfd);
close(sockfd);
return 0;
}Practical Example – Simple TCP Client
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define SERVER_IP "127.0.0.1"
#define PORT 8888
#define MAX_BUFFER_SIZE 1024
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) { perror("socket creation failed"); return 1; }
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("invalid server IP"); close(sockfd); return 1; }
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
perror("connect failed"); close(sockfd); return 1; }
const char *msg = "Hello, server!";
if (send(sockfd, msg, strlen(msg), 0) == -1) { perror("send failed"); close(sockfd); return 1; }
char buf[MAX_BUFFER_SIZE] = {0};
ssize_t n = recv(sockfd, buf, MAX_BUFFER_SIZE-1, 0);
if (n <= 0) { perror("recv failed"); close(sockfd); return 1; }
buf[n] = '\0';
printf("Received from server: %s
", buf);
close(sockfd);
return 0;
}Compilation and Execution
Compile with:
gcc -o server server.c
gcc -o client client.cRun the server first ( ./server) and then the client ( ./client). The output confirms successful connection, data exchange, and proper shutdown.
Common Issues and Solutions
Port conflicts Identify the owning process with netstat -tulnp | grep PORT or lsof -i:PORT , then terminate it ( kill PID ) or choose a different port.
Connection timeouts Adjust timeout values based on workload (e.g., 100 ms for high‑throughput services, a few seconds for typical apps). Verify network health with ping , check firewall rules, and ensure proper routing.
Data transmission anomalies Always check the return values of send and recv . Implement loops that continue until the expected byte count is transferred (see send_all / recv_all patterns). Resize buffers as needed and consider zero‑copy techniques for large payloads.
Robust Send/Receive Helpers
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1024
ssize_t send_all(int sockfd, const void *buf, size_t len) {
size_t total = 0;
const char *ptr = (const char *)buf;
while (total < len) {
ssize_t sent = send(sockfd, ptr + total, len - total, 0);
if (sent == -1) { perror("send failed"); return -1; }
total += sent;
}
return total;
}
ssize_t recv_all(int sockfd, void *buf, size_t len) {
size_t total = 0;
char *ptr = (char *)buf;
while (total < len) {
ssize_t recvd = recv(sockfd, ptr + total, len - total, 0);
if (recvd == -1) { perror("recv failed"); return -1; }
if (recvd == 0) break; // peer closed
total += recvd;
}
return total;
}These helpers guarantee that all bytes are transmitted or received, which is essential because a single send or recv may process fewer bytes than requested.
Half‑Close Example
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
int main() {
int sockfd = /* obtained via accept or connect */;
const char *msg = "Server finished sending data.";
if (send(sockfd, msg, strlen(msg), 0) == -1) { perror("send"); close(sockfd); return 1; }
if (shutdown(sockfd, SHUT_WR) == -1) { perror("shutdown"); close(sockfd); return 1; }
printf("Write side closed, waiting for client data...
");
char buf[1024] = {0};
ssize_t n = recv(sockfd, buf, sizeof(buf)-1, 0);
if (n > 0) {
buf[n] = '\0';
printf("Received after shutdown: %s
", buf);
}
close(sockfd);
return 0;
}Using shutdown(sockfd, SHUT_WR) closes only the write direction, allowing the server to continue reading client data—a useful pattern for request/response protocols.
Summary
Linux TCP development hinges on a small set of system calls ( socket, bind, listen, accept, connect, read/write or recv/send, close / shutdown). Understanding their prototypes, error handling, and the TCP state machine (three‑way handshake, four‑step termination) enables developers to build reliable network services. Proper buffer management, looped I/O, and handling of common pitfalls such as port conflicts, timeouts, and partial transfers are essential for production‑grade applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
