皆様に質問がございます。私は今、ネットワークプログラムの入り(?)である
チャットプログラムを作成していて、一番初めの通信がうまくいくところまで
こじつけることができました。接続は受信一回ごとにソケットを破棄して、毎回作り
直せばいいかな?と考えたので、Winproc()でlistenThreadと言うスレッドを毎回作って
受信しています。しかし、サーバー側のプログラムを起動したままもう一度受信待ちを
しようとするとなぜかうまくいきません。どうやらbind()がエラーを返してきている
ようなのですが、いまいちどうやったら直るのかが分かりません。私個人は、前回
作ったスレッドがまだ残って悪さしているのかな?と考えてもいるのですがnetstatで
見る限りそのようなこともなさそうなさそうです。繰り返しになってしまいますが
本当によく分かりません。ご助言のほどよろしくお願いいたします。
#include <windows.h>
#include <winsock.h>
#include <stdlib.h>
#include <stdio.h>
#include recv.h
#include resource.h
void listenThread(HWND hWnd){
char Buffer[128] ;
char str[200];
int rVal, fromlen;
SOCKADDR_IN local, from ;
WSADATA wsaData ;
SOCKET listen_socket ;
SOCKET msgsock ;
do{ // スレッドを抜けるための大枠
// ソケットの初期化
if((rVal = WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) ) != 0 )
{
MessageBox( hWnd, ソケットの初期化に失敗しまし
た, ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// SOCKADDR_IN構造体の設定
local.sin_family = AF_INET ;
local.sin_addr.s_addr = INADDR_ANY ;
local.sin_port = htons( port ) ;
// ソケット生成
listen_socket = socket( AF_INET, SOCK_STREAM, 0 ) ; // TCP
socket
if( listen_socket == INVALID_SOCKET ) {
MessageBox( hWnd, ソケットの作成に失敗しまし
た, ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup();
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// 生成したソケットにバインド
if( bind( listen_socket, (struct sockaddr*)&local, sizeof
(local) ) == SOCKET_ERROR) {
MessageBox( hWnd, IPアドレスとポートの関連付けに失
敗しました, ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup();
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// listen
if( listen( listen_socket, 5 ) == SOCKET_ERROR ) {
MessageBox( hWnd, 受信待ちに失敗しました, ソケッ
トエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
wsprintf( (LPSTR)&str, ポート番号%dをlistning中..., port);
drawClient(hWnd,memdc,str,hFont,CharHeight);
// accept
fromlen = sizeof( from ) ;
msgsock = accept( listen_socket, (struct sockaddr*)&from,
&fromlen ) ;
if( msgsock == INVALID_SOCKET ) {
MessageBox( hWnd, 接続に失敗しました, ソケットエ
ラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
wsprintf( (LPSTR)&str, \nサーバ=%s, ポート番号=%dに接
続.\n\n,inet_ntoa(from.sin_addr), htons(from.sin_port));
drawClient(hWnd,memdc,str,hFont,CharHeight);
// 受信永久ループ
while( TRUE ) {
// メッセージを受信
rVal = recv( msgsock, Buffer, sizeof( Buffer ), 0 );
if( rVal == SOCKET_ERROR ) {
MessageBox( hWnd, 受信に失敗しました, ソ
ケットエラー, MB_OK | MB_ICONEXCLAMATION);
break ;
}
if( rVal == 0 ) {
drawClient(hWnd,memdc,接続が終了しまし
た,hFont,CharHeight);
break ;
}
// 「.」なら終了
if( Buffer[0] == '.' ){
drawClient(hWnd,memdc,接続が終了しまし
た,hFont,CharHeight);
break ;
}
// メッセージを表示してループを続ける
Buffer[rVal] = '\0' ;
drawClient(hWnd,memdc,Buffer,hFont,CharHeight);
}
}while(FALSE);
// 終了処理
shutdown(msgsock,2); // SD_BOTH 送受信を無効にする
closesocket( msgsock ) ;
PostMessage(hWnd, ID_EXIT_THREAD, 0, 0);
WSACleanup() ;
}
すみません、ソース見にくいので再び晴らせていただきます。
#include <windows.h>
#include <winsock.h>
#include <stdlib.h>
#include <stdio.h>
#include recv.h
#include resource.h
void listenThread(HWND hWnd){
char Buffer[128] ;
char str[200];
int rVal, fromlen;
SOCKADDR_IN local, from ;
WSADATA wsaData ;
SOCKET listen_socket ;
SOCKET msgsock ;
do{
// ソケットの初期化
if((rVal = WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) ) != 0 )
{
MessageBox( hWnd, ソケットの初期化に失敗,
ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// SOCKADDR_IN構造体の設定
local.sin_family = AF_INET ;
local.sin_addr.s_addr = INADDR_ANY ;
local.sin_port = htons( port ) ;
// ソケット生成
listen_socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
if( listen_socket == INVALID_SOCKET ) {
MessageBox( hWnd, ソケットの作成に失敗,
ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup();
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// 生成したソケットにバインド
if( bind( listen_socket, (struct sockaddr*)&local,
sizeof(local) ) == SOCKET_ERROR) {
MessageBox( hWnd, 関連付けに失敗,
ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup();
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
// listen
if( listen( listen_socket, 5 ) == SOCKET_ERROR ) {
MessageBox( hWnd, 受信待ちに失敗,
ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
wsprintf( (LPSTR)&str, ポート番号%dをlistning中..., port);
drawClient(hWnd,memdc,str,hFont,CharHeight);
// accept
fromlen = sizeof( from ) ;
msgsock = accept( listen_socket, (struct sockaddr*)&from,
&fromlen ) ;
if( msgsock == INVALID_SOCKET ) {
MessageBox( hWnd, 接続に失敗,
ソケットエラー, MB_OK | MB_ICONEXCLAMATION);
WSACleanup() ;
PostMessage(HWND_BROADCAST, ID_EXIT_THREAD, 0, 0);
break;
}
wsprintf( (LPSTR)&str,
\nサーバ=%s, ポート番号=%dに接続.\n\n,
inet_ntoa(from.sin_addr), htons(from.sin_port));
drawClient(hWnd,memdc,str,hFont,CharHeight);
// 受信永久ループ
while( TRUE ) {
// メッセージを受信
rVal = recv( msgsock, Buffer, sizeof( Buffer ), 0 );
if( rVal == SOCKET_ERROR ) {
MessageBox( hWnd, 受信に失敗,
ソケットエラー,
MB_OK | MB_ICONEXCLAMATION);
break ;
}
if( rVal == 0 ) {
drawClient(hWnd,memdc,
接続の終了,hFont,CharHeight);
break ;
}
// 「.」なら終了
if( Buffer[0] == '.' ){
drawClient(hWnd,memdc,
接続の終了,hFont,CharHeight);
break ;
}
// メッセージを表示してループを続ける
Buffer[rVal] = '\0' ;
drawClient(hWnd,memdc,Buffer,hFont,CharHeight);
}
}while(FALSE);
// 終了処理
shutdown(msgsock,2); // SD_BOTH 送受信を無効にする
closesocket( msgsock ) ;
PostMessage(hWnd, ID_EXIT_THREAD, 0, 0);
WSACleanup() ;
}
頭から全部スレッド化するんじゃなくて、bind は一回でいいと思います。
ソケットの初期化~listen までを一回だけ行い、accept 待ちスレッドを立てる。
accept から戻ったら、受信スレッドを立てる、で。
listenがエラーになるのは単純にlistenソケットを閉じてないからなのでは?
というか、ループごとに毎回WSAStartupをするのは問題があると思います。
(WSACleanupと対応していないし)
根本的な問題としてlistenの使い方を勘違いしていると思います。
bind → ソケットを固定ポートと関連付ける
listen → 指定ソケットで接続を受け付けることを通知する
accept → クライアントからの接続を実際に受け付ける
なので、リッスンしたらそのソケットをずっと利用してacceptでクライアントの
接続を待ちつづけます。
具体的には(エラー処理を除く)
// ソケット作成
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
// ソケットを指定ポートに関連付ける
bind(listenSocket, (sockaddr*)&local, sizeof(local));
// ソケットがリッスンすることを通知
listen(listenSocket, 5);
while(1) {
// 新規クライアントの受付待ち
msgsock = accept(listenSocket, (struct sockaddr*)&from, &fromlen );
while(1) {
// クライアントとの通信
...
}
// クライアントを切断
closesocket(msgsock);
}
closesocket(listenSocket);
という流れになります。
あ、シャノンさんが完結にされてましたね(^^;;;
九条様、シャノン様、いろいろご指摘ありがとうございました。
言われたとおり修正したら、何の問題もなくなりました。
本当にありがとうございました。