0

私はTCPサーバーで作業していましたが、非常に奇妙なことが起こりました。

1つのクライアントに接続すると、すべてがうまくいきます。
2つ以上のクライアントが接続する場合でも、トラフィックは正常です。
ただし、いずれかのクライアントが切断されると、リーパー呼び出しの直後にサーバーがジャムします。それはただそこに座って待っています。

2つのうち1つのクライアントを切断し、再接続しようとしたときに、これを発見しました。再接続しているクライアントはエラーメッセージをまったく表示せず、スムーズに実行されます。パッケージはサーバーに送信できますが、サーバー内に蓄積されます。

一方、サーバーはそこでハングし、特定の1つのクライアントが切断されるのを待ちます。そのクライアントが切断されると、サーバーは機能を再開し、サーバー内に蓄積されたすべての要求を実行します。

以下は、サーバー構造に使用したベアボーンコードです。
このコードは、上記の問題も示しています。
誰かお願いします、エラーが発生した場所を指摘してください。

void    reaper(int sig)
{
int status;

while (waitpid(-1, &status, WNOHANG) >= 0)
    /* empty */;
}


int     errexit(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(1);
}




int     errno;

unsigned short  portbase = 0;   /* port base, for non-root servers      */

int     passivesock(const char *service, const char *transport, int qlen)

{
    struct servent  *pse;   /* pointer to service information entry */
    struct protoent *ppe;   /* pointer to protocol information entry*/
    struct sockaddr_in sin; /* an Internet endpoint address         */
    int     s, type;        /* socket descriptor and socket type    */

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;

/* Map service name to port number */
    if ( pse = getservbyname(service, transport) )
            sin.sin_port = htons(ntohs((unsigned short)pse->s_port)
                    + portbase);
    else if ((sin.sin_port=htons((unsigned short)atoi(service))) == 0)
            errexit("can't get \"%s\" service entry\n", service);

/* Map protocol name to protocol number */
    if ( (ppe = getprotobyname(transport)) == 0)
            errexit("can't get \"%s\" protocol entry\n", transport);

/* Use protocol to choose a socket type */
    if (strcmp(transport, "udp") == 0)
            type = SOCK_DGRAM;
    else
            type = SOCK_STREAM;

/* Allocate a socket */
    s = socket(PF_INET, type, ppe->p_proto);
    if (s < 0)
            errexit("can't create socket: %s\n", strerror(s));

/* Bind the socket */
    if (errno=bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
            errexit("can't bind to %s port: %s\n", service,
                    strerror(errno));
    if (type == SOCK_STREAM && listen(s, qlen) < 0)
            errexit("can't listen on %s port: %s\n", service,
                    strerror(type));
    return s;
}

int     passiveTCP(const char *service, int qlen)
{
    return passivesock(service, "tcp", qlen);
}




#define QLEN              32    /* maximum connection queue length      */
#define BUFSIZE         4096


int     TCPechod(int fd);

int main(int argc, char *argv[])
{
    char    *service;      /* service name or port number  */
    struct  sockaddr_in fsin;       /* the address of a client      */
    unsigned int    alen;           /* length of client's address   */
    int     msock;                  /* master server socket         */
    int     ssock;                  /* slave server socket          */

    if (argc !=2)
            errexit("usage: %s port\n", argv[0]);

    service = argv[1];

    msock = passiveTCP(service, QLEN);

    (void) signal(SIGCHLD, reaper);

    while (1) {
            alen = sizeof(fsin);
            ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
            if (ssock < 0) {
                    if (errno == EINTR)
                            continue;
                    errexit("accept: %s\n", strerror(ssock));
            }
            printf("Accept connection %d from %s:%d\n", ssock, inet_ntoa(fsin.sin_addr), (int)ntohs(fsin.sin_port));
            switch (fork()){
            case 0:
                (void) close(msock);
                TCPechod(ssock);
                close(ssock);
                exit(0);
            default:
                close(ssock);
                break;
            case -1:
                errexit("fork: %s\n", strerror(errno));
            }              
    }
}


int     TCPechod(int fd)
{
    char    buf[BUFSIZE];
    int     cc;

    while (cc = read(fd, buf, sizeof(buf))) {
            if (cc < 0)
                    errexit("echo read: %s\n", strerror(cc));
            if (errno=write(fd, buf, cc) < 0)
                    errexit("echo write: %s\n", strerror(errno));
    }
    return 0;
}

どんなヘッズアップでも大歓迎です。
よろしくお願いします。

4

2 に答える 2

3

問題は、waitpidをどのように呼び出しているかです。これは、エラーが発生したときだけ待機しているためです(エラーが発生した場合、waitpidは<0を返します)。WNOHANGフラグを指定してwaitpidを呼び出すと、終了した子プロセスがない場合は0が返されます(実際に状態を変更する:停止、再開、または終了)。この修正を試してください:

void reaper(int sig)
{
  int status;
  pid_t pid;
  while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
    printf("Proces PID: %d Hash Finished With Status: %d", pid, status);
  if (0 == pid) printf("No More Process Waiting");
  if (pid < 0) printf("An Error Ocurred");
}

待機を使用する場合、リーパー関数は次のようになっている必要があります。

void reaper(int sig)
{
  int status;
  pid_t pid;
  pid = wait(&status); // Wait suspend the execution of the current process.
  if (pid > 0) printf("Proces PID: %d Hash Finished With Status: %d", pid, status);
  if (pid < 0) printf("An Error Ocurred");
}

wait(2)の詳細については、http://linux.die.net/man/2/waitにアクセスしてください。

于 2010-12-14T21:24:02.483 に答える
1

私もこの問題に遭遇しました。

テストを使用した「リーパー」関数は、さまざま>=0な例にありますが、子をクリーンアップした後でも、それ以上なくなるまでループを続けないため、これは無限ループになる可能性があります。ある種のエラー。

このコードにはPerlバージョンがあり、通常は>0の代わりにを使用して「修正」され>=0ますが、ここに示すロジックを使用して、対象のケースを明示的にテストすることもできます。

于 2011-08-11T17:47:40.813 に答える