C言語でselectを使用したチャットサーバの実装でエラーが発生する

このQ&Aのポイント
  • C言語でselectを使用してチャットサーバを実現する際に、エラーが発生しています。具体的には、メッセージの受信と新しいクライアントの受け入れを処理するループ内のif文の分岐に入らなくなってしまいます。
  • このエラーは、1回目の処理後にループ内でメッセージを送信してもif文の分岐に入らなくなることを意味しています。
  • 原因を特定できる方、助けていただけないでしょうか?
回答を見る
  • ベストアンサー

selectを使った文がうまくいきません

C言語でselectを使ってチャットサーバを実現したいのですが、うまくいきません。 以下にメッセージの受信と新しいクライアントの受付を処理するループのコードを示します。 以下のコードのif(select(FD_SETSIZE,&rfds,NULL,NULL,&tv)>0) { ... } の分岐に入って1回目の処理を行った後、以降のループでメッセージを送ってもこの分岐に入らなくなります。 どなたか原因がわかるかたよろしくお願いします。 while(1){ FD_ZERO(&rfds); /* rfds を空集合に初期化*/ FD_SET(sock,&rfds); /* 接続要求を待つソケット*/ /* クライアントを受け付けたソケット*/ /* 監視する待ち時間を1 秒に設定*/ tv.tv_sec = 1; tv.tv_usec = 0; /* 標準入力とソケットからの受信を同時に監視する*/ max = sock; for(i = 0;i = k ; i++){ if(max > csock[i]){ max = csock[i]; } } if(select(FD_SETSIZE,&rfds,NULL,NULL,&tv)>0) { /* s3 */ if(FD_ISSET(sock,&rfds)){ /* s4 */ /* クライアントの受付*/ clen = sizeof(clt); if ( ( csock[k] = accept(sock,(struct sockaddr *)&clt,&clen) ) <0 ) { perror("accept"); exit(2); } FD_SET(csock[k],&rfds); if(k < MAXCLIENTS){ strcpy(jusin,"REQUEST ACCEPTED\n"); write(csock[k],jusin,strlen(jusin)); /* s5 */ read(csock[k],nbuf,sizeof(nbuf)); check = 0; for(i = 0;i < 5;i++){ for(j = 0; j < 100;j++){ if(name[i][j] != nbuf[j]){ check = 1; break; } if(nbuf[j] == '\n'){ break; } } } if(check != 1){ strcpy(userkyohi,"USERNAME REJECTED\n"); write(csock[k],userkyohi,strlen(userkyohi)); write(csock[k],nbuf,strlen(nbuf)); close(csock[k]); } else{ i = 0; while(rbuf[i] != '\n'){ name[k][i] = nbuf[i]; i++; } name[k][i]= '\n'; strcpy(toroku,"USERNAME REGISTERED\n"); write(csock[k],toroku,sizeof(toroku)); k++; } }else{ strcpy(kyohi,"REQUEST REJECTED\n"); write(csock[k],kyohi,strlen(kyohi)); close(csock[k]); } } for(i = 0;i < k;i++){ if(FD_ISSET(csock[i],&rfds)){ read(csock[i],mbuf,sizeof(mbuf)); if(mbuf[0] == 'E'&& mbuf[1]=='O' && mbuf[2] == 'F'){ /* s7 */ for(;i == k ; i++){ csock[i]=csock[i+1]; memset(name[i],0,sizeof(name[i])); j = 0; while(name[i+1][j] != '\n'){ name[i][j] = name[i+1][j]; } } close(csock[k]); k--; } else{ memset(msg, 0, sizeof(msg)); j = 0; while(name[i][j] != '\n'){ msg[j] = name[i][j]; j++; } msg[j] = ' ';j++; msg[j] = '>';j++; l = 0; while(mbuf[l]!='\0'){ msg[j]= mbuf[l]; j++; l++; } msg[j] = '\0'; for(t = 0; t < k;t++){ write(csock[t],msg,sizeof(msg)); } } } } } }

  • pwpr
  • お礼率25% (11/44)

質問者が選んだベストアンサー

  • ベストアンサー
  • trapezium
  • ベストアンサー率62% (276/442)
回答No.4

全体的な話として、どこかでブロックしてる可能性。 非ブロッキングならread,write,send,recvすべてでエラーチェックとフォローが必須。 クライアントとのプロトコルで、どうしても時間がかかる処理を実装するならサーバ側もfork()する。 場合によってはwrite処理もselect()でチェックして、書き込むデータはqueueから読み出す。

その他の回答 (3)

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.3

ループの頭で >FD_ZERO(&rfds); してて、すべてのfdをFD_SET()してますか? accept()した後のfdはどこで?

pwpr
質問者

補足

確かに、セットし忘れていました; /* クライアントを受け付けたソケット*/ for(i=0;i < k;i++){ FD_SET(csock[i],&rfds); } と書き加えました。 ありがとうございました。 ただ、これが原因ではないようです;

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.2

>変数max自体使うのをやめたのに消すの忘れてました 肝心なところですから、現在お手元にあるコードを載せてください。

pwpr
質問者

補足

以前までif(select(max,&rfds,NULL,NULL,&tv)>0)とかいていたのを if(select(FD_SETSIZE,&rfds,NULL,NULL,&tv)>0) と書き直してmaxを使わなくなったので max = sock; for(i = 0;i = k ; i++){ if(max > csock[i]){ max = csock[i]; } } このfor文自体に意味がなくなったということです。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

本題と関係があるかどうかはわかりませんが、 >for(i = 0;i = k ; i++){ このfor文は思ったとおりに動いていますか?

pwpr
質問者

補足

i == k; ですね; というか変数max自体使うのをやめたのに消すの忘れてました 汚いコードで申し訳ないです;

関連するQ&A

  • 多人数のチャットソフトを考えています

    winsockを使ってプログラミングしています。 1対1のチャットはできたのですが、サーバを挟んで1対多のチャットがうまくいきません。 サーバがクライアントごとにスレットを作って、acceptするたびにできるディスクリプタを配列に格納して、それを元に送信してきたクライアント以外に送るということをしたいと思っています。 説明下手ですいません。。。。 サーバ↓↓↓ ・・・・・・・・・初期化は略・・・・・・・・・・・・・・・・・・・・ while(1){ Csock[i] = accept(s,(struct sockaddr *) &Saddr, &Ssize); CreateThread(Csock[i]);  //クライアントごとにスレッドを作ってるつもり i++; } /**CreateThreadで作るスレッド**********/ unsigned __stdcall recvthread(void *lpx) //lpxにはクライアントのディスクリプタを格納 { fd_set fds, readfds; int sock = *(int *)lpx; int recvSize; char recvbuf[256]; FD_ZERO(&readfds); FD_SET(sock, &readfds); while(1) { memcpy(&fds, &readfds, sizeof(fd_set)); memset(recvbuf, 0, sizeof(recvbuf)); select(0, &fds, NULL, NULL, NULL); if (FD_ISSET(sock, &fds)) { WaitForSingleObject(mutex, INFINITE); recvSize = recv(sock, recvbuf, sizeof(recvbuf), 0); Send(sock,(const char)recvbuf); ReleaseMutex(mutex); if(recvSize == 0) { printf("通信終了\n"); closesocket(sock); break; } if(recvSize == -1) { printf("socket errer (recv)\n"); closesocket(sock); break; } } } return 0; } void Send(int sock,char recvbuf) { for(int j=0;j<5;j++) { if(Csock[j]==sock) continue; send(Csock[j],(const char*)recvbuf,sizeof(recvbuf),0); } } これを実行するとスレッドが無数に作成され、強制終了させられてしまいます。 初級者なのでプログラムのミスがあったら教えて下さい。 違うアイディアもあったら教えて欲しいです。見にくいと思いますがよろしくお願いします。。。。

  • TCP/IP通信型大文字・小文字変換プログラム

    TCP/IP通信型大文字・小文字変換プログラムを作りたいです。 しかし、うまく2つのプログラムが接続されません。 恐らく、IPアドレスやホスト名の問題だと思います。 超初心者でそこのところをあまり理解していません。 どなたかプログラムの補足をお願いします。 概要は ・クライアント キーボードから文字列を入力し、サーバーに送信。 サーバーから送信された文字列を画面に出力。 ・サーバー クライアントから送信された文字列に対し、 大文字は小文字に、小文字は大文字に変換して返す。 クライアント側プログラム #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define SOCK_NAME "./socket" int main() { struct sockaddr_in saddr; int soc; char buf[1024]; if ( (soc =socket(AF_INET, SOCK_STREAM, 0 ) ) < 0 ) { perror("socket"); exit(1); } memset((char *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr=inet_addr("192.168.1.1"); saddr.sin_port=htons(1357); if(connect(soc, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { perror("connect"); exit(1); } fprintf(stderr, "Connection established: socket %d used.\n", soc); while(fgets(buf, 1024, stdin)){ if(buf[strlen(buf) -1] == "\n") buf[strlen(buf) -1] = "\0"; write(soc, buf, 1024); read(soc, buf, 1024); fprintf(stdout, "%s\n", buf); } close(soc); return 0; } サーバー側プログラム #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define SOCK_NAME "./socket" int main() { int i; int fd1, fd2; struct sockaddr_in saddr; struct sockaddr_in caddr; int len; int ret; char buf[1024]; if((fd1 =socket(AF_INET, SOCK_STREAM, 0)) < 0 ){ perror("socket"); exit(1); } memset((char *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr=INADDR_ANY; saddr.sin_port=htons(1357); unlink(SOCK_NAME); if(bind(fd1, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { perror("bind"); exit(1); } if(listen(fd1,5) < 0 ) { perror("listen"); exit(1); } while(1){ len = sizeof(caddr); if((fd2 = accept(fd1, (struct sockaddr *)&caddr, &len)) < 0){ perror("accept"); exit(1); } fprintf(stderr, "Connection established: socket %d used.\n", fd2); while((ret = read(fd2, buf, 1024)) > 0 ){ fprintf(stderr, "read: &s\n", buf); for(i=0; i<ret; i++) if(islower(buf[i])) buf[i] = toupper(buf[i]); if(isupper(buf[i])) buf[i] = tolower(buf[i]); fprintf(stderr, "write: %s\n", buf); write(fd2, buf, 1024); } close(fd2); } close(fd1); return 0; }

  • TCP/IP通信型電話番号検索プログラムを作りたいです。

    TCP/IP通信型電話番号検索プログラムを作りたいです。 クライアントは以下のようで大丈夫みたいなのですが、サーバの方を修正しなければなりません。 この質問で「TCP/IP通信型大文字・小文字変換プログラム」を発見しました。 サーバー側プログラム #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define SOCK_NAME "./socket" int main() { int i; int fd1, fd2; struct sockaddr_in saddr; struct sockaddr_in caddr; int len; int ret; char buf[1024]; if((fd1 =socket(AF_INET, SOCK_STREAM, 0)) < 0 ){ perror("socket"); exit(1); } memset((char *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr=INADDR_ANY; saddr.sin_port=htons(1357); unlink(SOCK_NAME); if(bind(fd1, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { perror("bind"); exit(1); } if(listen(fd1,5) < 0 ) { perror("listen"); exit(1); } while(1){ len = sizeof(caddr); if((fd2 = accept(fd1, (struct sockaddr *)&caddr, &len)) < 0){ perror("accept"); exit(1); } fprintf(stderr, "Connection established: socket %d used.\n", fd2); while((ret = read(fd2, buf, 1024)) > 0 ){ fprintf(stderr, "read: &s\n", buf); for(i=0; i<ret; i++) if(islower(buf[i])) buf[i] = toupper(buf[i]); if(isupper(buf[i])) buf[i] = tolower(buf[i]); fprintf(stderr, "write: %s\n", buf); write(fd2, buf, 1024); } close(fd2); } close(fd1); return 0; } 先生によると、クライアントは同じもので良いそうです。 誰か、助けて下さい。

  • 動的なメモリ管理

    下記のプログラムで、確保したメモリブロックをどこで開放していいかわかりません。教えてください。お願いします。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFFERSIZE 11 #define MAX_PERSON 10 #define MAX_CHARS 10 int main(void){ char *name[10]; //氏名 int count; int i; int l; //文字列の長さ int top_index = 0; int bot_index; char *tmp; printf("*** 入力された氏名をソートし、表示します ***\n"); printf("*** 最大入力件数10件(1文字目'0'で入力終了) ***\n"); putchar('\n'); for (i = 0; i < MAX_PERSON ; i++) { printf("氏名入力(10文字まで有効) > "); name[i] = malloc(sizeof(char) * BUFFERSIZE); fgets( name[i], BUFFERSIZE, stdin); l = strlen(name[i]); if (name[i][l-1] == '\n'){ name[i][l-1] = '\0'; } else { while ( getchar() != '\n'){ } } if (name[i][0] == '0'){ break; } printf("累計 : %d\n", i+1); } count = i; //ソート前 /*printf("ソート前\n"); for (i = 0 ;i < count ; i++ ){ printf("%s\n", name[i]); } */ bot_index = count -1; //シェーカーソート while (1) { int last_swap_index; // 順方向のスキャン last_swap_index = top_index; for ( i = top_index; i < bot_index; i++){ if(strcmp(name[i],name[i+1]) > 0 ){ //ポインタ配列の要素の交換 tmp = name[i]; name[i] = name[i+1]; name[i+1]=tmp; //実体交換 //tmp = (char*)malloc((strlen(name[i])+1)*sizeof(char)); //strcpy(tmp,name[i]); //strcpy(name[i],name[i+1]); //strcpy(name[i+1],tmp); last_swap_index = i; } } //後方のスキャン範囲を狭める bot_index = last_swap_index; if (top_index == bot_index){ break; } // 逆方向のスキャン last_swap_index = bot_index; for ( i = bot_index; i > top_index; i--){ if(strcmp(name[i],name[i-1]) < 0 ){ //実体交換 //tmp = (char*)malloc((strlen(name[i])+1)*sizeof(char)); //strcpy(tmp,name[i]); //strcpy(name[i],name[i-1]); //strcpy(name[i-1],tmp); //要素の交換 tmp = name[i]; name[i] = name[i-1]; name[i-1]=tmp; last_swap_index = i; } } //前方のスキャン範囲を狭める top_index = last_swap_index; if (top_index == bot_index){ break; } } printf("+++++データ表示+++++\n"); for (i = 0 ;i < count ; i++ ){ printf("%2d : %s\n", i+1, name[i]); } return 0; }

  • TCP/IP Soket 通信

    本当に泣きそうです。教えてください。 サーバには   str_len=read(clnt_sock,msg_num,sizeof(msg_num));   msg_num[str_len]=\'\\0\';   printf(\"%s \\n\",msg_num);   str_len=read(clnt_sockt,msg_file,sizeof(msg_file));   msg_file[str_len]=\'\\0\';   printf(\"%s \\n,msg_file\"); クライアントには   write(sock,msg_num,sizeof(msg_num));   write(sock,msg_file,sizeof(msg_file)); になっていますが、お互いに実行させると、 サーバ側に「msg_num」は出力しますが、 「msg_file」は出力されず、待機状態になるのです。 どうすればいいのでしょうか?

  • TCPでの非同期型select関数について

    マッキントッシュOS-X上で動くTCP通信プログラムを作る場合について教えてください。 ウィンドウアプリを考えています。 ウィンドウ環境なので、アプリがフリーズしてしまわないために、 以下のような非同期処理が考えられます。 この場合、データが来ていなくても、while(1)がぐるぐる回るので 無駄にプロセスを食ってしまうと思うのですが、これはしかたのないことでしょうか。 もっとよい方法がるのでしょうか。 Winsockだとイベント応答関数での受信処理処理ができてエレガントなのですが、 バークレイソケットでは、どうすべきなのか疑問に思い質問させていただきました。 よろしくお願いします。 //ソケットを非同期モードにセット val = 1; ioctl(sock, FIONBIO, &val); while (1) {   memcpy(&fds, &readfds, sizeof(fd_set));   select(2, &fds, NULL, NULL, NULL);   // sockに読み込み可能データが届いている場合は、受信   if (FD_ISSET(sock, &fds)) {     memset(buf, 0, sizeof(buf));     recv(sock, buf, sizeof(buf), 0);   }   else{     //受信データがなかったときの処理   } }

  • 強制終了されていまいます。

    標準入力された1行を"<"から">"までと文字のところにわけるプログラムを書いたのですが、コンパイルして実行して入力すると、画面が黒くなって止まってしまいます。以下のところが問題の部分だと思うんですが、理由がわかりません。よろしくお願いします。 while(fgets(buf, 1024, stdin) != NULL){ i = 0; j = 0; while(buf[i] != '\n' || buf[i] != '\0'){ if ((x = (char *)malloc(sizeof(char))) == NULL){ printf("malloc error\n"); exit(1); } if(buf[i] == '<'){ j = i; while(buf[j] != '>'){ j++; } strncpy(x, &buf[i], j-i+1); i = j + 1; x[strlen(x)] = '\0' putRear(deq, x); } else if(isalpha(buf[i])){ j = i; while(buf[j] != '>'){ j++; } strncpy(x, &buf[i], j-i); i = j + 1; x[strlen(x)] = '\0'; putRear(deq, x); } } }

  • c 言語初心者です。

    c 言語初心者です。 私は下記の構造体配列をつくりました。 しかしバッファオーバーランが起きてエラーが起きてしまいます。 ヒープ領域に問題があるのかもしれませんが、プログラム上どこに原因があるのかが良くわかりません。 どなたかよろしければ教えていただけないでしょうか? #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include<memory.h> struct s { int i; char name[25]; char huri[25]; char num[23]; }; void touroku(struct s *p); void hyouji(struct s *p); int main(void) { struct s data; touroku( &data ); hyouji( &data ); //data.num *= 1; /* dataはポインタではないのでドット演算子 */ hyouji( &data ); return 0; } /* 構造体のメンバを設定する */ void touroku(struct s *p) { int i=0; for(i=1;i<3;i++) { printf( "25文字以内の名前を入力して下さい\n" ); memset(p[i].name, 0, sizeof(p[i].name)); fgets( p[i].name,sizeof(p[i].name) , stdin ); if(strchr(p[i].name,'\n')==NULL)//バッファ処理 { while(getchar() != '\n'); } if(p[i].name[strlen(p[i].name)-1]=='\n')//改行解除 { p[i].name[strlen(p[i].name)-1] = '\0'; } printf("25文字以内のふりがなを入力してください\n"); memset(p[i].huri, 0, sizeof(p[i].huri)); fgets(p[i].huri,sizeof(p[i].huri),stdin); if(strchr(p[i].huri,'\n')==NULL)//バッファ処理 { while(getchar() != '\n'); } if(p[i].huri[strlen(p[i].huri)-1]=='\n')//改行解除 { p[i].huri[strlen(p[i].huri)-1] = '\0'; } printf( "整数を入力して下さい\n" ); memset(p[i].num, 0, sizeof(p[i].num)); fgets(p[i].num,sizeof(p[i].num),stdin ); if(strchr(p[i].num,'\n')==NULL)//バッファ処理 { while(getchar() != '\n'); } if(p[i].num[strlen(p[i].num)-1]=='\n')//改行解除 { p[i].num[strlen(p[i].num)-1] = '\0'; } } } /* 構造体のメンバを出力する */ void hyouji(struct s *p) { int i=0; for(i=1;i<3;i++) printf("%-8s %3s %3s %d\n" ,p[i].name , p[i].huri , p[i].num , i); puts("----------------------------------------------------------------"); return ; }

  • 無線局でのデータ伝送に失敗した際の再送プログラム

    無線局から無線局へデータを飛ばしたいのですが、電波状況が悪く受信側に届かない場合があります。 そのような場合に同じ情報を再送するプログラムを作りたいのですが、よくわかりません。 どなたか教えていただけないでしょうか。 ちなみに送る情報は「0, 1, 2,…」と一定間隔で数字を増やしつつ送るプログラムです。 i = 0; while (1) { sprintf(buf, "%d,", i); j = strlen(buf); write(fd, buf, j); printf("%s\n", buf); sleep (2); i++; if (i >= 10) i = 0; }

  • strlen

    c言語初心者です。 10文字以上入力すると警告してくれるプログラムを考えています。 一応文字数を制限するにあたりstrlenを使おうとしてるのですが 思ったように機能してくれません。 以下が私の書いたプログラムです。 void main() { char name[10]={0}; memset( name, '\0', sizeof(name) ); loop: puts("*****登録*****"); printf(" 名前 :"); fgets(name,10,stdin); //バッファクリア**// if(strchr(name,'\n') == NULL) { while(getchar() != '\n'); } if(strlen(name)>10) { puts("<<文字入力数が多すぎです>>"); fgets(name,10,stdin); //バッファクリア**// if(strchr(name,'\n') == NULL) { while(getchar() != '\n'); } system("cls"); goto loop; } else { puts("OK"); } } よろしくお願いします。

専門家に質問してみよう