Network programming -- multiple options for sockets

reference resources

  1. TCP/IP network programming Yin Shengyu

Multiple options for sockets

Some socket options are listed in the table above. IPPROTO_IP layer options are IP protocol related matters, IPPROTO_TCP layer options are matters related to TCP protocol, sol_ The socket layer is a general option related to sockets

Read the options (Get)

#include <sys/socket.h>

int getsockopt(int sock, int level, int optname, void* optval, socklen_t* optlen);

Returns 0 on success and - 1 on failure
(1)sock
Socket file descriptor for viewing options

(2)level
Optional protocol layer to view

(3)optname
Optional item name to view

(4)optval
Save the buffer address value of the view result

(5)optlen
The buffer size passed to the fourth parameter optval. After calling the function, the number of bytes of optional information returned through the fourth parameter is saved in the variable

View socket type examples

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int tcp_sock, udp_sock;
    int sock_type;
    socklen_t optlen;
    int state;

    optlen = sizeof(sock_type);
    tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
    udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
    printf("SOCK_STREAM: %d \n", SOCK_STREAM);
    printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);

    state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    if (state)
    {
        error_handling("getsockopt() error!");
    }
    printf("Socket type two: %d \n", sock_type);

    state = getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    if (state)
    {
        error_handling("getsockopt() error!");
    }
    printf("Socket type two: %d \n", sock_type);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

Set options

#include <sys/socket.h>

int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);

Returns 0 on success and - 1 on failure
(1)sock
Used to change the optional socket file descriptor

(2)level
Optional protocol layer to change

(3)optname
Optional item name to change

(4)optval
Save the buffer address value of the option information to be changed

(5)optlen
The number of bytes of optional information passed to the fourth parameter optval

Viewing and modifying I/O buffer sizes (SO_SNDBUF and SO_RCVBUF)

SO_RCVBUF is an option related to the input buffer size, SO_SNDBUF is an output buffer size option

View the default I/O buffer size when creating a socket

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int sock;
    int snd_buf, rcv_buf, state;
    socklen_t len;

    sock = socket(PF_INET, SOCK_STREAM, 0);
    len = sizeof(snd_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
    if (state)
    {
        error_handling("getsockopt() error");
    }

    len = sizeof(rcv_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
    if (state)
    {
        error_handling("getsockopt() error");
    }
    printf("Input buffer size: %d \n", rcv_buf);
    printf("Output buffer size: %d \n", snd_buf);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

Modify I/O buffer size

The setting of buffer size needs to be handled carefully, so it will not be carried out completely according to our requirements

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int sock;
    int snd_buf = 1024*3, rcv_buf = 1024*3;
    int state;
    socklen_t len;

    sock = socket(PF_INET, SOCK_STREAM, 0);
    state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
    if (state)
    {
        error_handling("setsockopt() error!");
    }

    state = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));
    if (state)
    {
        error_handling("setsockopt() error!");
    }
    // Verify I/O buffer modifications
    len = sizeof(snd_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
    if (state)
    {
        error_handling("getsockopt() error!");
    }

    len = sizeof(rcv_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
    if (state)
    {
        error_handling("getsockopt() error!");
    }

    printf("Input buffer size: %d \n", rcv_buf);
    printf("Output buffer size: %d \n", snd_buf);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

SO_REUSEADDR

Time wait status

Usually, the client requests to disconnect first, but if the server sends a FIN message to the client first, there will be a problem when the server runs again. If you re run the server side with the same port number, a "bind() error" message will be output and cannot be run again. However, in this case, the server side can be re run in about 3 minutes

The reason for this is that the server side is in the time wait state. After four handshakes, the socket is not eliminated immediately, but in a Time-wait state over a period of time. Only the host that first disconnects (sends FIN message first) passes through the time wait state. Therefore, if the server side disconnects first, it cannot run again immediately. When the socket is in the time wait process, the corresponding port is in use. Therefore, an error occurs during the call to the bind function

In fact, whether it is server-side or client-side, the socket will have a time wait process. The socket that is disconnected first must go through the time wait process. However, there is no need to consider the client time wait state. Because the port number of the client socket is arbitrarily specified. Different from the server side, the client will dynamically allocate the port number every time it runs the program, so there is no need to pay too much attention to the time wait state

Address redistribution

Consider the emergency stop caused by system failure. At this time, you need to restart the server as soon as possible to provide services, but you must wait a few minutes because it is in the time wait state. The solution is to change so in the socket's options_ Status of reuseaddr. SO_ The default value of reuseaddr is 0, which means that the socket port number in the time wait state cannot be assigned, so you need to set this value to 1

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

#define TRUE 1
#define FALSE 0
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int serv_sock, clnt_sock;
    char message[30];
    int option, str_len;
    socklen_t optlen, clnt_adr_sz;
    struct sockaddr_in serv_adr, clnt_adr;
    if (argc != 2)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
    {
        error_handling("socket() error");
    }

    optlen = sizeof(option);
    option = TRUE;
    setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, option);

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)))
    {
        error_handling("bind() error");
    }
    if (listen(serv_sock, 5) == -1)
    {
        error_handling("listen error");
    }
    clnt_adr_sz = sizeof(clnt_adr);
    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
    
    while ((str_len = read(clnt_sock, message, sizeof(message))) != 0)
    {
        write(clnt_sock, message, str_len);
        write(1, message, str_len);
    }
    close(clnt_sock);
    close(serv_sock);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

TCP_NODELAY

Nagle algorithm

Nagle algorithm is applied to TCP layer to prevent network overload due to too much data. Only when the ACK message of the previous data is received, the Nagle algorithm sends the next data

By default, TCP sockets use the Nagle algorithm to exchange data, so they are buffered to the maximum extent until an ACK is received. The sending process without Nagle algorithm has nothing to do with ack interpretation, so the data will be sent immediately after reaching the output buffer. Not using Nagle algorithm will have a negative impact on network Traffic (Traffic: refers to network load or chaos)

However, when the network traffic is not greatly affected, the transmission speed without Nagle algorithm is faster than that with it. In general, the transmission speed can be improved without using Nagle algorithm. However, if you unconditionally give up using Nagle algorithm, you will increase too much network traffic and affect the transmission. Therefore, the Nagle algorithm should not be disabled when the data characteristics are not accurately judged

Disable Nagle algorithm

To disable Nagle, simply turn the socket option TCP_ Change nodelay to 1:

int opt_val = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, sizeof(opt_val));

Can be over TCP_ View the setting status of the Nagle algorithm with the value of nodelay:

int opt_val;
socklen_t opt_len;
opt_len = sizeof(opt_val);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, &opt_len);

Windows based implementation

There is no difference between Windows and Linux

Read options

#include <WinSock2.h>

int getsockopt(SOCKET sock, int level, int optname, char* optval, int* optlen);

Returns 0 on success and socket on failure_ ERROR
(1)sock
To view an optional socket handle
(2)level
Optional protocol layer to view
(3)optname
Optional item name to view
(4)optval
Save the buffer address value of the view result
(5)optlen
The buffer size passed to the fourth parameter optval. After the call, the variable holds the number of optional bytes returned through the fourth parameter

Modify the options

#include <WinSock2.h>

int setsockopt(SOCKET sock, int level, int optname, const char* optval, int optlen);

Returns 0 on success and socket on failure_ ERROR
(1)sock
To change the optional socket handle
(2)level
Optional protocol layer to change
(3)optname
Optional item name to change
(4)optval
Save the buffer address value of the optional information to be changed
(5)optlen
The number of bytes of optional information passed in the fourth parameter optval

Modify and view the I/O buffer size

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
void ErrorHandling(char* message);
void ShowSocketBufSize(SOCKET sock);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hSock;
	int sndBuf, rcvBuf, state;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		ErrorHandling("WSAStartup() error!");
	}

	hSock = socket(PF_INET, SOCK_STREAM, 0);
	ShowSocketBufSize(hSock);

	sndBuf = 1024 * 3;
	rcvBuf = 1024 * 3;
	state = setsockopt(hSock, SOL_SOCKET, SO_SNDBUF, (char*)&sndBuf, sizeof(sndBuf));
	if (state == SOCKET_ERROR)
	{
		ErrorHandling("setsockopt() error!");
	}

	state = setsockopt(hSock, SOL_SOCKET, SO_RCVBUF, (char*)&rcvBuf, sizeof(rcvBuf));
	if (state == SOCKET_ERROR)
	{
		ErrorHandling("setsockopt() error!");
	}

	ShowSocketBufSize(hSock);
	closesocket(hSock);
	WSACleanup();
	return 0;
}

void ShowSocketBufSize(SOCKET sock)
{
	int sndBuf, rcvBuf, state, len;

	len = sizeof(sndBuf);
	state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sndBuf, &len);
	if (state == SOCKET_ERROR)
	{
		ErrorHandling("getsockopt() error");
	}

	len = sizeof(rcvBuf);
	state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&rcvBuf, &len);
	if (state == SOCKET_ERROR)
	{
		ErrorHandling("getsockopt() error");
	}

	printf("Input buffer size: %d \n", rcvBuf);
	printf("Output buffer size: %d \n", sndBuf);
}

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

Tags: network socket computer networks

Posted on Thu, 28 Oct 2021 14:39:44 -0400 by David4321