Fundamentals 45 min read

Understanding Sockets: Concepts, TCP/UDP, System Calls, and Sample C Code

This article explains the fundamentals of sockets as a network communication abstraction, covering process communication, TCP/UDP protocols, socket descriptors, key system calls, the TCP three‑way handshake and four‑way termination, Linux kernel basics, and provides complete C examples for a server and client.

Deepin Linux
Deepin Linux
Deepin Linux
Understanding Sockets: Concepts, TCP/UDP, System Calls, and Sample C Code

Socket is an abstract concept for network communication that provides a programming interface allowing applications to exchange data across machines. A socket endpoint consists of an IP address and a port number, enabling client‑server bidirectional communication by establishing a connection.

Inter‑process communication (IPC) on a single host uses mechanisms such as pipes, named pipes, signals, messages, shared memory, and semaphores. Networked IPC requires unique identification of processes across hosts, which is achieved by the triple (IP address, protocol, port) defined by the TCP/IP suite.

TCP (Transmission Control Protocol) offers reliable, ordered, stream‑based transmission, while UDP (User Datagram Protocol) provides an unreliable, connectionless, low‑latency service suitable for real‑time applications.

Socket as a Facade – In Unix/Linux, a socket is a special file that implements the Facade design pattern, hiding the complexity of the TCP/IP stack behind a simple set of functions such as socket() , bind() , listen() , connect() , accept() , read() , write() , and close() .

Socket Descriptor – When a socket is created, the kernel returns a small integer (the descriptor) that uniquely identifies the socket within the process. This descriptor behaves like a file descriptor and can be used with standard I/O functions.

Key System Calls

int socket(int domain, int type, int protocol); // returns sockfd

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int listen(int sockfd, int backlog);

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // returns new connection fd

ssize_t read(int fd, void *buf, size_t count);

ssize_t write(int fd, const void *buf, size_t count);

int close(int fd);

The socket() call creates a descriptor; bind() assigns an address (IP + port) to it; listen() marks it as a passive listening socket; connect() initiates a client‑side connection; accept() retrieves a pending connection and returns a new descriptor for data exchange.

TCP Three‑Way Handshake – The client sends a SYN, the server replies with SYN‑ACK, and the client finalizes with ACK, establishing a reliable connection. The four‑step termination (FIN‑ACK sequence) gracefully closes the connection, handling half‑close states and TIME_WAIT.

Linux Kernel Overview – The kernel manages memory, processes, device drivers, file systems, and networking. It provides system‑call interfaces (SCI) for user‑space programs to request services such as socket creation, file I/O, and process control. The kernel separates user space (Ring 3) from kernel space (Ring 0) to ensure security.

Example: Socket Programming in C

Server (listens on port 8000, accepts connections, receives a message, and replies):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define DEFAULT_PORT 8000
#define MAXLINE 4096
int main(int argc, char** argv) {
    int socket_fd, connect_fd;
    struct sockaddr_in servaddr;
    char buff[MAXLINE];
    int n;
    if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(DEFAULT_PORT);
    if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    if (listen(socket_fd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    printf("======waiting for client's request======\n");
    while (1) {
        if ((connect_fd = accept(socket_fd, NULL, NULL)) == -1) {
            printf("accept socket error: %s(errno: %d)", strerror(errno), errno);
            continue;
        }
        n = recv(connect_fd, buff, MAXLINE, 0);
        if (!fork()) {
            if (send(connect_fd, "Hello, you are connected!\n", 26, 0) == -1)
                perror("send error");
            close(connect_fd);
            exit(0);
        }
        buff[n] = '\0';
        printf("recv msg from client: %s\n", buff);
        close(connect_fd);
    }
    close(socket_fd);
}

Client (connects to a given IP, sends a line of text, and prints the server's reply):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv) {
    int sockfd, n, rec_len;
    char sendline[MAXLINE];
    char buf[MAXLINE];
    struct sockaddr_in servaddr;
    if (argc != 2) {
        printf("usage: ./client
\n");
        exit(0);
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8000);
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
        printf("inet_pton error for %s\n", argv[1]);
        exit(0);
    }
    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    printf("send msg to server: \n");
    fgets(sendline, MAXLINE, stdin);
    if (send(sockfd, sendline, strlen(sendline), 0) < 0) {
        printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    if ((rec_len = recv(sockfd, buf, MAXLINE, 0)) == -1) {
        perror("recv error");
        exit(1);
    }
    buf[rec_len] = '\0';
    printf("Received : %s", buf);
    close(sockfd);
    exit(0);
}

Utility functions such as inet_pton() convert textual IP addresses to binary form, and proper handling of byte order (host vs. network) is essential for portable socket programs.

C++TCPLinuxNetwork ProgrammingSocketsystem callsUDP
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.