5

この質問は、 「ネットワーク ポートが開いていますが、プロセスが接続されていませんか?」に似ています。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

ご覧のとおり、リッスン ソケットに問題はありませんが、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 でサーバーに接続されているが、プログラムが接続されていないことを示しています。2 番目の列 ( RECV-Q) は、クライアントが要求を送信するにつれて増加し続けます。

上記のリンクは、NFS マウントと RPC について述べています。RPCに関しては、コマンドを使用しましたrcpinfo -pが、結果はポート8588とは何の関係もありません。そして、NFSマウント、nfssta出力は言うError: No Client Stats (/proc/net/rpc/nfs: No such file or directory).

質問 : これはどのように起こりますか? 常に 5 であり、同じ 5 クライアントからではありません。他のクライアントも同じサーバーIPとポートに接続されており、それらはすべてサーバーによって適切に処理されるため、ポートの競合ではないと思います。

注:epollクライアントの要求を受け入れるために Linux を使用しています。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 年 6 月 8 日更新: システムを CentOS 6.4 にアップグレードした後、同じ問題が発生します。最後に に戻ったところlisten fd をノンブロッキングに設定し、エラーが返るまでと書かれているページepollを見つけましそして、はい、それは動作します。保留中の接続はこれ以上ありません。しかし、それはなぜですか?Unix Network Programming Volume 1は言うacceptEAGAINEWOULDBLOCK

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私はちょうどこの問題に気づきました。私のせいです。覚えておいてください:ソケットをリッスンしている場合でも、alwaysreadまたはacceptuntilEAGAINが出てきEPOLLETテスト プログラムで私を証明してくれた Matthew に再び感謝します。

4

1 に答える 1

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

}

クライアントは次のとおりです。

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 を使用して 6001 個のソケットが取得されます (1 個のリッスン、3000 個のクライアント側ソケット、および 3000 個のサーバー側ソケット)。

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

クライアントに接続されている IP 接続の数を確認すると、3000 を取得します。

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

また、リモートマシン上のサーバーでテストも成功しました。

同じことを試してみることをお勧めします。

さらに、接続追跡の癖が発生した場合に備えて、iptables を完全にオフにしてみてください。iptables オプション/procも役立つ場合があります。だから試してみてくださいsysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1

編集:あなたの側に表示される出力を生成する別のテストを行いました。問題は、サーバー側の接続を事前にシャットダウンしていることです。

次のことを行っているのと同様の結果を複製できます。

  • サーバーにデータを読み込んだ後、 を呼び出しますshutdown(fd, SHUT_RD)
  • サーバー上で行いますsend(fd, buf, sizeof(buf))

これを行った後、次の動作が見られます。

  • クライアントでは、netstat/ss で ESTABLISHED で 3000 の接続が開かれています。
  • lsof の出力では、2880 (シャットダウンを行っていた方法の性質) の接続が確立されています。
  • 残りの接続lsof -i:8558 | grep -v ESは CLOSE_WAIT にあります。

これは、ハーフ シャットダウン接続でのみ発生します。

そのため、これはクライアントまたはサーバー プログラムのバグであると思われます。サーバーがオブジェクトを送信しているサーバーに何かを送信しているか、サーバーが何らかの理由で接続を無効に閉じています。

「異常な」接続の状態 (close_wait など) を確認する必要があります。

この段階では、これはプログラミングの問題であり、実際にはサーバーフォールトに属するものではないと考えています。クライアント/サーバーのソースの関連部分を確認しないと、障害の原因を追跡することはできません。これは、オペレーティングシステムが接続を処理する方法とは何の関係もないと確信しています。

于 2013-06-08T14:06:07.637 に答える