Tools: Linux Socket Network Programming (Theoretical Analysis + Comprehensive Examples) (2026)
What is a Socket?The Socket interface is the API for TCP/IP networks. It defines numerous functions or routines that developers can use to build applications over TCP/IP networks. To master Internet-based TCP/IP network programming, one must understand the Socket interface.Socket interfaces were originally designed for Unix operating systems. If you are familiar with Unix I/O operations, understanding Sockets becomes much easier. Network socket data transmission is a special form of I/O, and a socket is essentially a file descriptor. Similar to opening a file, there is a function call socket() that returns an integer socket descriptor. Subsequent operations such as connection establishment and data transfer are performed using this descriptor. There are two commonly used socket types: stream sockets (SOCK_STREAM) and datagram sockets (SOCK_DGRAM). Stream sockets are connection-oriented and used with TCP services; datagram sockets are connectionless and correspond to UDP services. Socket CreationTo create a socket, a program calls the socket() function, which returns a handle similar to a file descriptor. The function prototype is: The domain parameter specifies the protocol family, usually PF_INET, indicating the Internet protocol family (TCP/IP). The type parameter defines the socket type: SOCK_STREAM or SOCK_DGRAM. The Socket interface also defines raw sockets (SOCK_RAW), allowing programs to access lower-level protocols. The protocol parameter is typically set to 0. The socket() call returns an integer socket descriptor, which can be used in subsequent operations.A socket descriptor is a pointer to an internal data structure, pointing to an entry in the descriptor table. When socket() is called, the system allocates storage space for a socket data structure. The system manages the descriptor table on behalf of the application.A network connection between two programs consists of five pieces of information: communication protocol, local protocol address, local host port, remote host address, and remote protocol port. This information is stored within the socket data structure. Socket ConfigurationAfter obtaining a socket descriptor via socket(), the socket must be configured before network transmission can occur. Connection-oriented socket clients use the connect() function to store local and remote information in the socket structure. Unconnected socket clients and servers, as well as connection-oriented socket servers, use the bind() function to configure local information.The bind() function associates a socket with a port on the local machine, after which the application can listen for service requests on that port. The function prototype is: Here, sockfd is the socket descriptor returned by socket(), my_addr is a pointer to a sockaddr structure containing the local IP address and port number, and addrlen is typically set to sizeof(struct sockaddr).The struct sockaddr type is used to store socket information: The sa_family field is usually AF_INET, representing the Internet (TCP/IP) address family. The sa_data field contains the socket's IP address and port number.Another commonly used structure is: This structure is more convenient. The sin_zero field ensures sockaddr_in is the same size as sockaddr and can be zeroed using bzero() or memset(). Pointers to sockaddr_in and sockaddr can be cast interchangeably, meaning you can pass a sockaddr_in* where a sockaddr* is expected, and vice versa.When using bind(), you can use the following assignments to automatically obtain the local IP address and a randomly selected unused port: Setting my_addr.sin_port to 0 lets the system automatically choose an available port. Setting my_addr.sin_addr.s_addr to INADDR_ANY tells the system to fill in the local IP address.Note: When using bind(), sin_port and sin_addr must be converted to network byte order; sin_addr itself does not require conversion.Computers use two byte orders: big-endian (most significant byte first) and little-endian (least significant byte first). Internet data is transmitted in big-endian order. Machines using little-endian internally must convert data before transmission to avoid inconsistencies.The following functions perform byte order conversion: Connection EstablishmentConnection-oriented client programs use the connect() function to configure the socket and establish a TCP connection with a remote server. The function prototype is: sockfd is the socket descriptor returned by socket(); serv_addr is a pointer to a structure containing the remote host's IP address and port; addrlen is the size of the address structure. connect() returns -1 on error and sets errno accordingly. Client programs typically do not call bind() because they only need to know the destination IP address. The system automatically selects an unused local port and notifies the program when data arrives.connect() initiates a direct connection to the remote host. Only connection-oriented clients need to connect their socket to a remote host. Connectionless protocols never establish direct connections. Connection-oriented servers do not initiate connections; they passively listen for client requests on a port.The listen() function places the socket in passive listening mode and creates an incoming connection queue, storing service requests until the program processes them. sockfd is the socket descriptor returned by socket(); backlog specifies the maximum number of pending connections in the queue, which wait for accept() (see below). Most systems default backlog to 20. If a new request arrives when the queue is full, the socket rejects the connection, and the client receives an error.listen() returns -1 on error and sets errno.The accept() function allows a server to accept incoming client connection requests. After setting up the input queue, the server calls accept(), then sleeps until a connection request arrives. sockfd is the listening socket descriptor; addr is typically a pointer to a sockaddr_in variable that stores information about the requesting host (which host, which port); addrlen is usually a pointer to an integer with value sizeof(struct sockaddr_in). On error, accept() returns -1 and sets errno.When accept() detects a connection request on the monitored socket, the system creates a new socket, associates it with the requesting process's address, and returns its descriptor. The original socket continues listening, while data transmission occurs over the new socket. Data TransmissionThe send() and recv() functions are used for data transfer over connection-oriented sockets.send() prototype: sockfd is the socket descriptor for data transmission; msg is a pointer to the data to send; len is the data length in bytes; flags is usually 0 (refer to man pages for advanced usage).send() returns the number of bytes actually sent, which may be less than requested. Always compare the return value with len and handle partial sends appropriately.Example: sockfd is the receiving socket descriptor; buf is the buffer to store received data; len is buffer size; flags is usually 0. recv() returns the number of bytes actually received, or -1 on error with errno set.sendto() and recvfrom() are used for connectionless datagram sockets. Since no connection is established, the destination address must be specified during data transmission.sendto() prototype: This function adds two parameters: to, which contains the destination IP and port, and tolen, typically sizeof(struct sockaddr). sendto() returns the number of bytes sent or -1 on error.recvfrom() prototype: from is a sockaddr variable storing the source IP and port. fromlen is usually sizeof(struct sockaddr). On return, fromlen contains the actual size of data stored in from. recvfrom() returns the number of bytes received or -1 on error with errno set.If connect() is called on a datagram socket, send() and recv() can be used for data transfer. The socket remains a datagram socket using UDP, but the kernel automatically adds source and destination address information during transmission. Ending TransmissionAfter all data operations are complete, call close() to release the socket and stop all data operations: Alternatively, use shutdown() to close the socket partially. This function allows stopping data transmission in one direction while continuing in the other. For example, you can disable sending while still receiving data until all incoming data is read. sockfd is the socket descriptor to close. The how parameter specifies the shutdown mode: Socket Programming ExampleThe server in this example sends the string "Hello, you are connected!" to the client via a socket connection. Running the server software on the server and the client software on the client will result in the client receiving the string.Server code: The server workflow: First, socket() creates a socket. Then bind() associates it with the local address and port. listen() starts listening on that socket. When accept() receives a connection request, it creates a new socket. The server prints the client's IP address and sends the greeting via the new socket, then closes it.The fork() function creates a child process to handle data transfer. fork() returns 0 in the child process, so the if block contains child process code, executed concurrently with the parent process code that follows. The client first resolves the server's hostname to an IP address using gethostbyname(), then creates a socket and connects to the server. After a successful connection, it receives data and closes the socket.gethostbyname() performs domain name resolution. Since IP addresses are hard to remember, domain names are commonly used, requiring conversion. Function prototype: It returns a pointer to a hostent structure: On success, gethostbyname() returns a pointer to struct hostent; on failure, it returns NULL. Use herror() instead of perror() to print error messages. Connectionless client/server programs work similarly in principle to connection-oriented ones. The key difference is that connectionless clients typically do not establish a connection and must specify the remote address when sending or receiving data. Blocking and Non-blockingA blocking function prevents the program from calling another function until its task is complete. For example, when a program executes a read operation, it waits until the read finishes before proceeding. When a server reaches the accept() statement and no client connection request has arrived, it blocks, waiting for a request. This is called blocking. Non-blocking operations return immediately. For instance, if a server should only check for pending connections without waiting, the socket can be set to non-blocking mode. In non-blocking mode, accept() returns immediately if no client is waiting. Setting a socket to non-blocking enables polling of multiple sockets. Attempting to read from a non-blocking socket with no data returns immediately with -1 and sets errno to EWOULDBLOCK. However, polling wastes CPU cycles. The select() system call solves this efficiently by suspending the process while the kernel monitors a set of file descriptors. When activity occurs on any monitored descriptor, select() returns, indicating which descriptor is ready. This avoids wasteful CPU polling. The select() prototype is: readfds, writefds, and exceptfds are sets of file descriptors monitored for read, write, and exception events. To check if data can be read from standard input and a socket, add file descriptor 0 and the socket descriptor to readfds. numfds is the highest-numbered file descriptor plus one—in this case, sockfd + 1. After select() returns, readfds is modified to indicate ready descriptors. Use FD_ISSET() to test. Macros for manipulating fd_set: POP3 Client Example
This example implements a POP3 client that connects to a mail server and retrieves emails for a specified account. Commands are stored in the POPMessage array and sent in a do-while loop. A robust Linux/Unix network file transfer example: I've been studying Unix network programming recently. Initially, when transferring image files, extra bytes would appear. After testing and reviewing online articles, I discovered the issue was improper handling of the final read/write operations. I've improved the program, and it now successfully transfers various file types. Next step: implement video file transfer. This article was translated from Chinese to English with AI assistance and a light human review. The original is published at Sienovo Blog. The original Chinese source is at CSDN. Learn more about Sienovo edge AI computing. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse