• 締切済み

WinSockでの通信プログラムがうまくいきません

WinSockでの通信プログラムがうまくいきません。 使用言語はC++とDirectXです。 ローカルでの通信(ルータを介したパソコン同士)はうまくいくんですが、別の場所にあるPCとの通信ができません。 以下実際に使用している関数です。 関数はマルチスレッドで動かしています。 WSADATA mWsaData; SOCKET mSockYou,mSockI; struct sockaddr_in mAddr; struct sockaddr_in mServer; struct sockaddr_in mClient; void CNetwork::Init(){ int err = WSAStartup( MAKEWORD( 2, 0 ), &mWsaData ); if( err != 0 ){ ERROR_EXIT(); return; } //Socket初期化 mSockI = socket( AF_INET, SOCK_STREAM, 0 ); if( mSockI == INVALID_SOCKET ){ ERROR_EXIT(); return; } mPort = 0; memset( mName, 0, sizeof( mName ) ); } //Server側 void CNetwork::Accept(){ FILE* fp; fopen_s( &fp, "messageLog.txt", "w" ); mAddr.sin_family = AF_INET; mAddr.sin_port = htons( mPort ); mAddr.sin_addr.S_un.S_addr = INADDR_ANY; if( bind( mSockI, (struct sockaddr *)&mAddr, sizeof( mAddr ) ) ) fprintf_s( fp, "bind失敗\n" ); if( listen( mSockI, 10 ) != 0 ) fprintf_s( fp, "listen失敗\n" ); int len = sizeof( mClient ); SOCKET t = accept( mSockI, (struct sockaddr*)&mClient, &len ); if( t == INVALID_SOCKET ) fprintf_s( fp, "Accept失敗\n" ); mSockYou = t; fprintf_s( fp, "Accept終了\n" ); fclose( fp ); } void CNetwork::Connect(){ FILE* fp; fopen_s( &fp, "messageLog.txt", "a" ); fprintf_s( fp, "Connect開始\n" ); //ソケットの設定 mServer.sin_family = AF_INET; mServer.sin_port = htons( mPort ); mServer.sin_addr.S_un.S_addr = inet_addr( mName ); if (mServer.sin_addr.S_un.S_addr == 0xffffffff) { fprintf_s( fp, "hostbynameへ\n" ); struct hostent *host; host = gethostbyname( mName ); if ( host == NULL ) { return false; } mServer.sin_addr.S_un.S_addr = *(unsigned int *)host->h_addr_list[0]; } fprintf_s( fp, "Socketの設定完了\n" ); while( true ){ fprintf_s( fp, "connect()開始\n" ); if( connect( mSockI, (struct sockaddr *)&mServer, sizeof( mServer ) ) == 0 ){ fprintf_s( fp, "Connect完了\n" ); break; } else{ fprintf_s( fp, "Connect失敗\n" ); Sleep(100); } } fprintf_s( fp, "Connect終了\n" ); fclose(fp); } また、ログは下のように出ました。 //サーバ側 Accept失敗 //クライアント側 Connect開始 Socketの設定完了 connect()開始 Connect失敗 ご教授お願いします。

みんなの回答

回答No.10

NO.9の者です。 もし複数ルーターで分断されたLAN同士の通信ということでなく、ルーターを一段介しただけのクライアント・サーバー間通信であればNATブレイクは必要ないのでプログラムのバグでしょう。 出来ればネットワーク構成と端末接続構成を知りたいですね。

回答No.9

あなたのプログラムを読んではいないのですが、もし実験しているPC群が複数のルータで分けられた複数LANに散在しているのであればNATブレイクの必要アリかと・・・ P2P通信には必須の方法です。 ルーターR0以下に構成されているLAN内にあるソケット同士は普通にソケット関数をたたいてやれば通信できますが、ルーターR0とルーターR1で別々のLANが構築されていて、どのルーターもブリッジモードにはなっていないという前提(ルートのルーターにカスケード接続されたルーターとして動いている)ならば、R0以下のPCとR1以下のPCがP2P通信するにはまずNATブレイクでしょうね。NATピンホールパンチなどとも呼びますか・・

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.8

エラーについて書き忘れです。 >これは、関数呼び出しの間に割り込みがあったとのことですが、 >そのような処理の原因として何か考えられるものはあるでしょうか? >マルチスレッドにしたことが原因でしょうか? >(一定時間ごとに呼び出されるため、いちいち中断している可能性もありますし) WSAEINTRはWinsock1.1の停止型ソケットが WSACancelBlockingCallで取り消されたときに 発生するエラーです。 この関数は、現在非推奨になっていて、 一般にWinsock2では外部公開されない関数です。 MSDNにはGUIスレッドから内部で呼び出される 可能性があると記載がありますので、 GUIを使用すると発生するという可能性があります。 対応策としては、非同期ソケットにする必要が あるかもしれません。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.7

反応が遅くなりましたが。。。 ちょこっと見てて気になったことを 書いておきます。 ・socketの作成 プロトコルが0で指定されていますが、 環境によって値が変わってしまうので、 TCPならIPPROTO_TCPを指定した方がよさそう。 ちなみに手持ちの環境で、winsock1なら 0はIPPROTO_IP、IPPROTO_TCPは6に なっています。 #VC2008環境。 ・Bind対象のアドレス 一番気になったところですが、 サーバ側のソケットは意図した アドレスにbindされていますか? ListenSocketのアドレス情報が、 mAddr.sin_addr.S_un.S_addr = INADDR_ANY; となっている為、意図したアドレスに bindされていないような気がします。 #ちなみにInAddrを使用する方法は #既に一般的ではありません。 ・マルチスレッド 標準ライブラリ関数と、_beginthread CreateThreadなどを併用すると メモリリークが発生するという問題が 確か数年前にMSから出ていたと思います。 Windows上で標準関数とマルチスレッドを 使用する場合は_beginthreadex() _endthreadex()を使いましょう。 ・クライアントについて クライアントで使用されているOSは何でしょうか? WindowsVista以降の場合、IPv6のアドレスが 割り当てられている可能性もありますが、 上記のコードはIPv4環境でしか使用できません。 #IPv6とIPv4が両方割り当てられていれば大丈夫かも。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.6

返却されるエラーについては不明になってしまいますな…。 より低レベルな部分で確認が必要なのかも知れません。 パーソナルファイヤーウォールで拒否はしていませんよね? # 同一ネットワークの場合は無条件で受け入れ…とかになっているのかも知れませんし。 Wiresharkなどで接続要求が来ているか確認してみるのも必要かも知れません。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.5

>クラス内の関数をマルチスレッドで動かすようにしています。Connectも同様です。 CNetwork::AcceptStart()の呼び出し元でインスタンスの破棄とかしていませんか? # デストラクタのshutdown()とか走ったから、「WSACancelBlockingCall()でキャンセルを…」というエラーが返却されている…とか考えられませんかね? というか…スレッドにしてもインスタンスを破棄する前に起動したスレッドを停止させないとなりませんよね? accept()が動作しないと、TerminateThread()で無理矢理殺すことになるかと思われますが…。 # _beginthread()で生成したモノをTerminateThread()で…だと、リソースリークとかしそうな気がします。 >一定時間main関数(またはメッセージ処理)を行わないとフリーズして止まってしまうので、このような形でサブスレッドで実行するようにしてます。 select()または、WSAAsyncSelect()やWSAEventSelect()を使うべき…かと。 とはいえ、ローカルネットワークでは問題ない(表面化していないだけの可能性もありますが)が、別の場所(外部ネットワーク経由?)だとダメ…というのも……。 # ルータのポート転送関係はチェック済みなんですよね?当然。

dai_directx
質問者

補足

>インスタンスの破棄 インスタンスの破棄は、アプリケーション自体を閉じたときにしか呼び出していないのでおそらくないとは思います・・・ >ルータのポート転送関係 ポートの開放のことでしょうか?ポートの開放でしたら、お互いに開放は行っています。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.4

>accept()は接続要求されるまでそこで待機されると思っていたので、connect()とのタイミングなどとの関係はあるのですか? ブロッキングモードですので接続されるまで待つでしょうから、とりあえずは問題なさそうです。 # accept()がタイムアウト…なんてことはたぶん無いと思われますが。 サーバ側のマルチスレッド…はどのような動作になっているのでしょう? 呼び出し元のCNetwork::Accept()は接続されるまで戻ってきませんが、問題ない…んですよね? # 戻らない…ので、そのクラスを別のスレッドから破棄することもできない…ハズです。 accept()で、エラーがWSAEINTRとなると…WSACancelBlockingCall()でキャンセルしろ…と?

dai_directx
質問者

補足

サーバ側の動作は以下のようにしています。 クラス内の関数をマルチスレッドで動かすようにしています。Connectも同様です。 void CNetwork::AcceptStart(){  mThread = (HANDLE) _beginthread(      &CNetwork::AcceptLauncher,      0,      this); } static void AcceptLauncher(void* args){  reinterpret_cast<CNetwork*>(args)->Accept(); } DirectXの仕様なのか、一定時間main関数(またはメッセージ処理)を行わないとフリーズして止まってしまうので、このような形でサブスレッドで実行するようにしてます。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.3

>accept関数の後、調べてみた結果WSAEINTRというエラーが発生していました。 listen()の直後にaccept()している様ですが… ねそのタイミングでクライアントがconnect()していますか? # ノンブロッキングでしょうから、タイムアウトまでaccept()で止まっているとは思われますが…。 複数接続できるようには作られていないっぽいので、listen()で10接続受け付ける様にするのは止めた方がいいかもしれません。 # まぁ、初回以外はaccept()されないのでクライアントはタイムアウトするでしょうが。 ということで、マルチスレッドにしても接続できるのは1クライアントだけです。 テストに使用した環境が接続保持状態のまま正しく開放できていなかったりしませんか? CNetworkクラスがどうなっているのか、同じポートで複数のインスタンスを作っていないか(その場合、bind()失敗ですが)、デストラクタで正しくSocket()の開放が行われているか…等々確認されてはどうでしょうか?

参考URL:
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/
dai_directx
質問者

補足

>listen()の直後にaccept()している accept()は接続要求されるまでそこで待機されると思っていたので、connect()とのタイミングなどとの関係はあるのですか? >複数接続できるようには作られていない はい、このプログラムでは1つだけ接続が確立できればいいです。その場合、listen()では1つだけ受け付けるようにすればいいのですか? >テストに使用した環境が接続保持状態のまま正しく開放できていなかったりしませんか? 一応毎回CNetwork内のデストラクタで解放処理は行っています。 以下ソースコードです。 void CNetwork::Destroy(){  if( mType == TCP_SERVER ){   shutdown( mSockI, SD_BOTH );   closesocket( mSockYou );  }  //WinSock解放  WSACleanup();  ThreadClose();  DeleteCriticalSection( &mCSThread ); }

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.2

通信周りの関数で失敗した直後に WSAGetLastError関数を使用して エラーの詳細コードを取るとセッションが 確率できない理由がわかるかも知れません。 大抵の場合、ローカルエリアでつながるけど インターネットでつなげないのは、 ポートの解放がうまく行ってない場合が多いです。 ポートの設定はお使いのルータによって異なるので 確認してみてください。

dai_directx
質問者

補足

accept関数の後、調べてみた結果WSAEINTRというエラーが発生していました。 これは、関数呼び出しの間に割り込みがあったとのことですが、そのような処理の原因として 何か考えられるものはあるでしょうか? マルチスレッドにしたことが原因でしょうか?(一定時間ごとに呼び出されるため、いちいち中断している可能性もありますし)

回答No.1

ログの状況から見て、サーバー側のAccept失敗が問題だと感じます。 Acceptの失敗コードをとるのが良いのではないでしょうか? ちなみにAcceptが失敗する理由は、 http://software.aufheben.info/kouza/senior/kouza_socket3.html を参照されると、良いかもしれません。