Location>code7788 >text

TCP CLOSE_WAIT

Popularity:666 ℃/2024-09-19 11:31:06

The reason why CLOSE_WAIT occurs is because of a design flaw in Linux's implementation of TCP. After all, none of us are the kind of people who have traveled through the years from the invention of TCP to its widespread use, and this kind of mistake is indeed possible to make.

Close and Shutdown

First Linux closes a socket with two system callsclosecap (a poem)shutdown. This is so because TCP is a two-way communication protocol consisting of two one-way channels.closewill close both channels, and theshutdownThere is an option to turn off either one, or both. Use a table to tell the difference.

functionality shutdown close
Closing method Option to turn off read side, write side or both Simultaneous shutdown of the read and write side
Send FIN message be be
Release of resources No, until four waves are complete Yes, release immediately
transmitter buffer If the write end is turned off, the data in the transmit buffer continues to be sent If the shutdown function does not close the write side, the close function forces the write side to close, discarding the remaining data in the transmit buffer

And TCP is shutting down the read end whenThere is no method to notify the other end of the socket., which means that the client closes the read side and calls theshutdown(sock, SHUT_RD)When the kernel is used, it just marks a state in the kernel and doesn't tell the server "you can't send me data anymore".

Reproducing CLOSE_WAIT

Use the following code as the server side, and then use thenc localhost 12345, and then to nc Ctrl+C to close it. At this pointnetstat -anp | grep 12345, you will find at least one channel in the CLOSE_WAIT state.

#include <>
#include <>
#include <>
#include <arpa/>
#include <sys/>

#define PORT 12345
#define BUFFER_SIZE 1024
#include <>

void sigpipe_handler(int sig) {
  printf("SIGPIPE received!\n");
}


int main() {
  // Setting the signal processing function at the beginning of the program
  signal(SIGPIPE, sigpipe_handler);
  int server_fd, client_fd;
  struct sockaddr_in server_addr, client_addr;
  socklen_t client_addr_len = sizeof(client_addr);
  char buffer[BUFFER_SIZE];

  // establish socket
  server_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (server_fd < 0) {
    perror("socket");
    exit(EXIT_FAILURE);
  }

  // Binding address
  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(PORT);

  if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind");
    close(server_fd);
    exit(EXIT_FAILURE);
  }

  // monitor a connection
  if (listen(server_fd, 1) < 0) {
    perror("listen");
    close(server_fd);
    exit(EXIT_FAILURE);
  }

  printf("Server is listening on port %d\n", PORT);

  // accept a connection
  client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len);
  if (client_fd < 0) {
    perror("accept");
    close(server_fd);
    exit(EXIT_FAILURE);
  }

  printf("Client connected, %d\n", client_fd);

  // Reading client data(Stay connected)
  while (1) {
    ssize_t bytes_received = recv(client_fd, buffer, BUFFER_SIZE - 1, 0);
    if (bytes_received < 0) {
      perror("recv");
      break;
    }
    sleep(1);

    buffer[bytes_received] = '\0';
    printf("Received: %ld, %s\n", bytes_received, buffer);
    // send(client_fd, "pong", 4, 0);
  }

  // Close connection(analog (device, as opposed digital) CLOSE_WAIT state of affairs)
  // take note of:non-closure socket,will result in the entry of CLOSE_WAIT state of affairs
  // close(client_fd);

  // cloture server socket
  close(server_fd);

  return 0;
}

Avoiding the CLOSE_WAIT problem

  1. Use of automatic shutdown mechanism for long periods of no communication

This scenario is also available in Redis. cluster communicate bus has this scenario.

  1. send + sigpipe + close, it works but I don't think anyone does it. It's fine as a toy for yourself.

A send is performed, which triggers a sigpipe, leaving the socket in an unavailable state, but you still have to manually close. this diagram is an example. You can see that you do remove two channels with this scheme, but the fd is still not closed.

image

  1. read + close

If read returns 0, that means you should close. (I guess no one will try to write 0 into the size parameter of read ......)

  1. Multiplexing + read + close

After the client closes, the server will trigger the readable event. At this point read this socket, will return 0, which means the server side should close.

  1. Non-blocking read + close

A non-blocking read returns no data when theEAGAIN/EWOULDBLOCK, but if it returns 0, it's still time to close.

But with all the multiplexing, no one should be thinking of using non-blocking READ ...... Non-blocking WRITE is really necessary because blocking WRITE waits for at least one RTT, but READ doesn't.