クレイジーなバグ【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
エラーはser.c:
だから変えるべきだ
実行:
サーバ:
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
そこで私もこの方面のエラーを探し始めましたが、見つからず、最後に標準コードと比較して、エラーの原因を発見しました.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