クレイジーなバグ【select-IO多重中】

8861 ワード

selectバージョンのエコーサービスでは、初期のバグは、クライアントがサーバに接続できることだったが、クライアントが文字列を送信した後、サーバのselectはそこにブロックされ、死活は反映されなかった.gdbデバッグでfd_が表示されましたsetタイプの値も、確かに2つの値が重なっていることがわかりました.1つはリスニングソケットで、1つは接続したばかりの顧客ソケットです.これはおかしい!見るまでhttp://bbs.csdn.net/topics/230024080 他の人の質問に行って、最後に彼は言いました:解決しました、原因は私のsocket集中が1つ混入したのです エラーのsocket、selectは10038のエラーをずっと報告しています.
そこで私もこの方面のエラーを探し始めましたが、見つからず、最後に標準コードと比較して、エラーの原因を発見しました.selectの最初のパラメータ設定エラーです.
エラーコード:サーバser.c
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#define CONN_MAX 10000
#define cerror(str) do{perror(str); exit(EXIT_FAILURE);}while(0)

int sock_init_ser(int is_any, 
            const char *addr,
            int port,
            int backlog);
            
void do_client_sock(int *pcd);

int conn_fd[CONN_MAX];
int conn_n;   //               +1,   ,>=conn_n       
int fd_max;

/*
 *   MAIN
 *            
 *     select  
 */
int main(int argc, char * argv[])
{
    int sd;
    fd_set fs;
    int i;
    
    if(argc<3)
    {
        printf("Usage: argv[0] port backlog [ipaddr]
"); exit(0); } if (argc>=4) { sd=sock_init_ser(0,argv[3],atoi(argv[1]), atoi(argv[2])); }else { sd=sock_init_ser(1,NULL,atoi(argv[1]), atoi(argv[2])); } for(i=0; i<CONN_MAX; i++) // init to -1 { conn_fd[i]=-1; } conn_n=0; fd_max=sd; while(1) { int sr; FD_ZERO(&fs); FD_SET(sd, &fs); fd_max=sd; for (i=0; i<conn_n; i++) { if (conn_fd[i]!=-1) { // continue; FD_SET(conn_fd[i], &fs); if(i>fd_max) { fd_max=i;//conn_fd[i]; // fd_max=i !!! } } } sr = select(fd_max+1, &fs, NULL, NULL, NULL); // select if (sr<0) // select error { perror("select"); continue; } if(sr==0) // select timeout { continue; } for (i=0; i<conn_n; i++) { if(conn_fd[i]!=-1 && FD_ISSET(conn_fd[i], &fs)) do_client_sock(conn_fd+i); // echo back } // listen socket if (FD_ISSET(sd, &fs)) { int cd=accept(sd, NULL, NULL); if(cd<0) { if(errno == EINTR) { continue; } else { perror("accept"); continue; } } // for(i=0; i<conn_n+1; i++) // or i<CONN_MAX { if(conn_fd[i]==-1) { conn_fd[i]=cd; printf("add a new client
"); break; } } if(i>=CONN_MAX-1) { close(cd); conn_fd[i]=-1; printf("conn_fd full
"); continue; } if(i>=conn_n) conn_n=i+1; printf("conn_n= %d
",conn_n); } } close(sd); return 0; } /* * sock_init_ser * is_any 0 , IP , ,IP , INADDR_ANY * port ,backlog */ int sock_init_ser(int is_any, const char *addr, int port, int backlog) { int sd; struct sockaddr_in sin; sd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sd<0) { cerror("socket"); } memset(&sin, 0, sizeof(sin) ); sin.sin_family = AF_INET; sin.sin_port = htons(port); if (is_any) { sin.sin_addr.s_addr = htonl(INADDR_ANY); }else { inet_pton(AF_INET, addr, &(sin.sin_addr)); } if (bind(sd, (struct sockaddr*)&sin,sizeof(sin))<0) { cerror("bind"); } if(listen(sd,backlog)<0) cerror("listen"); printf("server open addr: %d
", ntohs(sin.sin_port)); return sd; } /* * do_client_sock * , read, , * , , 。 */ void do_client_sock(int *pcd) { char buf[256]; int nr = read(*pcd, buf, 256-1); if(nr<0) { perror("read
"); close(*pcd); *pcd=-1; printf("del a client
"); return; } if (nr==0) { close(*pcd); *pcd=-1; printf("del a client
"); return; } printf("recv: %s
", buf); write(*pcd, buf, 256-1); }
クライアント:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#define cerror(str) do{perror(str); exit(EXIT_FAILURE);}while(0)


int sock_init_client( const char *addr,  int port);
            
void do_client_sock(int cd);

/*
 *   MAIN
 *             ,      client 5778 192.168.1.105
 *       ,  some test string by %d, seq: %d       ,        id
 *           , 1    。        ,  10s,       。
*/
int main(int argc, char * argv[])
{
    int cd;
   
    if(argc<3)
    {
        printf("Usage: argv[0] port ipaddr");
    }
    
    cd= sock_init_client(argv[2], atoi(argv[1])) ;
    do_client_sock(cd);
    
    return 0;                  
}

/*
 * do_client_sock
 *             
*/
void do_client_sock(int cd)
{
    while(1)
    {
        char buf[256];
        static int seqnum=1;
        int nr;
        snprintf(buf, sizeof(buf), "some test string by %d, seq: %d",getpid(), seqnum++);
        nr=write(cd, buf, strlen(buf));
        if(nr!=strlen(buf))
            printf("write error
"); nr = read(cd, buf, 256-1); if(nr<0) { perror("read
"); close(cd); exit(1); } if (nr==0) { close(cd); exit(1); } buf[strlen(buf)]='\0'; printf("echo back: %s
", buf); sleep(10); } } /* * sock_init_client * IP , */ int sock_init_client( const char *addr, int port) { int cd; struct sockaddr_in sin; cd=socket(AF_INET, SOCK_STREAM, 0); if(cd<0) { cerror("socket"); } memset(&sin, 0, sizeof(sin) ); sin.sin_family = AF_INET; sin.sin_port = htons(port); inet_pton(AF_INET, addr, &(sin.sin_addr)); if (connect(cd, (struct sockaddr*)&sin, sizeof(sin) )<0) { cerror("connect"); } return cd; }

エラーはser.c:
  for (i=0; i<conn_n; i++)
        {
            if (conn_fd[i]!=-1)
            {               // continue;
                FD_SET(conn_fd[i], &fs);
                if(i>fd_max)
                {
                    fd_max=i;//conn_fd[i]; //   fd_max=i  !!!
                } 
            } 
        } 
ソケット記述は1つずつ割り当てられており、リスニングソケットが先に割り当てられ、acceptが入ってきた顧客ソケットが後に割り当てられるため、顧客ソケットはリスニングソケットよりも大きくなる.そしてconn_n最初のacceptで1つまたは複数の顧客を入力した後、値は1または2、3.です.のだから、iでfd_に行くmax比は、正しくありません.顧客が1人しかいない場合、ループは1回で終了し、i=0はfd_より小さいに違いない.max、従ってfd_maxは最後にリスニングソケットのみを網羅し,クライアントソケットは無視した.
だから変えるべきだ
  for (i=0; i<conn_n; i++)
        {
            if (conn_fd[i]!=-1)
            {               // continue;
                FD_SET(conn_fd[i], &fs);
                if(conn_fd[i]>fd_max)
                {
                    fd_max=conn_fd[i]; //   fd_max=i  !!!
                } 
            } 
        } 

実行:
サーバ:
administrator@ubuntu:~/mytest$ ./ser 8887 5 192.168.1.105 server open addr: 8887 add a new client conn_n= 1 recv: some test string by 5757, seq: 1 recv: some test string by 5757, seq: 2 recv: some test string by 5757, seq: 3 recv: some test string by 5757, seq: 4 recv: some test string by 5757, seq: 5 recv: some test string by 5757, seq: 6
お客様:
administrator@ubuntu:~/mytest$ ./client 8887 192.168.1.105 echo back: some test string by 5757, seq: 1 echo back: some test string by 5757, seq: 2 echo back: some test string by 5757, seq: 3 echo back: some test string by 5757, seq: 4 echo back: some test string by 5757, seq: 5 echo back: some test string by 5757, seq: 6