開発環境
Windows XP SP1
Visual C++ .NET MFC
ダイアログベースでUDP通信プログラムを作成しています。
recvfrom( )でSOCKET_ERRORが帰ってきたのでGetLastError( )でエラーコードを取得し
たところ、「 10054 」というコードでした。
このエラーコードが発生する要因がわからないので知っている方がいましたら教えてく
ださい。
recvfrom( )を呼び出す関数内でWaitForSingleObject( )を使っているのですが、それも
関係しているのでしょうか?
データ受信時のタイムアウトを検知したいので使用しています。
10054に対応するエラー内容が分からない場合:
Windows Sockets Error Codes
http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp
エラー内容は分かるがrecvfrom()で発生する原因が分からない場合:
recvfrom
http://msdn.microsoft.com/library/en-us/winsock/winsock/recvfrom_2.asp
原因は分かっているが、身に覚えがない、または対処法が分からない場合:
問題が発生する最小限のコードを示してください。
dairygoods さん、スレありがとうございます。
英語だらけで大変でしたけど、結局のところ相手側が通信を切断したから OS が「あん
たと通信してた人は通信切っちゃったよ」と教えてくれた・・・ってことなんでしょう
か?
処理コードは以下のとおりです。
BOOL xxxxxx( )
{
// ソケットイベント生成
hEvent = WSACreateEvent( ) ;
if( hEvent == WSA_INVALID_EVENT )
{
// エラーログを出力
}
else
{
// ソケットイベント関連付け
nStatus = WSAEventSelect( m_nUdpSocket, hEvent, FD_READ ) ;
if( nStatus == SOCKET_EVENT )
{
// エラーログを出力
}
else
{
nRtn = WaitForSingleObject( hEvent, 5000 ) ;
if( nRtn == WAIT_OBJECT_0 )
{
// イベント受信
memset( &SockAddr, 0x00, sizeof( SockAddr ) ) ;
memset( &szRecvData[0], 0x00, sizeof( szRecvBuff ) ) ;
nSockAddrLen = ( INT )sizeof( SockAddr ) ;
nSize = recvfrom( m_nUdpSocket,
( char* )&szRecvBuff[0],
sizeof( m_ndRecvData ),
0,
( SOCKADDR * )&SockAddr,
&nSockAddrLen ) ;
if( nSize == SOCKET_EVENT )
{
// エラーログを出力( ここで WSAECONNEVENT[ 10054 ]を取得 )
xxxx( Receive Data Error( Code : %d ),
GetLastError( ) ) ;
}
}
else
{
// エラーログを出力
}
}
}
if( nEvent != WSA_INVALID_EVENT )
{
WSACloseEvent( nEvent ) ;
}
}
変数の定義などは省かせていただきました。
UDPソケットの生成部分も見せてください。
追記します。
2台の Windows マシン(ともに OS は、Windows XP SP1 )上でそれぞれサーバ用プロ
グラムとクライアント用プログラムを動作させています。
サーバ/クライアント用プログラムはともに Visual C++ .NET MFC ダイアログベースで
作成しています。
1回以上通信を行い(正常の通信はなんら問題はないです)サーバ用プログラムを終了
させて、クライアント用マシンから送信すると recvfrom( ) で SOCKET_ERROR が帰って
きて、取得したエラーコードが WSAECONNRESET[ 10054 ]だったというわけです。
期待している動作としては WaitForSingleObject( ) でタイムアウトなのですが、それ
より先にエラーが帰ってきてしまいます。
サーバ用プログラムは、指示された通知に対して応答を返すだけなので、
WaitForSingleObject( ) は使っていません。
クライアント用プログラムのみ使用しています。
ソケット生成に関しては両方とも同じ処理をしています。
違いはポート番号くらいです。
クライアント側のソケット生成処理です。
BOOL xxxxOpen( int nPort )
{
WSADATA wsaData ;
SOCKADDR_IN SockAddrIn ;
BOOL bStatus = TRUE ;
if( WSAStartup( MAKEWORD( 2, 0 ), &wsaData ) )
{
bStatus = FALSE ;
}
else
{
m_nUdpSocket = socket( AF_INET , SOCK_DGRAM , 0 ) ;
if( m_nUdpSocket == INVALID_SOCKET )
{
bStatus = FALSE ;
}
else
{
memset( &SockAddrIn , 0x00 , sizeof( SockAddrIn ) ) ;
SockAddrIn.sin_family = AF_INET ;
SockAddrIn.sin_port = htons( nPort ) ;
SockAddrIn.sin_addr.s_addr = htonl( INADDR_ANY ) ;
// バインドに失敗
if( bind( m_nUdpSocket , ( LPSOCKADDR )&SockAddrIn ,
( int )sizeof( SockAddrIn ) ) )
{
bStatus = FALSE ;
}
}
}
if( bStatus == FALSE )
{
if( m_nUdpSocket != INVALID_SOCKET )
{
closesocket( m_nUdpSocket ) ;
}
WSACleanup( ) ;
}
return( bStatus ) ;
}
む、、
私、Windowsソケットに詳しくは無いので、Linuxに置き換えて考えていました。
connect()を呼んでいるならば、ICMPエラーが取れるので
それで、ECONNREFUSEDとなっていると思って生成部を見せて貰ったんですが・・・
CONNRESETですか・・・
もはや、どう動くのか?という検証でしかありませんが、
サーバー用プログラムが使用していたポートがTIMEWAITになっていませんか?
(./netstat -a で見れます。)
そうなっていたらTIMEWAITは暫くすると消えるので、その後はエラーとなりますか?
動作が変わるなら、多分、そうゆーものなんですw。
すみません、解決の方向には向かってないので、別にやらなくても良いです。
残念ながら、UDP なので connect( ) はしていません。
単純に通信していた相手が切断されたことを Windows XP が検知してエラーを返してく
れるのであれば、それはそれでよいのです。
やはり、Windows XP の仕様なのかなぁ。
的外れかもですが、横槍失礼します。
MSDNに以下の内容があったので、たぶんこれのことではないかと・・・
「If the socket is connection oriented and the remote side has shut down the
connection gracefully, the call to recvfrom will complete immediately with zero
bytes received.
If the connection has been reset recvfrom will fail with the error
WSAECONNRESET.」
私の英語力では訳が正しくないかもですが・・・
「リモート側で通信が切断されたら、recvfrom は0バイトのデータを受信したものとして終了
します。
再接続された場合は、WSAECONNRESET のエラーで失敗します。」
と言うことは、現状の動作であってるぽい??
# 関係なかったらごめんなさい・・・m(__
> 1回以上通信を行い(正常の通信はなんら問題はないです)サーバ用プログラムを終了
> させて、クライアント用マシンから送信すると recvfrom( ) で SOCKET_ERROR が帰って
> きて、取得したエラーコードが WSAECONNRESET[ 10054 ]だったというわけです。
> 期待している動作としては WaitForSingleObject( ) でタイムアウトなのですが、それ
> より先にエラーが帰ってきてしまいます。
イベントオブジェクトは成功しても失敗してもシグナル状態になるので、
WaitForSingleObject()でイベントが発生した後、
WSAEnumNetworkEvents()でイベントの発生原因を調べる必要があると思います。