• ベストアンサー

ソケット通信でチャットプログラム(unix c言語)

c言語のソケット通信でチャットプログラムを作り中です 仕様:あるクライアントからメッセージがサーバに送信された時点でサーバは接続されたいる全てのクライアントにメッセージを送信する。 以下のようにクライアントからの接続(accept)がある度に、その返り値であるファイルディスクリプタにに対するreadと全クライアントに対するwriteの処理を行うchild関数をforkで起動します。これだとforkが起動した時点での全てのファイルディスクリプタ(接続されている全クライアント)の情報をchild関数に渡せますが、それ以降増え続けていくファイルディスクリプタの情報をchild関数に渡せないので、最初の方に接続したクライアントからのメッセージをそれ以降接続した他のクライアントに送信できないという状態です。 main() {   socket()   bind()   listen()     while(1){       accept()       if(fork()==0) {        child()       }     } } child() {   select()   if(FD_ISSET()) {     read()     write()   } } ※forkやソケット通信に関してかなり初心者なので、ソースや説明分が意味不明かもしれませんがよろしくお願いします。

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

  • ベストアンサー
回答No.2

子プロセス自身がその他の子プロセスにデータを出すのではなくて、一度親プロセスにデータを渡し、親プロセスが全ての子プロセスにデータを配信するように作ればいいと思います。親プロセス側は子プロセスと pipe() で繋げばいいんじゃないでしょうか。(UNIX系OSの場合。でもその他のOSでもパイプってありますよね?) ちなみに accept() で接続がなかったときに停止させないようにする方法は fcntl() で O_NONBLOCK を設定すればできます。pipe() の入出力でデータがないときに停止させないようにするには select() を使えばできます。

taurus4_ikeda
質問者

お礼

ありがとうございました、fcntl、pipeを使うことによって実現できました。初心者なのでpipe等の使い方も良く分からず調べていて時間がかかりました。

その他の回答 (2)

  • MASA_H
  • ベストアンサー率42% (64/151)
回答No.3

fork()後にメインプロセスで取得されたディスクリプタに対して書き込みできないことはこちらでも確認しました。原因はfork()した時点でのファイルとディスクリプタの対応関係がコピーされるだけで共有はされなく、ファイルとディスクリプタの関係はプロセスごとに保持されるためのようです。 要するにファイルディスクリプタを異なるプロセス間で共有しても意味がないようです。 間違った解答を答えてしまい申し訳ないです。 よって解決策はnoboru2000さんがおっしゃっているようにするか、fork()でなくpthread_create()を使うことになるかと思います。pthread_create()であれば同一のプロセスなのでプロセス間通信など考えずにすみます。

taurus4_ikeda
質問者

お礼

わざわざ調べていただきありがとう御座います。でも共有メモリなど使ったこと無かったので勉強になりました。

  • MASA_H
  • ベストアンサー率42% (64/151)
回答No.1

fork()しているということは別のプロセスになっているのでプロセス間通信を考えなくてはいけません。 ソースの変更が少なそうな方法としては、ファイルディスクリプタの配列を共有メモリ上に作るといった方法です。書き込むのは親プロセスのみなので書き込み時に気をつければセマフォは別に使わなくてもいいでしょう。

taurus4_ikeda
質問者

補足

ありがとうございます グローバル変数として宣言しておいたファイルディスクリプタを共有メモリにしたところ、確かに親プロセスで新たにacceptのあったファイルディスクリプタも子プロセスでみれるようになったのですが、子プロセスでそのファイルディスクリプタにwriteしてみても、子プロセスが起動された時点にすでにあったファイルディスクリプタに対してしかwriteができませんでした。 原因となるのは何か分かりますでしょうか?

関連するQ&A

  • C言語でのソケット通信のclose

    C言語でソケット通信をするプログラムの見本は、大体がサーバのプログラム内でサーバとクライアントのファイルディスクリプタを、クライアントのプログラム内でクライアントのファイルディスクリプタをcloseしてから終了しています。 でも、プログラムを終了させる時にopenしているファイルディスクリプタは自動的にcloseされるのだから、わざわざ明示的にcloseする必要がないと思います。 「明示的にcloseするのがマナーだから」という理由しか、調べても出てきませんでした。 明示的に全てのファイルディスクリプタをcloseする理由をご存知の方、教えてください。

  • C言語でチャットプログラミングを・・・

    C言語(unix環境)でチャットプログラミングを作りたいのですが、まずは、TCP/IPによるマルチクライアントで簡単なメッセージのやりとりができるプログラムを練習として作りたいと考えています。 そこでサーバにクライアントからメッセージが届いた時点で全クライアントにメッセージを送信(write)し、逐次更新されるようにしたいのですが、クライアント側で常にread関数を呼び出すのは非常に不格好だし、メッセージを入力するといった他の処理ができなくなると思うのです。 何か方法はありますでしょうか? なにぶんネットワークプログラミングは始めたばかりですので初心者向けの解説をお願いします。

  • ソケット通信について

    VC++6.0MFCでソケット通信のプログラムを作成しようとしています。勉強始めたばかりなので、やっと普通に通信するプログラムは理解できたところです。 そこで応用していろいろ考えているのですが、方法がわからないところがありますので教えていただければと思ってます。 サーバ側の接続待ちはいつでも受けれるようにして、接続してきたクライアントに対して送信したいと考えております。ソケットの接続情報を保持しておいて、別プロセスの送信プログラムが接続クライアントに対して送信したいと考えてます。その送信プログラムが複数ある場合も同じ接続情報を利用したいと考えてるのですが、可能でしょうか。 説明が下手で申し訳ありません。補足はいたしますので、アドバイスお願いいたします。

  • c# ソケット非同期通信プログラム

    C#でソケット非同期通信プログラムを作りたいと思い勉強しております。ちなみにソケット通信はc言語ではやっておりました。 今作りたいと思ってるプログラム ・ラジオボタンで接続形態(サーバー・クライアント)を選択。 ・送信ボタンを押した時は送信 ・受信した時は受信データをテキストボックスに表示 ・コネクション数は1つで送受信を行う お手数ではございますが、参考になる様なサイトやアドバイス等があれば宜しくお願い致します。

  • Linux C言語 ソケット通信について

    C言語でソケット通信のプログラムを始めて作成しました。 受信時のデータ量が少ない場合は問題ないのですが、データ量が多くなると、 サーバーの受信が、出来たり、出来なかったりと不安定になります。 ログを確認しますと、パケットが勝手に分割されサーバーで受信した場合に、 おかしな動きになることがわかりました。 なぜそのようになるのか、また何がいけないのかわかりません。 どうぞご教授をお願いします。 FD_ZERO(&r_socket); FD_SET(sock, &r_socket); width = sock+1; *leng = 0; ef = TRUE; memset(&s,0x00,sizeof(s)); /* タイムアウト設定 */ time.tv_sec = 3; time.tv_usec = 0; while(1) { /* 受信待機 */ ret = select(width, &r_socket, NULL, NULL, &time) if ( ret < 0 ) { "受信待機エラー"表示 break; } if ( ret = 0 ) { "タイムアウトエラー"表示 break; } /* パケットを受信 */ if ( FD_ISSET(sock, &r_socket)) { memset(&rb,0x00,sizeof(rb)); read_len = read(sock, rb, sizeof(rb)); "受信した内容をログに出力" /* 割り込み中断エラーは継続し、その他エラーは終了する */ if ( read_len < 0 ) { if ( errno == EINTR ) { continue; } else { ef = FALSE; "受信エラー"表示 break; } } /* クライアントのソケットが閉じた場合は終了する */ if ( read_len == 0 ) { break; } /* データ送信要求を受信した場合は終了する */ if ( rb[read_len-1] == ACK ) { break; } /* 受信バッファーへ取り込み */ if ( rb[read_len-1] == ETX || rb[read_len-1] == ETB ) { len = read_len - 1; } else { len = read_len; } for (i = 0;i < len;i++) { if ( rb[i] != ETB ) { rsbuf[(*leng)++] = rb[i]; } } /* 最終データ時は終了する */ if ( rb[read_len-1] == ETX ) { break; } /* 最終データでない場合はデータ送信要求を送信する */ if ( rb[read_len-1] == ETB ) { sprintf(trxmsg,"%1c",ACK); ef = send_socket(sock, strlen(trxmsg), trxmsg); if ( ef != TRUE ) { break; } } } }

  • 異なる言語間でのソケット通信について

    簡単なネットワークプログラムを作成して、ソケット通信の確認をしています。 クライアントとサーバが両方ともにC++の場合と、両方ともにJavaの場合で、正常に通信が行われていることは確認しました。 そこで、クライアントでC++のプログラムを動かし、サーバでJavaのプログラムを動かしてみたのですが、うまくいきませんでした。 (ポート番号を合わせたので、相互接続は出来てるみたいですが、データが渡ってきませんでした。OSはクライアント/サーバともに、Windowsです。) 言語が異なる場合の、ソケット通信について良いサイトをご存知でしたら、教えていただけないでしょうか?

    • ベストアンサー
    • Java
  • C#でソケット通信

    初歩的な質問ですみません。 クラアント側とサーバー側にそれぞれC#で作ったプログラムをインストールし、 クライアントからサーバーにデータを繰り返し送信したいとき、 そのプログラム内でソケット接続して送ることはできるでしょうか? できるとして、どのような手順でソケット接続し、データを送ればいいのでしょうか? まったくの初心者です。 よろしくお願いします。

  • C言語のread関数に関するファイルディスクリプタ

    C言語のread関数に関するファイルディスクリプタについて質問です。 open関数から得られたファイルディスクリプタを、一般的には変数(例:fd)に代入して、fdをread関数や、write関数の第1引数として用いるんですが、ファイルディスクリプタは数値のはずなので、それを引数として用いてもどうやってそこからパス情報を得ることが出きるのかが分かりません。 例えば以下のような場合です。 ****** 省略 fd = open("/home/XXX/YYY", O_RDONLY); read(fd, buf, 10); 省略 ****** 誰か詳しい方がおりましたら、どうかご教授よろしくお願い致します。

  • c言語のチャットプログラムのsendとrecv

    現在、複数のクライアントからサーバーにメッセージを送りサーバーからクライアントにメッセージを送るというものを作成しているのですが、クライアントからサーバーにはメッセージを送れるのですが、サーバー側からクライアント1人にしか送れず、クライアント全員にメッセージを送信できませんので、よろしければアドバイスをお願いします サーバーのプログラム インクルード省略 #define BUF_LEN 256 typedef struct CLIENT_INFO { char hostname[BUF_LEN]; char ipaddr[BUF_LEN]; int port; time_t last_access; } CLIENT_INFO; CLIENT_INFO client_info[FD_SETSIZE]; int listening_socket; struct sockaddr_in sn; int accept_new_client(int sock){ int len; int new_socket; struct hostent *peer_host; struct sockaddr_in peer_sin; len = sizeof(sn); new_socket = accept(listening_socket, (struct sockaddr *)&sn, &len); if ( new_socket == -1 ){ perror("accept"); exit(1); } if ( new_socket > FD_SETSIZE-1 ){ return -1; } len = sizeof(peer_sin); getpeername(new_socket, (struct sockaddr *)&peer_sin, &len); peer_host = gethostbyaddr((char *)&peer_sin.sin_addr.s_addr, sizeof(peer_sin.sin_addr), AF_INET); strncpy(client_info[new_socket].hostname, peer_host->h_name, sizeof client_info[new_socket].hostname); strncpy(client_info[new_socket].ipaddr, inet_ntoa(peer_sin.sin_addr), sizeof client_info[new_socket].ipaddr); client_info[new_socket].port = ntohs(peer_sin.sin_port); time(&client_info[new_socket].last_access); printf("接続: %s (%s) ポート %d ディスクリプタ %d 番\n", client_info[new_socket].hostname, client_info[new_socket].ipaddr, client_info[new_socket].port, new_socket); return new_socket; } int read_and_reply(int sock){ int read_size; char buf[BUF_LEN]; read_size = read(sock, buf, sizeof(buf)-1); if ( read_size == 0 || read_size == -1 ){ printf("%s (%s) ポート %d ディスクリプタ %d 番からの接続が切れました。\n", client_info[sock].hostname, client_info[sock].ipaddr, client_info[sock].port, sock); close(sock); client_info[sock].last_access = 0; } else { buf[read_size] = '\0'; printf("%s (%s) ポート %d ディスクリプタ %d 番からのメッセージ: %s", client_info[sock].hostname, client_info[sock].ipaddr, client_info[sock].port, sock, buf); write(sock, buf, strlen(buf)); time(&client_info[sock].last_access); } return read_size; } int main(){ fd_set target_fds; fd_set org_target_fds; int sock_optval = 1; int port = 5000;    listening_socket = socket(AF_INET, SOCK_STREAM, 0); if ( setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &sock_optval, sizeof(sock_optval)) == -1 ){ perror("setsockopt"); exit(1); }  sn.sin_family = AF_INET; sn.sin_port = htons(port); sn.sin_addr.s_addr = htonl(INADDR_ANY); if ( bind(listening_socket, (struct sockaddr *)&sn, sizeof(sn)) < 0 ){ perror("bind"); exit(1); }   if ( listen(listening_socket, SOMAXCONN) == -1 ){ perror("listen"); exit(1); printf("ポート %d を見張ります。\n", port);   FD_ZERO(&org_target_fds); FD_SET(listening_socket, &org_target_fds);   while (1){ int i; time_t now_time; struct timeval waitval; waitval.tv_sec = 2; waitval.tv_usec = 500;  memcpy(&target_fds, &org_target_fds, sizeof(org_target_fds));  select(FD_SETSIZE, &target_fds, NULL, NULL, &waitval);  for ( i=0 ; i<FD_SETSIZE ; i++ ) { if ( FD_ISSET(i, &target_fds) ) { printf("ディスクリプタ %d 番が読み込み可能です。\n", i); if ( i == listening_socket ) { int new_sock; new_sock = accept_new_client(i); if ( new_sock != -1 ) { FD_SET(new_sock, &org_target_fds); } } else {int read_size;  read_size = read_size; read_size = read_and_reply(i); if ( read_size == -1 || read_size == 0 ) { FD_CLR(i, &org_target_fds); } } } } time(&now_time); for ( i=0 ; i<FD_SETSIZE ; i++){ if ( ! FD_ISSET(i, &org_target_fds) ) continue; if ( i == listening_socket ) continue; if ( now_time-60 > client_info[i].last_access ) { close(i); FD_CLR(i, &org_target_fds); } } } close(listening_socket); FD_CLR(i, &org_target_fds); } }

  • ソケット通信で同時受信の制限?

    こんにちは。 早速ですが、ソケット通信について質問です。 複数クライアント(Win)からサーバ(UNIX)に対して、connect()し、サーバ側はaccept()したら、それぞれに対しfork()で子プロセスを作っています。 そこで、50台くらいのクライアントから一気にconnect()すると通信エラーとなってしまうのですが、なにか制限があるのでしょうか? connect()のタイミングをずらせば、それぞれのクライアントとサーバの子プロセスでやり取りができます。 一斉にconnect()される場合の通信エラーの原因や対処法などをご教授願います。 初歩的なことかもしれませんが・・・。 よろしくお願いいたします。

専門家に質問してみよう