socket closing detection and processing
Detect socket shutdown
reference SIGPIPE signal processing and sorting
When calling write, send, sendto and other sending functions, the SIGPIPE signal is triggered, resulting in the direct exit of the program.
Program received signal SIGPIPE, Broken pipe. 0x00007ffff7af2224 in write () from /lib/x86_64-linux-gnu/libc.so.6
After the program sets errno to EPIPE, the program receives the SIGPIPE signal sent by the kernel and exits.
Conditions for generating SIGPIPE:
-
Call the read method on a socket that has received a FIN packet. If the receive buffer is empty, it returns 0, which is often referred to as "connection closed".
-
When the write method is called for the first time for a socket that has received a FIN packet, if the sending buffer is OK, the write call will return the written data and send the data at the same time. However, the sent message will cause the opposite end to send back RST message. Because the socket at the opposite end has called close to completely close, it is in the state of neither sending nor receiving data. Therefore, when the write method is called the second time (assuming that after receiving RST), a SIGPIPE signal will be generated, resulting in the process exiting (which is why the SIGPIPE can be triggered only after the second write).
Handling SIGPIPE How to prevent SIGPIPEs (or handle them properly)
-
Set signal processing function
signal(SIGPIPE, SIG_IGN);
-
Set the socket option, ignore the SIGPIPE signal and process the corresponding errno instead, so that the signal processing function does not need to be installed
int set = 1; setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
Note: there is no so in Linux_ Nosigpipe signal: SO_NOSIGPIPE was not declared, but we can bring MSG on the signal calling send or recv_ NOSIGNAL
send(fd, buf, nBytes, MSG_NOSIGNAL);
The server closes the socket
The following tests are done. The following code is the client code fragment. The server uses nc to listen on a port as the server
sleep(10); char buf[32]; ret = recv(event[i].data.fd, buf, 32, MSG_NOSIGNAL); LOG_DEBUG("recv ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); ret = send(event[i].data.fd, "hello", 5, MSG_NOSIGNAL); LOG_DEBUG("send ret: %d, errno: %d, error: %s", ret, errno, strerror(errno));
Close the server within 10s of sleep, and the printed log is as follows
2021-10-23 Sat 11:38:02 [debug] epolltestconnect.c:163 In function 'server_run' --> recv ret: 0, errno: 115, error: Operation now in progress 2021-10-23 Sat 11:38:02 [debug] epolltestconnect.c:165 In function 'server_run' --> send ret: 5, errno: 115, error: Operation now in progress
wireshark captures packets as follows: close the server and capture packets
Server: 192.168.92.1:2233
Client: 192.168.92.139:port
The first three packets in the picture are three handshake packets. After three handshakes, press Ctrl + C to terminate the server. The server sends FIN to the client, and the client replies ACK.
When the client calls recv, there is a direct error. When calling send, the server will receive the data sent by the client (here are 5 bytes: hello), and then the server will reply to RST to reset the connection.
If the client sends the data first and closes the connection before the client calls recv, the client can still receive the data because the data has reached the client kernel buffer
Close the connection after the server sends data
Corresponding log
2021-10-23 Sat 12:02:25 [debug] epolltestconnect.c:163 In function 'server_run' --> recv ret: 6, errno: 115, error: Operation now in progress 2021-10-23 Sat 12:02:25 [debug] epolltestconnect.c:165 In function 'server_run' --> send ret: 5, errno: 115, error: Operation now in progress
How about calling recv and send twice in a row?
2021-10-23 Sat 12:08:56 [debug] epolltestconnect.c:163 In function 'server_run' --> recv ret: 0, errno: 115, error: Operation now in progress 2021-10-23 Sat 12:08:56 [debug] epolltestconnect.c:165 In function 'server_run' --> send ret: 5, errno: 115, error: Operation now in progress 2021-10-23 Sat 12:08:56 [debug] epolltestconnect.c:167 In function 'server_run' --> recv ret: 0, errno: 115, error: Operation now in progress 2021-10-23 Sat 12:08:56 [debug] epolltestconnect.c:169 In function 'server_run' --> send ret: 5, errno: 115, error: Operation now in progress
You can see that send will send data (it should be sent to the kernel buffer), but errno = EINPROGRESS(Operation now in progress) cannot operate on resources
That is to say, as the customer service side, if the service side is closed, and then call recv to read the message from the service network, it will return 0, and errno = EINPROGRESS; When you call send, the server will return n (n > 0) and errno = EINPROGRESS
ret = recv(server_fd, buf, bufsize, MSG_NOSIGNAL); if (ret == 0 && errno = EINPROGRESS) { // server side closed, close client side here. close(sever_fd); } ret = send(server_fd, buf, bufsize, MSG_NOSIGNAL); if (ret > 0 && errno = EINPROGRESS) { // server side closed, close client side here. close(sever_fd); }
The client closes the socket
Test method: as a client, nc connects to the server and disconnects after three handshakes.
The following is the code of the server
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len); sleep(10); ret = recv(client_fd, buf, 32, MSG_NOSIGNAL); LOG_DEBUG("recv ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); ret = send(client_fd, "hello", 5, MSG_NOSIGNAL); LOG_DEBUG("send ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); close(client_fd);
After closing the client, the server reads and writes the corresponding log
2021-10-23 Sat 12:55:40 [debug] epolltestconnect.c:219 In function 'main' --> recv ret: 0, errno: 11, error: Resource temporarily unavailable 2021-10-23 Sat 12:55:40 [debug] epolltestconnect.c:221 In function 'main' --> send ret: 5, errno: 11, error: Resource temporarily unavailable
client: 192.168.92.1
server: 192.168.92.139:2233
The errors received on the server are different
Try reading and writing many times and find that the error is still the same.
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len); sleep(10); ret = recv(client_fd, buf, 32, MSG_NOSIGNAL); LOG_DEBUG("recv ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); ret = send(client_fd, "hello", 5, MSG_NOSIGNAL); LOG_DEBUG("send ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); ret = recv(client_fd, buf, 32, MSG_NOSIGNAL); LOG_DEBUG("recv ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); ret = send(client_fd, "hello", 5, MSG_NOSIGNAL); LOG_DEBUG("send ret: %d, errno: %d, error: %s", ret, errno, strerror(errno)); close(client_fd);
The corresponding logs are as follows
2021-10-23 Sat 13:04:18 [debug] epolltestconnect.c:219 In function 'main' --> recv ret: 0, errno: 11, error: Resource temporarily unavailable 2021-10-23 Sat 13:04:18 [debug] epolltestconnect.c:221 In function 'main' --> send ret: 5, errno: 11, error: Resource temporarily unavailable 2021-10-23 Sat 13:04:18 [debug] epolltestconnect.c:223 In function 'main' --> recv ret: 0, errno: 11, error: Resource temporarily unavailable 2021-10-23 Sat 13:04:18 [debug] epolltestconnect.c:225 In function 'main' --> send ret: 5, errno: 11, error: Resource temporarily unavailable
That is, call recv to read the client on the server. If recv returns 0 and errno = EAGAIN (Resource temporarily unavailable), it means that the opposite socket is closed; If the call write returns n (n > 0) and errno = EAGAIN (Resource temporarily unavailable), it indicates that the peer socket is closed.
// MSG_NOSIGNAL indicates that the signal is ignored. If it is not ignored, SIGPIPE signal will be generated // Cause the program to exit and set errno = EPIPE ret = recv(client_fd, buf, bufsize, MSG_NOSIGNAL); if (ret == 0 && errno == EAGAIN) { // client side closed, close server side here close(client_fd); } ret = send(client_fd, buf, bufsize, MSG_NOSIGNAL); if (ret > 0 && errno == EAGAIN) { // client side closed, close server side here close(client_fd); }
If recv returns - 1 and errno = EAGAIN, it means that the client has no data and the socket is normal; If the call to send returns - 1 and errno = EAGAIN, the socket is also normal.