reference resources
- 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); }