- ベストアンサー
ソケット通信での再接続
- ソケット通信でサーバ側は立ち上げたまま、クライアント側を同じポート番号で何度も再接続できるようにしたいと考えています。
- 最初の起動に関してはうまくいくのですが、クライアント側を一度終了してもう一度再接続すると、バインドエラーが出ます。
- どうすればいいのでしょうか。ご存じの方がいらっしゃればお答えいただきたいと思っています。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
普通は、サーバ側のポートは固定一個です。 ソケットは、[サーバIPアドレス、サーバポート番号、クライアントIPアドレス、クライアントポート番号] の組みで指定されるので、サーバ側のポート番号が同じでもクライアント側が異なると(普通クライアント側ポート番号はランダムに振られる)別のソケットになります。 acceptシステムコールが返すソケットを使えばいいです。
その他の回答 (3)
- notnot
- ベストアンサー率47% (4900/10358)
検索してみたのですが、すぐにサンプルが見つからなかったので、Rubyで書いてみました。Cだといろいろと書くのが面倒なので。 Cに直しやすいように、低水準ライブラリを使って、Rubyを知らなくても読める程度に書いています。 require "socket" # 子スレッド用サブルーチン定義 def child_thread(s,r) remote_port, remote_addr = Socket.unpack_sockaddr_in(r) STDERR.printf "connect from %s\n", remote_addr while (input = s.gets) !~ /^quit/i s.printf "you typed : %s",input end STDERR.printf "close from %s\n", remote_addr s.close end # ここからメインルーチン source = Socket.pack_sockaddr_in(7000, Socket::INADDR_ANY) s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP) s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) s.bind(source) s.listen(1) while true sock, remote = s.accept Thread.start(sock,remote) { |ss,rr| child_thread(ss,rr) } end
補足
notnotさん、 お返事ありがとうございます。 すいませんが、最後のループに関して少し解説していただけないでしょうか? ポイントとしては、child_threadを立ち上げたときに あたらなポート番号をどのように振っているのか、 または、振らなくてもいいのか、です。 複数クライアントと通信する場合、 それぞれにサーバと通信用のポート番号を振ってやる必要があると考えているので、 そのあたりが気になります。 たびたび質問してしまい、申し訳ありませんが、 よろしくお願いいたします。
- notnot
- ベストアンサー率47% (4900/10358)
>1.accept用スレッドを作成し、クライアントからポート番号7000に接続があるまで待機状態にする。 >2.クライアントから通信があった場合、新たにポート番号7001を送り、クライアントにはそちらに接続してデータの送受信をしてもらう。 何が目的でそんなことをするのかよくわかりません。ループの中でbindしているのがおかしいと思いますけど。ループの中でbindするということは複数回bindするということで、エラーになって当然と思います。 とりあえずエラーを押さえるためには、bind前にSO_REUSEADDRというソケットオプションをセットすればエラーは出ないと思いますけど、本質的な解決でないような気がします。
補足
notnotさん お返事ありがとうございます。 >何が目的でそんなことをするのかよくわかりません。 すいません。最初に何が目的かを書けばよかったですね。 やりたいことは「複数(最大6個)のクライアントと通信をする」ことです。 これを行うために今回私が考えたのが 「クライアントから接続要求があるたびに、 そのクライアント用に用意してあるポート番号(今回は7001の部分)に アクセスしてもらうようにする。」 という方法でした。 今回載せたソースコードはその一部でした。 notnotさんのコメントからすると、私の考えたコードはおかしいようですね。 もしよろしければ、 一般的に行われている複数クライアントとの通信方法を ご教授お願いできないでしょうか? お手数ではございますが、 よろしくお願いいたします。
- notnot
- ベストアンサー率47% (4900/10358)
バインドするのは、サーバー側のプログラムであって、クライアント側ではコネクトです。 サーバーは上げっぱなしなんですよね?クライアント側のエラーですよね? あるいは、クライアントの再起動でサーバー側でエラーが出るということですか?
補足
notnotさん 早速のお返事ありがとうございます。 ご質問の解答ですが、少しサーバ側のソースコードを載せたいと思います。 下記のソースコードのように 1.accept用スレッドを作成し、クライアントからポート番号7000に接続があるまで待機状態にする。 2.クライアントから通信があった場合、新たにポート番号7001を送り、クライアントにはそちらに接続してデータの送受信をしてもらう。 という形にしています。 それで質問の答えですが、 サーバーは上げっぱなしで、 クライアントの再起動でサーバー側でエラー(下記ソースの「ここでエラー」と書いた部分)が出ます。 これでわかりますでしょうか? よろしくお願いいたします。 int main(){ // accept用スレッドの作成 hTh_accept=(HANDLE)_beginthreadex(NULL,0,mythread_accept,NULL,0,&thID_accept); // 以下、データ用スレッドと通信して処理。 } // accept専用スレッド関数 // クライアントから接続を受け付けると、 // 新たなポート番号を設定し、新しいデータ受信用スレッドを立ち上げる。 unsigned __stdcall mythread_accept(void *lpx) { // ソケット通信の開始準備 WSADATA data; result = WSAStartup(MAKEWORD(2, 0), &data); // クライアントからのaccept用ソケットの生成 struct sockaddr_in source; source.sin_family = AF_INET; source.sin_port = htons(7000); source.sin_addr.s_addr = htonl(INADDR_ANY); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // ソケットのバインド result = bind(s, (struct sockaddr *)&source, sizeof(source)); // クライアントから接続があるまで、何度も待機する while(1){ // クライアントから通信があるまで待機 // 接続の許可 result = listen(s, 1); // クライアントから通信があるまで待機 s_tmp = accept(s, NULL, NULL); // クライアントから通信があった場合 recv_num = recv(s_tmp, message, 1024, 0); if(recv_num != SOCKET_ERROR) printf("%s\n" ,message); // 新しいポート番号を送る。 sprintf(message, "%d", 7001); send(s_tmp, message, sizeof(message), 0); // 一度切断 closesocket(s_tmp); // クライアントからのデータ受信用ソケットを生成 struct sockaddr_in source1; source1.sin_family = AF_INET; source1.sin_port = htons(7001); source1.sin_addr.s_addr = htonl(INADDR_ANY); s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // ソケットのバインド result = bind(s1, (struct sockaddr *)&source1, sizeof(source1)); // ここでエラー // データ受信用ソケット:クライアントから通信があるまで待機 result = listen(s1, 1); // クライアントから通信があるまで待機 s2 = accept(s1, NULL, NULL); // データ受信用スレッド用の変数の準備 thread_arg_t targ; targ.socket = s2; // データ受信用スレッドを生成 hTh = (HANDLE)_beginthreadex(NULL,0,mythread_Data,(void *)&targ,0, &thID); } return 0; }
お礼
notnotさん、 ご回答ありがとうございます。 >ソケットは、[サーバIPアドレス、サーバポート番号、クライアントIPアドレス、クライアントポート番号] の組みで指定されるので、 >サーバ側のポート番号が同じでもクライアント側が異なると(普通クライアント側ポート番号はランダムに振られる)別のソケットになります。 なるほど。 別にクライアントごとに新しいポート番号をわざわざ用意してやることはないのですね。 実際作りなおしてみたところ、確かにうまく動きました! それにしても、今回のことはソケット通信では基礎中の基礎のことのようですね。 もっと勉強したいと思います。 notnotさん、 このたびは長い時間お付き合いいただき 誠にありがとうございました。