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

leo*_*ang 5 linux networking netstat lsof

此问题类似于网络端口打开,但没有附加进程?netstat显示没有pid的侦听端口,但lsof没有.但是他们的答案无法解决我的问题,因为它太奇怪了.

我有一个服务器应用程序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
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,什么是错的监听套接字,但是当我连接几千测试客户端(由另一位同事写的)服务器,无论是2000,3000,或4000一直存在着5个客户端(这也是随机连接并向服务器发送登录请求,但无法接收任何响应.以3000个客户为例.这是netstat命令给出的:

[root@centos63 lcms]# netstat -nap | grep 8588 | grep ES | wc -l
3000
Run Code Online (Sandbox Code Playgroud)

这是lsof命令输出:

[root@centos63 lcms]# lsof -i:8588 | grep ES | wc -l
2995
Run Code Online (Sandbox Code Playgroud)

这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 -                   
Run Code Online (Sandbox Code Playgroud)

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

上面的链接说明了NFS mount和RPC.至于RPC,我使用了命令rcpinfo -p,结果与端口8588无关.而NFS mount,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
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助!我真的很困惑.


更新 2013-06-08:将系统升级到CentOS 6.4后,会出现同样的问题.最后我回到了epoll,发现这个页面说set listen fd是非阻塞的,accept直到EAGAIN或者EWOULDBLOCK错误返回.是的,它有效.没有更多的连接待定.但那是为什么呢?在 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).
Run Code Online (Sandbox Code Playgroud)

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

更新 2013-7-1:我EPOLLET在添加监听套接字时使用,所以如果不EAGAIN遇到接受,我就不能接受所有内容.我刚刚意识到这个问题.我的错.请记住:如果使用,即使它是监听插座,也会一直readaccept直到EAGAIN出现EPOLLET.再次感谢Matthew为我提供测试程序.

Mat*_*Ife 1

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

  1. 服务器使用epoll来管理连接。
  2. 我建立了 3000 个连接。
  3. 连接被阻塞。
  4. 服务器基本上被“简化”为仅处理连接并执行很少的复杂工作。

我无法重复这个问题。这是我的服务器源代码。

#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();
  }

}
Run Code Online (Sandbox Code Playgroud)

这是客户端:

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)
Run Code Online (Sandbox Code Playgroud)

在我的本地计算机上运行此程序时,我使用端口 8558 获得 6001 个套接字(1 个监听、3000 个客户端套接字和 3000 个服务器端套接字)。

$ ss -ant | grep 8558 | wc -l
6001
Run Code Online (Sandbox Code Playgroud)

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

# lsof -p$(pgrep python) | grep IPv4 | wc -l
3000
Run Code Online (Sandbox Code Playgroud)

我还在远程计算机上尝试了服务器测试,也取得了成功。

我建议你尝试做同样的事情。

此外,请尝试完全关闭 iptables,以防出现某些连接跟踪怪癖。有时 iptables 选项/proc也有帮助。所以尝试一下sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1

编辑:我做了另一个测试,它产生了您在您身边看到的输出。您的问题是您正在预先关闭服务器端的连接。

我可以复制类似于您所看到的结果,执行以下操作:

  • 将一些数据读入我的服务器后,调用shutdown(fd, SHUT_RD).
  • send(fd, buf, sizeof(buf))在服务器上做。

执行此操作后,会出现以下行为。

  • 在客户端上,我在 netstat/ss 中打开了 3000 个已建立的连接。
  • 在 lsof 输出中,我得到了 2880 个已建立的连接(我如何关闭的本质)。
  • 其余连接lsof -i:8558 | grep -v ES处于 CLOSE_WAIT 状态。

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

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

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

在这个阶段,我还认为这是一个编程问题,而不是真正属于服务器故障的问题。如果没有看到客户端/服务器源的相关部分,任何人都不可能追踪到故障的原因。尽管我非常有信心这与操作系统处理连接的方式无关。