Linux network programming-socket

Article directory

  • socket
    • create socket
    • bind socket
    • listen
    • receive request
    • establish connection
  • sockaddr
    • sockaddr_in
    • sockaddr_un
  • address translation function
    • Function to convert string into in_addr
      • inet_aton function
      • inet_pton function
      • inet_addr function
    • in_addr function to convert string
      • inet_ntoa function
      • inet_ntop function
  • Summarize

Socket

Socket is an abstraction in computer network programming that is used to communicate between processes on different computers. It is the basic building block of network communication, allowing processes to transfer data over the network. Socket is used to identify a communication endpoint on the network. This endpoint usually consists of an IP address and a port number, which can uniquely identify a specific application or process on the network. There are many types of Sockets, including Stream Sockets, which are used for reliable, stream-based communication, such as TCP protocols. There are also Datagram Sockets, used for unreliable, message-oriented communication, such as UDP protocol.

The general process for network communication is:

  1. Creation and Binding: Programming, you create Socket objects and bind them to specific IP addresses and ports. This allows other devices to connect to the Socket over the network.

  2. Connection: A client socket can connect to a server socket to establish a network connection. This is achieved through the connect function in socket programming.

  3. Listening and Accepting: Server sockets can listen for connection requests from clients and accept these connections. This is achieved through the listen and accept functions in socket programming.

  4. Sending and receiving data: Socket can be used to send and receive data. For streaming sockets, the data is a continuous stream, while for datagram sockets, the data is scattered messages.

  5. Close: When communication is complete, the socket needs to be closed to release resources.

Socket programming is commonly supported in different programming languages and is used to create a variety of network applications, including network servers, clients, P2P applications, and implementations of network communication protocols.

Create socket

The socket() function is a system call used in Linux and Unix-like systems to create a socket that can be used for network communication. The function prototype is as follows:


The parameter domain is the protocol family of the specified socket, such as AF_INET (IPv4) or AF_INET6 (IPv6) for Internet protocols, AF_UNIX or AF_LOCAL for Unix domain sockets, etc. The type parameter specifies the type of socket, which can be SOCK_STREAM (connection-oriented socket, such as TCP) or SOCK_DGRAM (connection-less socket, such as UDP), etc. The protocol parameter specifies the specific protocol used by the socket, usually 0, indicating the default protocol.

A successful socket() call returns a new file descriptor, which is used for subsequent socket operations. If the call fails, it returns -1 and sets the global variable errno to indicate the type of error.

Example: The socket() function creates a socket, specifying the address family (AF_INET represents IPv4) and the socket type (SOCK_STREAM represents TCP socket).

 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd == -1) {<!-- -->
       perror("Socket creation failed");
       exit(1);
   }

Bind socket

The bind() function is used in network programming to associate a socket with a specific IP address and port number. It is usually used on the server side, allowing the server to listen to a specific IP address and port to accept connection requests from clients. The function prototype is as follows:

The sockfd parameter is the file descriptor of the socket created previously using the socket() function. The addr parameter is a pointer to the sockaddr structure, which contains the IP address and port information to be bound. The addrlen parameter specifies the size of the addr structure. The bind() function returns 0 when successfully binding the socket to the specified address and port, -1 on failure, and sets errno to indicate the type of error.

Example: Use the struct sockaddr_in structure to configure the socket’s address information, including address family, port number, and IP address.

 // Bind port and address
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr*) & amp;server_addr, sizeof(server_addr)) == -1) {<!-- -->
        perror("Binding failed");
        exit(1);
    }

Listening

The listen() function is a system call used in network programming to set a socket to a state of listening for connection requests. Typically this only works with server-side sockets so that it can accept connection requests from clients. The following is the prototype of the listen() function:

The parameter sockfd is the file descriptor of the socket previously bound using the bind() function. The backlog parameter specifies the maximum number of client connection requests waiting in the queue. The listen() function returns 0 on success, -1 on failure, and sets errno to indicate the type of error. Once a socket is set to listening state via the listen() function, it can accept connection requests from clients. This is usually performed in the main loop on the server side to handle multiple client connection requests.

Example: Set the socket to listening mode and wait for client connections. Once a client connection request arrives, the accept function will return a new socket that can be used to communicate with the client. Here we simply print out the message that the client is connected.

 // Turn on listening mode
    if (listen(sockfd, 5) == -1) {<!-- -->
        perror("Listening failed");
        exit(1);
    }
    printf("Client connected\
");

Receive request

The server-side receives requests using the accept() function, which is a system call that is often used with socket programming, especially in TCP servers. It is used to accept a connection request from the queue of completed connections and then return a new socket file descriptor representing the accepted connection. This new descriptor is separate from the original listening socket. When the client sends a connection request to the server and the server is ready to accept the connection through the listen() system call, the connection request is placed in the queue of completed connections. The server can then use accept() to handle this request. The basic prototype of the function is as follows:


The parameter sockfd is the descriptor of the original listening socket. addr is a pointer to a structure sockaddr that will be populated with client information related to the accepted connection (such as IP address and port number). The addrlen parameter is an input/output parameter indicating the length of the structure, which should be set to the size of the addr structure before calling accept(). When accept() returns, it will be set to the size of the data actually populated into addr. If accept() succeeds, it returns a new file descriptor. On failure, it returns -1 and sets the appropriate error code.

In the TCP server, there are two important queues, namely the completed connection queue and the incomplete connection queue. When the client sends connection requests to the server, the server will put these requests into the completed connection queue, but it has not yet been accepted. they. These requests wait for the server to accept them using the accept() function. The outstanding connection queue is the queue of client connections that are undergoing a connection handshake. This is a temporary queue used to handle requests while the connection is being established. When the client sends a connection request and the server is ready to accept the connection via listen(), the connection request will move from the incomplete connection queue to the completed connection queue.
At this point, the server can use the accept() function to accept a connection request from the completed connection queue. It creates a new socket file descriptor representing this new connection, and you can then use this socket to communicate with the client.

Example: If the accept function successfully accepts the client connection, “Client connected” will be output to indicate that the client is connected to the server.

 //Accept connection
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int client_sock = accept(sockfd, (struct sockaddr*) & amp;client_addr, & amp;client_len);

    if (client_sock == -1) {<!-- -->
        perror("Accepting connection failed");
        exit(1);
    }

    printf("Client connected\
");

Establish connection

The connect() function is used to connect the client socket to the specified server address and port. This function is also a system call and is usually used in socket programming for the client to establish a connection with the server. The function prototype is as follows:

The parameter sockfd is the file descriptor of the client socket. addr is a pointer to a sockaddr structure containing server address information. The addrlen parameter is the size of the addr structure. The general process of using the connect() function is as follows:

  1. The client creates a socket, usually using the socket() function.
  2. Populates a sockaddr structure with the server’s IP address and port number.
  3. Call connect() to connect the client socket to the address and port specified by the server.

If connect() succeeds, it returns 0. On failure, it returns -1 and sets the appropriate error code. Typically, connect() is used to establish a network connection between the client and the server, which allows the client to exchange data with the server, such as obtaining web pages, downloading files, or performing other network communication operations.


Example: Set the server address and port. This is the host here, so the local loopback address is used.

//Set server address
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
inet_pton(AF_INET, "127.0.0.1", & amp;server_addr.sin_addr);

// Connect to server
if (connect(sockfd, (struct sockaddr*) & amp;server_addr, sizeof(server_addr)) == -1) {<!-- -->
    perror("Connection failed");
    exit(1);
}

printf("Connected to server\
");

sockaddr

sockaddr is a general-purpose socket address structure used in socket programming to represent and store network address information. It is an abstract structure, and its specific form depends on the protocol family of the socket (for example, IPv4, IPv6, Unix domain socket, etc.). The sockaddr structure is usually used as a basic structure, and its specific types and fields depend on the used The agreement varies.

Variants of sockaddr include sockaddr_in and sockaddr_un, among which sockaddr is used to represent unspecific protocols. It includes two fields, sa_family and sa_data. sa_family is an address family field that indicates the address type of the socket. For example, AF_INET represents an IPv4 address, AF_INET6 represents an IPv6 address, AF_UNIX represents a Unix domain socket, etc. sa_data is a byte array containing address information.


sockaddr_in

The sockaddr_in structure is used to represent IPv4 socket address information. It contains sin_family (address family, usually set to AF_INET). sin_port is a 16-bit integer used to specify the target port number for socket communication. It identifies the specific service on the server to connect to. sin_addr is a struct in_addr structure used to represent an IPv4 address. It contains the IP address of the server to connect to. sin_zero is a padding field and is usually not used. It exists for compatibility with older socket implementations.

The sockaddr_in structure is used to specify the IPv4 address and port number between the client and server to establish a network connection. It is commonly used in socket programming, such as when communicating over a network using the TCP or UDP protocols, to determine the destination server address for the communication.

In addition to IPv4, there are also structures used to represent IPv6 socket addresses. sockaddr_in6 is the structure used to represent IPv6 socket addresses. It is an important structure used in IPv6 network programming to specify the destination server address for socket communication.

The field sin6_family is usually set to AF_INET6, indicating the use of IPv6 addresses. sin6_port is a 16-bit integer that specifies the destination port number for socket communication. It identifies the specific service on the server to connect to. sin6_flowinfo is a 32-bit integer, usually not used, used to specify data flow information. sin6_addr is a struct in6_addr structure used to represent an IPv6 address. sin6_scope_id is a 32-bit integer, usually not used, that specifies the scope ID. The sockaddr_in6 structure is used to specify the IPv6 address and port number between the client and server to establish a network connection. It is commonly used in socket programming, such as when communicating over IPv6 networks using the TCP or UDP protocols, to determine the destination server address for the communication. IPv6 is the next generation Internet protocol to IPv4, providing a larger address space and other improvements to cope with growing network demands.

in_addr is a structure used to represent an IPv4 address, commonly used in socket programming and network programming.

s_addr is a 32-bit unsigned integer used to store IPv4 addresses. An IPv4 address consists of four 8-bit numbers, usually expressed in dotted decimal notation, such as “192.168.0.1”. The in_addr structure is mainly used in the sockaddr_in structure to represent the IPv4 socket address. In socket programming, you would use the in_addr structure to store and manipulate IPv4 addresses, and then nest this within a sockaddr_in structure to specify the IPv4 address of the target server for socket communication.

sockaddr_un

sockaddr_un is the socket address structure used for Unix Domain Sockets. This structure is used for communication between processes within the local host without relying on network protocols.

sun_family is usually set to AF_UNIX, indicating the use of Unix domain sockets. sun_path is a null-terminated string that specifies the file system path of the socket. Unlike a network socket’s IP address and port number, the address of a Unix domain socket is a file system path. sockaddr_un allows different processes on the same computer to identify and connect to each other via file system paths. This is very useful in local inter-process communication (IPC) because it provides a mechanism that allows processes to communicate on the same computer regardless of the network protocol.

Address conversion function

When we perform network programming, we usually use dotted decimal strings to represent IP addresses, so it involves conversion between some string representations and in_addr representations.

Function to convert string into in_addr

The functions for converting strings into in_addr can use the inet_aton, inet_pton and inet_addr functions. These functions are used to convert IP address strings in different formats into binary forms for processing in network programming.

inet_aton function

The inet_aton function is a function used to convert the string representation of the IPv4 address into a 32-bit binary form of the IPv4 address. It accepts two parameters, a pointer to the string containing the IPv4 address and a pointer to the struct in_addr structure. pointer. If the string is parsed successfully, it stores the binary representation of the IPv4 address in the struct in_addr structure and returns 1 (success); otherwise it returns 0 (failure). This function works for IPv4 address translation, but does not support IPv6. It has limited flexibility in handling special cases and errors, so may not be the best choice when dealing with IPv6 or where more error handling is required.


Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main() {<!-- -->
    const char* ip_address_str = "192.168.1.1"; // An IPv4 dotted decimal string
    struct in_addr ip_address; // used to store the converted IPv4 address

    // Use inet_aton to convert a dotted decimal string into an IPv4 address structure
    if (inet_aton(ip_address_str, & amp;ip_address) == 0) {<!-- -->
        perror("Invalid IP address");
        exit(1);
    }

    //Print the converted IPv4 address
    printf("IPv4 Address in binary: %u\
", ip_address.s_addr);

    return 0;
}

inet_pton function

inet_pton is used to convert a string representation of an IPv4 or IPv6 address into the binary form of an IP address. It accepts three parameters, the address family (AF_INET for IPv4, AF_INET6 for IPv6), a pointer to a string containing the IP address, and a pointer to the memory where the target address is stored in binary form. If the string is parsed successfully, it stores the binary representation of the IP address in the provided memory, returning 1 on success and 0 on failure. You can also use errno to get error information. This function supports translation of IPv4 and IPv6 addresses and is therefore more general, providing more error handling and support for special cases.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main() {<!-- -->
    const char* ip_address_str = "192.168.1.1"; // An IPv4 dotted decimal string
    struct in_addr ipv4_address; // used to store the converted IPv4 address

    // Use inet_pton to convert a dotted decimal string to an IPv4 address
    if (inet_pton(AF_INET, ip_address_str, & amp;ipv4_address) == 0) {<!-- -->
        perror("Invalid IP address");
        exit(1);
    }

    //Print the binary representation of the IPv4 address
    printf("IPv4 Address in binary: %u\
", ipv4_address.s_addr);

    return 0;
}

inet_addr function

inet_addr is used to convert a string representation of an IPv4 address into a 32-bit binary form of the IPv4 address. It accepts a pointer to a string containing the IPv4 address. If the string is parsed successfully, it returns the IPv4 address in 32-bit binary form, or the special value INADDR_NONE, usually -1, indicating an error. This function is not suitable for IPv6 address translation and is not flexible enough to handle special cases. It has been marked as obsolete and is not recommended for use in new code.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main() {<!-- -->
    const char* ip_address_str = "192.168.1.1"; // An IPv4 dotted decimal string
    unsigned int ipv4_address; // Used to store the converted IPv4 address

    // Use inet_addr to convert a dotted decimal string to an IPv4 address
    ipv4_address = inet_addr(ip_address_str);
    
    // If INADDR_NONE is returned, it means the conversion failed
    if (ipv4_address == INADDR_NONE) {<!-- -->
        perror("Invalid IP address");
        exit(1);
    }

    // Print the 32-bit integer representation of the IPv4 address (network byte order)
    printf("IPv4 Address in binary: %u\
", ipv4_address);

    return 0;
}

Function to convert in_addr to string

When we want to convert the IPv4 address in the in_addr structure into a string representation, we can use the inet_ntoa function and the inet_ntop function. Both functions are used to convert IP address representations in different formats so that they can be used in network programming. for processing. inet_ntoa is mainly used for IPv4, while inet_ntop supports IPv4 and IPv6. . However, it should be noted that the inet_ntoa function returns a pointer to static memory and is therefore not thread-safe in a multi-threaded environment. inet_ntop is a more general function that can be used with both IPv4 and IPv6 addresses, and it is thread-safe and therefore safer in multi-threaded environments.

inet_ntoa function

The inet_ntoa function converts an IPv4 address in 32-bit binary form to a string in dotted decimal notation. Its parameter struct in_addr in is a struct in_addr structure containing the IPv4 address in 32-bit binary form. This function returns a pointer to a string representing the IPv4 address.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main() {<!-- -->
    struct in_addr ipv4_address;
    ipv4_address.s_addr = inet_addr("192.168.1.1"); // 32-bit IPv4 address in network byte order

    if (ipv4_address.s_addr == INADDR_NONE) {<!-- -->
        perror("Invalid IP address");
        exit(1);
    }

    // Use inet_ntoa to convert the 32-bit IPv4 address to a dotted decimal string
    const char* ip_address_str = inet_ntoa(ipv4_address);

    if (ip_address_str == NULL) {<!-- -->
        perror("Conversion failed");
        exit(1);
    }

    //Print the converted dotted decimal string
    printf("IPv4 Address in dotted decimal notation: %s\
", ip_address_str);

    return 0;
}

inet_ntop function

The inet_ntop function converts the binary form of the IP address into a string representation. Its parameter int af is the address family, which can be AF_INET (IPv4) or AF_INET6 (IPv6). const void *src is a pointer to the memory of the IP address in binary form. char *dst is a pointer to a buffer used to store a string representation of an IP address. socklen_t size is the size of the buffer. This function returns a pointer to a string representation of the IP address if the conversion is successful; if it fails, it returns NULL and sets errno to indicate the error.


Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main() {<!-- -->
    char ip_address_str[INET_ADDRSTRLEN]; // Used to store the converted IPv4 address string
    struct in_addr ipv4_addr;

    //Convert IPv4 address
    inet_pton(AF_INET, "192.168.1.1", & ipv4_addr);

    // Use inet_ntop to convert the IPv4 address to a dotted decimal string
    const char* result = inet_ntop(AF_INET, & ipv4_addr, ip_address_str, sizeof(ip_address_str));

    if (result == NULL) {<!-- -->
        perror("Conversion failed");
        exit(1);
    }

    //Print the converted IPv4 address string
    printf("IPv4 Address in dotted decimal notation: %s\
", ip_address_str);

    return 0;
}

Summary

The article introduces the process of using sockets in Linux network programming, as well as the required set of interfaces. It introduces the usage of these interface functions, analyzes and introduces some of the structures, and finally introduces the address translation function. , the usage, advantages and disadvantages of the address translation function are analyzed. Coding is not easy. If the content of the article is helpful to you, please click on it. Thank you for your support.