为什么总是5个连接没有附加程序?

时间:2013-06-01 08:40:47

标签: linux networking netstat lsof

此问题与Network port open, but no process attached?netstat shows a listening port with no pid but lsof does not类似。但是他们的答案无法解决我的问题,因为它太奇怪了。

我有一个名为lps的服务器应用程序,它等待端口8588上的tcp连接。

[root@centos63 lcms]# netstat -lnp | grep 8588   
tcp        0      0 0.0.0.0:8588                0.0.0.0:*                   LISTEN          6971/lps

正如您所看到的,侦听套接字没有任何问题,但当我将数千个测试客户端(由另一位同事编写)连接到服务器时,无论是2000,3000还是4000.总有5个客户端(连接并向服务器发送登录请求,但也无法接收任何响应。以3000个客户为例。这是netstat命令给出的内容:

[root@centos63 lcms]# netstat -nap | grep 8588 | grep ES | wc -l
3000

这是lsof命令输出:

[root@centos63 lcms]# lsof -i:8588 | grep ES | wc -l
2995

这里有5个连接:

[root@centos63 lcms]# netstat -nap | grep 8588 | grep -v 'lps'                   
tcp    92660      0 192.168.0.235:8588          192.168.0.241:52658         ESTABLISHED -                   
tcp    92660      0 192.168.0.235:8588          192.168.0.241:52692         ESTABLISHED -                   
tcp    92660      0 192.168.0.235:8588          192.168.0.241:52719         ESTABLISHED -                   
tcp    92660      0 192.168.0.235:8588          192.168.0.241:52721         ESTABLISHED -                   
tcp    92660      0 192.168.0.235:8588          192.168.0.241:52705         ESTABLISHED -                   

上面的5显示它们在端口8588上连接到服务器但没有附加程序。第二列(RECV-Q)随着客户端发送请求而不断增加。

上面的链接说明了NFS mount和RPC。至于RPC,我使用命令rcpinfo -p,结果与端口8588无关。而NFS挂载,nfssta输出显示为Error: No Client Stats (/proc/net/rpc/nfs: No such file or directory).

问题:这怎么可能发生?总是5,也不是来自同一个5个客户。我不认为这是端口冲突,因为其他客户端也连接到相同的服务器IP和端口,它们都由服务器正确处理。

注意:我正在使用Linux epoll来接受客户端请求。我还在我的程序中编写调试代码并记录accept返回但无法找到5个连接的每个套接字(以及客户端的信息)。这是uname -a输出:

Linux centos63 2.6.32-279.el6.x86_64 #1 SMP Fri Jun 22 12:19:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

感谢您的帮助!我真的很困惑。


更新 2013-06-08: 将系统升级到CentOS 6.4后,会出现同样的问题。最后,我回到了epoll,发现this page说set touch fd是非阻塞的,accept直到EAGAINEWOULDBLOCK错误返回。是的,它有效。没有更多的连接待定。但那是为什么呢? Unix网络编程第1卷

accept is called by a TCP server to return the next completed connection from the 
front of the completed connection queue. If the completed connection queue is empty,
the process is put to sleep (assuming the default of a blocking socket).

因此,如果队列中仍有一些已完成的连接,为什么进程会进入休眠状态?

更新 2013-7-1: 我在添加监听套接字时使用EPOLLET,所以如果在EAGAIN遇到read之前没有保持接受,我就无法接受。我刚刚意识到这个问题。我的错。请记住:如果使用accept,即使它是侦听套接字,也始终EAGAINEPOLLET到{{1}}。再次感谢Matthew为我提供测试程序。

1 个答案:

答案 0 :(得分:1)

我尝试使用以下参数复制您的问题:

  1. 服务器使用epoll来管理连接。
  2. 我建立了3000个连接。
  3. 连接正在阻止。
  4. 服务器基本上“减少”为仅处理连接并执行非常复杂的工作。
  5. 我不能复制这个问题。这是我的服务器源代码。

    #include <stddef.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    #include <errno.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    
    #include <err.h>
    #include <sysexits.h>
    #include <string.h>
    #include <unistd.h>
    
    struct {
      int numfds;
      int numevents;
      struct epoll_event *events;
    } connections = { 0, 0, NULL };
    
    static int create_srv_socket(const char *port) {
      int fd = -1;
      int rc;
      struct addrinfo *ai = NULL, hints;
    
      memset(&hints, 0, sizeof(hints));
      hints.ai_flags = AI_PASSIVE;
    
      if ((rc = getaddrinfo(NULL, port, &hints, &ai)) != 0)
        errx(EX_UNAVAILABLE, "Cannot create socket: %s", gai_strerror(rc));
    
      if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
        err(EX_OSERR, "Cannot create socket");
    
      if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
        err(EX_OSERR, "Cannot bind to socket");
    
      rc = 1;
      if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0)
        err(EX_OSERR, "Cannot setup socket options");
    
      if (listen(fd, 25) < 0)
        err(EX_OSERR, "Cannot setup listen length on socket");
    
      return fd;
    }
    
    static int create_epoll(void) {
      int fd;
      if ((fd = epoll_create1(0)) < 0)
        err(EX_OSERR, "Cannot create epoll");
      return fd;
    }
    
    static bool epoll_join(int epollfd, int fd, int events) { 
      struct epoll_event ev;
      ev.events = events;
      ev.data.fd = fd;
    
      if ((connections.numfds+1) >= connections.numevents) {
        connections.numevents+=1024;
        connections.events = realloc(connections.events, 
          sizeof(connections.events)*connections.numevents);
        if (!connections.events)
          err(EX_OSERR, "Cannot allocate memory for events list");
      }
    
      if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        warn("Cannot add socket to epoll set");
        return false;
      }
    
      connections.numfds++;
      return true;
    }
    
    static void epoll_leave(int epollfd, int fd) {
      if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) < 0)
        err(EX_OSERR, "Could not remove entry from epoll set");
    
      connections.numfds--;
    }
    
    
    static void cleanup_old_events(void) {
      if ((connections.numevents - 1024) > connections.numfds) {
        connections.numevents -= 1024;
        connections.events = realloc(connections.events,
          sizeof(connections.events)*connections.numevents);
      }
    }
    
    
    static void disconnect(int fd) {
      shutdown(fd, SHUT_RDWR);
      close(fd);
      return;
    }
    
    static bool read_and_reply(int fd) {
      char buf[128];
      int rc;
      memset(buf, 0, sizeof(buf));
    
      if ((rc = recv(fd, buf, sizeof(buf), 0)) <= 0) {
        rc ? warn("Cannot read from socket") : 1;
        return false;
      }
    
      if (send(fd, buf, rc, MSG_NOSIGNAL) < 0) {
        warn("Cannot send to socket");
        return false;
      }
    
      return true;
    }
    
    int main()
    {
      int srv = create_srv_socket("8558");
      int ep = create_epoll();
      int rc = -1;
      struct epoll_event *ev = NULL;
    
      if (!epoll_join(ep, srv, EPOLLIN)) 
        err(EX_OSERR, "Server cannot join epollfd");
    
      while (1) {
        int i, cli;
    
        rc = epoll_wait(ep, connections.events, connections.numfds, -1);
        if (rc < 0 && errno == EINTR)
          continue;
        else if (rc < 0)
          err(EX_OSERR, "Cannot properly perform epoll wait");
    
        for (i=0; i < rc; i++) {
          ev = &connections.events[i];
    
          if (ev->data.fd != srv) {
    
            if (ev->events & EPOLLIN) {
              if (!read_and_reply(ev->data.fd)) {
                epoll_leave(ep, ev->data.fd);
                disconnect(ev->data.fd);
              }
            } 
    
            if (ev->events & EPOLLERR || ev->events & EPOLLHUP) {
              if (ev->events & EPOLLERR)
                warn("Error in in fd: %d", ev->data.fd);
              else
                warn("Closing disconnected fd: %d", ev->data.fd);
    
              epoll_leave(ep, ev->data.fd);
              disconnect(ev->data.fd);
            }
    
          }
          else {
    
            if (ev->events & EPOLLIN) {
              if ((cli = accept(srv, NULL, 0)) < 0) {
                warn("Could not add socket");
                continue;
              }
    
              epoll_join(ep, cli, EPOLLIN);
            }
    
            if (ev->events & EPOLLERR || ev->events & EPOLLHUP)
              err(EX_OSERR, "Server FD has failed", ev->data.fd);
    
          }
        }
    
        cleanup_old_events();
      }
    
    }
    

    这是客户:

    from socket import *
    import time
    scks = list()
    
    for i in range(0, 3000):
      s = socket(AF_INET, SOCK_STREAM)
      s.connect(("localhost", 8558))
      scks.append(s)
    
    time.sleep(600)
    

    在我的本地机器上运行时,我使用端口8558(1个侦听,3000个客户端插槽和3000个服务器端插槽)获得6001个插槽。

    $ ss -ant | grep 8558 | wc -l
    6001
    

    当检查客户端上连接的IP连接数时,我得到3000。

    # lsof -p$(pgrep python) | grep IPv4 | wc -l
    3000
    

    我也尝试在远程计算机上使用服务器进行测试,但也取得了成功。

    我建议你尝试这样做。

    此外,请尝试完全关闭iptables,以防其连接跟踪怪异。 有时/proc中的iptables选项也可以提供帮助。所以试试sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1

    编辑:我已经完成了另一项测试,可以生成您在旁边看到的输出。您的问题是您正在抢先关闭服务器端的连接。

    我可以复制与您所看到的相似的结果:

    • 将一些数据读入我的服务器后,请致电shutdown(fd, SHUT_RD)
    • 在服务器上执行send(fd, buf, sizeof(buf))

    执行此操作后,可以看到以下行为。

    • 在客户端,我使用ESTABLISHED在netstat / ss中打开了3000个连接。
    • 在lsof输出中,我得到2880(我正在关机的性质)建立连接。
    • 其余连接lsof -i:8558 | grep -v ES位于CLOSE_WAIT。

    这只发生在半关闭连接上。

    因此我怀疑这是您的客户端或服务器程序中的错误。您正在向服务器所对的服务器发送内容,或者服务器因某种原因无效关闭连接。

    您需要确认“异常”连接的状态(如close_wait或其他内容)。

    在这个阶段,我也认为这是一个编程问题,而不是属于serverfault的东西。在没有看到客户端/服务器的源的相关部分的情况下,任何人都无法追踪故障原因。虽然我非常有信心这与操作系统处理连接的方式无关。