Important socket options

The following two system calls are specifically used to read and set socket file descriptor attributes:

int getsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
int setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t restrict option_len);

level specifies the options (i.e. attributes) of the protocol to be operated, such as IPV4, IIPV6, TCP, etc
option_name specifies the name of the option
option_value,option_len specifies the value and length to be manipulated

It is worth noting that for the server, some socket options can only be set for listening sockets before calling the listen system call. This is because the connection socket can only be returned by the accept call, and the connection accepted from the listen listening queue has completed at least the first two steps of the three TCP handshakes (because the connection in the listen listening queue has entered the SYN\u RCVD state at least), which indicates that the server has sent a TCP synchronization message segment to the accepted connection. However, some socket options should be set in the TCP synchronization message segment (the message segment with SYN flag set), such as the TCP maximum message segment option. In this case, the solution is to set these socket options for the listening socket, and the connection socket returned by accept will automatically inherit these options.

SO_REUSEADDR option

The server program can set the socket option SO_REUSEADDR to force the use of time_ Socket address occupied by connection in wait state

SO_RCVBUF and SO_SNDBUF option

SO_RCVBUF and SO_SNDBUF indicates the size of the receive buffer and the send buffer respectively.
However, when we use setsockopt to set the send buffer of the TCP receive buffer, the system will double its value, and it must not be less than the default minimum value of a system. Because the system needs to ensure that a TCP connection has enough free buffer to handle congestion (for example, the fast retransmission algorithm hopes that the TCP receive buffer can accommodate four TCP message segments with the size of SMSS)
We write a pair of client and server programs to modify the sizes of TCP send buffer and receive buffer respectively:
Client program for modifying TCP send buffer: set_send_buf.c

#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>

#define BUFFER_SIZE 512

int main(int argc, char* argv[]) {
    if (argc <= 2) {
        printf("usage: %s ip_address port_number send_buffer_size", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server_address;
    bzero(&server_address, sizeof(server_address));
    server_address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &server_address.sin_addr);
    server_address.sin_port = htons(port);

    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    int sendbuf = atoi(argv[3]);
    int len = sizeof(sendbuf);

    // First set the size of the TCP send buffer, and then read it immediately
    setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));
    // getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (struct socklen*)&len);
    getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t*)&len);
    printf("the tcp send buffer size after setting is %d\n", sendbuf);
    
    if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) != -1) {
        char buffer[BUFFER_SIZE];
        memset(&buffer, '\0', BUFFER_SIZE);
        send(sock, &buffer, BUFFER_SIZE, 0);
    }

    close(sock);
    return 0;
}

Modify the server program of TCP accept buffer: set_recv_buf.c

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>

#define BUFFER_SIZE     1024
#define BACKLOG         5

int main(int argc, char* argv[]) {
    if (argc <= 2) {
        printf("usage: %s ip_address port_number recv_buffer_size", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(sockfd >= 0);

    int recvbuf = atoi(argv[3]);
    int len = sizeof(recvbuf);

    // First set the size of the TCP accept buffer, and then read it immediately
    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
    getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t*)&len);
    printf("the tcp recvive buffer size after setting is %d\n", recvbuf);

    int ret = bind(sockfd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(sockfd, BACKLOG);
    assert(ret != -1);

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    // int connfd = accept(sockfd, &client, &client_addrlength);
    int connfd = accept(sockfd, (struct sockaddr*)&client, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        char buffer[BUFFER_SIZE];
        memset(buffer, '\0', sizeof(buffer));
        while (recv(connfd, &buffer, BUFFER_SIZE - 1, 0) > 0) {}
        close(connfd);
    }

    close(sockfd);
    return 0;
} 

We set the receive buffer size to 50 and the send buffer size to 2000. The following results are obtained

They have not been set according to the 50 and 2000 required by me. It should be because the minimum value of the system's current receive buffer is 2304 and the minimum value of the send buffer is 4608

Next, set the receive buffer to 1000, 1152

Still 2304
But if it is set to 1153, then

The buffer is set to twice what I asked for, 2306. Note that if our setting is still less than the minimum value required by the system after doubling, the minimum value required by the system will be adopted, otherwise it will be multiplied by 2

SO_RCVLOWAT and SO_SNDWAT options

SO_RCVLOWAT and so_ The sndlowat option indicates the low water mark of TCP receive buffer and send buffer respectively. They are generally called by the IO multiplexing system to determine whether the socket is readable or writable. When the total number of readable data in the TCP receive buffer is greater than its low watermark, the I/O multiplexing system call will notify the application that the corresponding socket Read data on; When the free space (the space where data can be written) in the TCP send buffer is greater than its low watermark, the I/O multiplexing system call will notify the application that it can go to the corresponding socke. Write personal data. By default, the low watermark of the TCP receive buffer and the low watermark of the TCP send buffer are both 1 byte.

SO_ Ringer options

Controls the behavior of the close system call when closing a TCP connection.

Posted by eugene2009 on Fri, 03 Jun 2022 06:47:33 +0530