1. Options for sockets
- After creating sockets, you can modify socket features
- Socket options are divided into three layers: SOL_SOCKET, IPPROTO_IP and IPPROTO_TCP.
2. Relevance function
- getsockopt
#include<sys/socket.h> // Function: Get information about socket options // Parameters: // sock -- Used to view options socket file descriptors // level -- Optional protocol layer to view // optname -- Optional name to view // optval -- Save the buffer address value of the result to view // optlen -- The buffer size passed to the fourth parameter optval. After calling the function, the number of bytes in the variable that hold the optional information returned by the fourth parameter // Return value: Return 0 on success and - 1 on failure int getsockopt(int sock, int level,int optname, void* optval, socklen_t* optlen);
- setsockopt
#include<sys/socket.h> // Function: Setting socket options // Parameters: // sock -- Socket file descriptor for changing optional options // level -- Optional protocol layer to be changed // optname -- an optional name to change // Optival -- Buffer address value that holds the option information to be changed // optlen -- The number of bytes of optional information passed to the fourth parameter optval // Return value: Return 0 on success and - 1 on failure int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);
3. Get sock_type
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/socket.h> void error_handling(char* message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } int main(int argc, char* argv[]) { int tcp_sock, udp_sock; int sock_type; socklen_t 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); int state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen); if (state) error_handling("getsockopt() error!"); printf("Socket type one: %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; }
Running results: sock_type can only be obtained, not set; socket type can only be specified at the time of creation, can not be modified.
4. Get and modify socket buffer size
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/socket.h> void error_handling(char* message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } int main(int argc, char* argv[]) { int sock; int snd_buf, rcv_buf; sock = socket(PF_INET, SOCK_STREAM, 0); socklen_t len = sizeof(snd_buf); int 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("Default input buffer size: %d \n", rcv_buf); printf("Default output buffer size: %d \n", snd_buf); snd_buf = 1024 * 3; rcv_buf = 1024 * 3; 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"); 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("After setting, input buffer size: %d \n", rcv_buf); printf("After setting, output buffer size: %d \n", snd_buf); return 0; }
Running results: The size of IO buffer set is not consistent with the specified 3K. This is automatically adjusted by the system.
5.Time-wait state
- In communication, the client first requests to disconnect the link, sends FIN to the server and then goes through four "wave" processes, and then enters the time-wait state. Enter ctrl+c to force the termination of the program. At this time, the operating system closes the file and socket, which is equivalent to calling the close function, and also sends FIN.
- If the server terminates the program compulsively in the process of communication and runs the server-side program with the same port again immediately, it will enter "bind() error" and cannot run again. Wait a few minutes to run again.
- Reason analysis: First disconnected (FIN sent first) host will go through the Time-wait state, at this time the corresponding port is still in use, call bind() using the same port will make mistakes.
- The port number of the client socket is automatically assigned, so there is no need to consider the Time-wait state of the client.
- Why is there a Time-wait state? Assuming that host A cancels the socket immediately after sending the last ACK packet, but this ACK message is lost in the transmission process and cannot be transmitted to host B, host B will think that the message it sent before did not reach host A and try to retransmit, but at this time A has completely terminated and can not receive the final ACK from host B. With Time-wait status, Host A retransmits the final ACK message like Host B, and Host B can terminate normally.
- Following is the echo server/client communication process, you can find that the server-side forced termination of the program, immediately run again, will report errors.
6. Port number redistribution in Time-wait state, modifying SO_REUSEADDR option
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> void error_handling(char* message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } int main(int argc, char* argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_addr, clnt_addr; char message[30]; 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"); int option = 1; socklen_t optlen = sizeof(option); setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) error_handling("bind() error"); if (listen(serv_sock, 5) == -1) error_handling("listen() error"); socklen_t clnt_addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); if (clnt_sock == -1) error_handling("accept() error"); int str_len; // After receiving the client's message, send it back to the client 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; }
Running results: It can be seen that even if the server end of the program forcibly, immediately re-execute the program, no error.
7.Naggle algorithm
- By default, TCP uses Naggle algorithm to exchange data, so it buffers as much as possible until it receives ACK.
- It is better to disable Naggle algorithm when transferring large file data in order to improve transmission speed.
- Naggle algorithm can be disabled simply by changing TCP_NODELAY to 1
Reference Books: TCP/IP Network Programming Yin Shengyu, Translated by Jin Guozhe