// // ネットプレイクラス // // 明示的にWinSock Libをリンク #pragma comment(lib, "wsock32.lib") #include "DebugOut.h" #include "NetPlay.h" #define CLOSESOCKET(soc) if((soc)!=INVALID_SOCKET){::closesocket((soc));(soc)=INVALID_SOCKET;} static char* SocketErrorDump( int eno ); CNetPlay NetPlay; CNetPlay::CNetPlay() { m_hWnd = m_hWndMsg = m_hWndChat = NULL; m_bConnect = FALSE; m_SocketConnect = INVALID_SOCKET; m_SocketData = INVALID_SOCKET; m_SocketChat = INVALID_SOCKET; m_nLatency = 0; m_nFrameCount = 0; m_nFrameStep = 0; m_hASyncTask = NULL; } CNetPlay::~CNetPlay() { // とりあえず Release(); } BOOL CNetPlay::Initialize( HWND hWnd ) { // とりあえず Release(); // WinSock DLLの初期化 if( ::WSAStartup( MAKEWORD(1,1), &m_WSAdata ) ) return FALSE; // バージョン違うじゃ〜ん if( m_WSAdata.wVersion != MAKEWORD(1,1) ) { ::WSACleanup(); return FALSE; } m_hWnd = hWnd; return TRUE; } void CNetPlay::Release() { Disconnect(); if( m_hWnd ) { ::WSACleanup(); m_hWnd = NULL; } } INT CNetPlay::ASyncHostCheck( HWND hWnd, const char* lpszHost ) { DEBUGOUT( "CNetPlay:ASyncHostCheck [%s]\n", lpszHost ); unsigned long IP_address = ::inet_addr( lpszHost ); if( IP_address != INADDR_NONE ) { DEBUGOUT( "CNetPlay:生IPっす\n" ); return 0; } m_hWndASync = hWnd; // ホスト検索開始 m_hASyncTask = ::WSAAsyncGetHostByName( hWnd, WM_NETPLAY_HOSTBYNAME, lpszHost, m_HostEntry, MAXGETHOSTSTRUCT ); if( m_hASyncTask ) { return 1; } return -1; } HRESULT CNetPlay::ASyncWndProc( HWND hWnd, WPARAM wParam, LPARAM lParam ) { DEBUGOUT( "CNetPlay:ASyncWndProc\n" ); if( WSAGETASYNCERROR(lParam) ) { DEBUGOUT( "CNetPlay:ASyncWndProc error.[%s]\n", SocketErrorDump( WSAGETASYNCERROR(lParam) ) ); m_hWndASync = NULL; return 0; } if( m_hASyncTask == (HANDLE)wParam ) { if( m_hWndASync ) { m_hWndASync = NULL; return *((unsigned long *)((((struct hostent FAR *)m_HostEntry)->h_addr_list)[0])); } } return 0L; } BOOL CNetPlay::Connect( BOOL bServer, const char* lpszIP, unsigned short Port ) { if( !m_hWnd ) return FALSE; m_bServer = bServer; if( bServer ) { // Server // 接続通知ソケット作成 if( m_SocketConnect == INVALID_SOCKET ) { m_SocketConnect = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if( m_SocketConnect == INVALID_SOCKET ) { DEBUGOUT( "CNetPlay:socket failed.\n" ); return FALSE; } } // 再利用を許可してみる unsigned long ulOpt = 1; if( ::setsockopt( m_SocketConnect, SOL_SOCKET, SO_REUSEADDR, (const char*)&ulOpt, sizeof(ulOpt) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:setsockopt failed. (SO_REUSEADDR)\n" ); CLOSESOCKET( m_SocketConnect ); return FALSE; } // ポートと結びつける ZEROMEMORY( &m_SAddr_Server, sizeof(m_SAddr_Server) ); m_SAddr_Server.sin_family = AF_INET; m_SAddr_Server.sin_addr.s_addr = ::htonl( INADDR_ANY ); m_SAddr_Server.sin_port = ::htons( Port ); if( ::bind( m_SocketConnect, (struct sockaddr *)&m_SAddr_Server, sizeof(m_SAddr_Server) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:bind failed.\n" ); CLOSESOCKET( m_SocketConnect ); return FALSE; } //----------------- // チャット用ソケット作成 m_SocketChat = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( m_SocketChat == INVALID_SOCKET ) { DEBUGOUT( "create socket failed.[chat]\n" ); Disconnect(); return FALSE; } // ポートに接続 if( ::bind( m_SocketChat, (struct sockaddr *)&m_SAddr_Server, sizeof(m_SAddr_Server) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:connect failed. [chat]\n" ); Disconnect(); return FALSE; } // ブロッキングモード設定 unsigned long ulArg = 1; if( ::ioctlsocket( m_SocketChat, FIONBIO, &ulArg ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:ioctlsocket failed.[chat]\n" ); Disconnect(); return FALSE; } //----------------- // 接続要求イベントの設定 if( ::WSAAsyncSelect( m_SocketConnect, m_hWnd, WM_NETPLAY, FD_ACCEPT ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.\n" ); CLOSESOCKET( m_SocketConnect ); return FALSE; } // 接続要求受付開始してみる if( ::listen( m_SocketConnect, 1 ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:listen failed.\n" ); CLOSESOCKET( m_SocketConnect ); return FALSE; } } else { // Client unsigned long ulOpt; // IPアドレス? unsigned long IP_address = ::inet_addr( lpszIP ); if( IP_address == INADDR_NONE ) { DEBUGOUT( "CNetPlay:IPアドレスが不正です。\"%s\"\n", lpszIP ); return FALSE; } // データ通信ソケット作成 m_SocketData = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if( m_SocketData == INVALID_SOCKET ) { DEBUGOUT( "CNetPlay:socket failed.\n" ); return FALSE; } // 再利用を許可してみる ulOpt = 1; if( ::setsockopt( m_SocketData, SOL_SOCKET, SO_REUSEADDR, (const char*)&ulOpt, sizeof(ulOpt) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:setsockopt failed. (SO_REUSEADDR)\n" ); CLOSESOCKET( m_SocketData ); return FALSE; } // Nagleアルゴリズムの無効化 ulOpt = 1; if( ::setsockopt( m_SocketData, IPPROTO_TCP, TCP_NODELAY, (const char*)&ulOpt, sizeof(ulOpt) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:setsockopt failed.\n" ); CLOSESOCKET( m_SocketData ); return FALSE; } // ブロッキングモード設定 unsigned long ulArg = 1; if( ::ioctlsocket( m_SocketData, FIONBIO, &ulArg ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:ioctlsocket failed.\n" ); CLOSESOCKET( m_SocketData ); return FALSE; } //----------------- // チャット用ソケット作成 m_SocketChat = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( m_SocketChat == INVALID_SOCKET ) { DEBUGOUT( "create socket failed.[chat]\n" ); Disconnect(); return FALSE; } // ブロッキングモード設定 // unsigned long ulArg = 1; ulArg = 1; if( ::ioctlsocket( m_SocketChat, FIONBIO, &ulArg ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:ioctlsocket failed.[chat]\n" ); Disconnect(); return FALSE; } //----------------- // 接続完了イベントの設定 if( ::WSAAsyncSelect( m_SocketData, m_hWnd, WM_NETPLAY, FD_CONNECT ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.\n" ); CLOSESOCKET( m_SocketData ); return FALSE; } // 接続を要求してみる ZEROMEMORY( &m_SAddr_Server, sizeof(m_SAddr_Server) ); m_SAddr_Server.sin_family = AF_INET; m_SAddr_Server.sin_addr.s_addr = IP_address; m_SAddr_Server.sin_port = ::htons( Port ); if( ::connect( m_SocketData, (struct sockaddr *)&m_SAddr_Server, sizeof(m_SAddr_Server) ) == SOCKET_ERROR ) { if( ::WSAGetLastError() != WSAEWOULDBLOCK ) { DEBUGOUT( "CNetPlay:connect failed.\n" ); CLOSESOCKET( m_SocketData ); return FALSE; } } } return TRUE; } void CNetPlay::Disconnect() { #if defined(_DEBUG)||defined(_DEBUGOUT) if( m_bConnect ) { DEBUGOUT( "CNetPlay::Disconnect\n" ); } #endif if( m_hASyncTask ) { ::WSACancelAsyncRequest( m_hASyncTask ); m_hASyncTask = NULL; } // ソケットをシャットダウンして破棄 if( m_SocketConnect != INVALID_SOCKET ) { ::shutdown( m_SocketConnect, SD_BOTH ); CLOSESOCKET( m_SocketConnect ); } if( m_SocketData != INVALID_SOCKET ) { ::shutdown( m_SocketData, SD_BOTH ); CLOSESOCKET( m_SocketData ); } if( m_SocketChat != INVALID_SOCKET ) { ::shutdown( m_SocketChat, SD_BOTH ); CLOSESOCKET( m_SocketChat ); } m_bConnect = FALSE; } INT CNetPlay::Send( BYTE data ) { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return -1L; while( TRUE ) { if( ::send( m_SocketData, (char*)&data, sizeof(BYTE), 0 ) == SOCKET_ERROR ) { // ブロックされたかも知れないので再度挑戦 if( ::WSAGetLastError() == WSAEWOULDBLOCK ) { ::Sleep(0); // 固まらない措置 continue; } else { // 致命的エラーっぽい DEBUGOUT( "CNetPlay:send failed. code=%d\n", ::WSAGetLastError() ); Disconnect(); return -1L; } } else { break; } } return 0L; } INT CNetPlay::Recv( BYTE& data ) { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return -1L; // データが届いているかを確認 unsigned long len = 0; if( ::ioctlsocket( m_SocketData, FIONREAD, (unsigned long*)&len ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:ioctlsocket failed.\n" ); Disconnect(); return -1L; } if( !len ) { // データが届いていない return 0L; } else { // 1バイト読み込み if( ::recv( m_SocketData, (char*)&data, sizeof(BYTE), 0 ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:recv failed.\n" ); Disconnect(); return -1L; } } return len; } INT CNetPlay::RecvTime( BYTE& data, unsigned long timeout ) { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return -1L; INT ret; DWORD dwTimeOut = ::timeGetTime(); while( (ret = NetPlay.Recv( data )) == 0 ) { // 固まらない措置 ::Sleep( 0 ); // タイムアウトのチェック if( (::timeGetTime()-dwTimeOut) > timeout ) { return -1; } } return ret; } BOOL CNetPlay::RecvBuffer() { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return FALSE; BYTE buf[SOCKET_RECEIVE_SIZE]; INT recvsize = ::recv( m_SocketData, (char*)buf, SOCKET_RECEIVE_SIZE, 0 ); if( recvsize == 0 ) { return FALSE; } else if( recvsize == SOCKET_ERROR ) { if( ::WSAGetLastError() == WSAEWOULDBLOCK ) { return TRUE; } else { DEBUGOUT( "CNetPlay::RecvBuffer failed. [%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); return FALSE; } } else { INT p = m_nRecvPtr; for( INT i = 0; i < recvsize; i++ ) { m_RecvBuffer[p] = buf[i]; p = (p+1) & SOCKET_BUFFER_SIZE-1; } m_nRecvPtr = p; m_nRecvSize += recvsize; } return TRUE; } INT CNetPlay::GetRecvBufferSize() { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return 0; return m_nRecvSize; } BOOL CNetPlay::BufferCheck() { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return 0; // LAN if( m_nLatency == 0 && m_nRecvSize < SOCKET_BLOCK_SIZE ) { return -1; } // Frame control // 速すぎか? if( m_nRecvSize < SOCKET_BLOCK_SIZE ) { return -1; // 少し待ってくれ } // 遅すぎか? if( m_nRecvSize > ((m_nLatency+2)*SOCKET_BLOCK_SIZE) ) { return 1; // 少し進めてくれ } return 0; } // 同期前処理(レイテンシの分だけ先に送信して同期を取る) INT CNetPlay::Sync() { BYTE senddata[SOCKET_BLOCK_SIZE]; DEBUGOUT( "CNetPlay::Sync\n" ); ZEROMEMORY( senddata, sizeof(senddata) ); m_nFrameCount = 0; m_nRingPtr = m_nSendPtr = m_nRecvPtr = 0; m_nRecvSize = 0; DEBUGOUT( "CNetPlay::Sync sending...\n" ); // データ送信 for( INT i = 0; i < m_nLatency+1; i++ ) { while( TRUE ) { if( ::send( m_SocketData, (char*)senddata, SOCKET_BLOCK_SIZE, 0 ) == SOCKET_ERROR ) { // ブロックされたかも知れないので再度挑戦 if( ::WSAGetLastError() == WSAEWOULDBLOCK ) { DEBUGOUT( "send::WSAEWOULDBLOCK!!\n" ); ::Sleep(0); // 固まらない措置 continue; } else { // 致命的エラーっぽい DEBUGOUT( "CNetPlay:Sync send failed. [%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); Disconnect(); return -1L; } } else { break; } } m_nSendPtr = (m_nSendPtr+SOCKET_BLOCK_SIZE) & SOCKET_BUFFER_SIZE-1; } // 相手データのバッファリングデータ待ち(10秒でタイムアウト) DWORD dwTime = ::timeGetTime(); while( m_nRecvSize < (m_nLatency+1)*SOCKET_BLOCK_SIZE ) { if( !RecvBuffer() ) { DEBUGOUT( "CNetPlay:recv error.\n" ); Disconnect(); return -1; } if( (::timeGetTime()-dwTime) > 10*1000 ) { DEBUGOUT( "CNetPlay:Sync recv timeout.\n" ); Disconnect(); return -1; } ::Sleep( 1 ); } DEBUGOUT( "CNetPlay::Sync OK!\n" ); return 0; } // プレイヤーキーの更新 INT CNetPlay::ModifyPlayer( LPBYTE p1, LPBYTE p2 ) { if( !m_hWnd || !m_bConnect || m_SocketData == INVALID_SOCKET ) return -1L; // 送るべきポイントで送信 if( m_nFrameCount == 0 ) { while( TRUE ) { if( ::send( m_SocketData, (char*)p1, SOCKET_BLOCK_SIZE, 0 ) == SOCKET_ERROR ) { // ブロックされたかも知れないので再度挑戦 if( ::WSAGetLastError() == WSAEWOULDBLOCK ) { DEBUGOUT( "send::WSAEWOULDBLOCK!!\n" ); ::Sleep(0); // 固まらない措置 continue; } else { // 致命的エラーっぽい DEBUGOUT( "CNetPlay:ModifyPlayer send failed. code=%d\n", ::WSAGetLastError() ); Disconnect(); return -1L; } } else { break; } } // バッファに放り込む for( INT i = 0; i < SOCKET_BLOCK_SIZE; i++ ) { m_SendBuffer[ m_nSendPtr ] = p1[i]; m_nSendPtr = (m_nSendPtr+1) & SOCKET_BUFFER_SIZE-1; } } // 相手データ待ち(10秒でタイムアウト) DWORD dwTime = ::timeGetTime(); while( m_nRecvSize < SOCKET_BLOCK_SIZE ) { if( !RecvBuffer() ) { DEBUGOUT( "CNetPlay:ModifyPlayer recv error.\n" ); Disconnect(); return -1; } if( (::timeGetTime()-dwTime) > 10*1000 ) { DEBUGOUT( "CNetPlay:ModifyPlayer recv timeout.\n" ); Disconnect(); return -1; } ::Sleep( 1 ); } // 情報更新 INT p = m_nRingPtr; for( INT i = 0; i < SOCKET_BLOCK_SIZE; i++ ) { p1[i] = m_SendBuffer[ p ]; p2[i] = m_RecvBuffer[ p ]; p = (p+1) & SOCKET_BUFFER_SIZE-1; } INT Count = m_nFrameCount; // ステップ更新 if( ++m_nFrameCount > m_nFrameStep ) { m_nFrameCount = 0; m_nRingPtr = (m_nRingPtr+SOCKET_BLOCK_SIZE) & SOCKET_BUFFER_SIZE-1; m_nRecvSize -= SOCKET_BLOCK_SIZE; } return Count; } void CNetPlay::ChatSend( LPCSTR lpStr ) { if( !m_hWnd || !m_bConnect || m_SocketChat == INVALID_SOCKET ) return; if( m_bServer ) { DEBUGOUT( "ChatSend Server:%s", lpStr ); if( ::sendto( m_SocketChat, (char*)lpStr, ::strlen(lpStr)+1, 0, (struct sockaddr *)&m_SAddr_Client, sizeof(m_SAddr_Client) ) == SOCKET_ERROR ) { DEBUGOUT( "ChatSend failed. Server:[%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); } } else { DEBUGOUT( "ChatSend Client:%s", lpStr ); if( ::sendto( m_SocketChat, (char*)lpStr, ::strlen(lpStr)+1, 0, (struct sockaddr *)&m_SAddr_Server, sizeof(m_SAddr_Server) ) == SOCKET_ERROR ) { DEBUGOUT( "ChatSend failed. Server:[%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); } } } HRESULT CNetPlay::WndProc( HWND hWnd, WPARAM wParam, LPARAM lParam ) { // エラー? if( WSAGETSELECTERROR(lParam) ) { DEBUGOUT( "CNetPlay::WndProc error.[%s]\n", SocketErrorDump( WSAGETSELECTERROR(lParam) ) ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_CLOSE, 0, 0 ); } return 0L; } switch( WSAGETSELECTEVENT(lParam) ) { case FD_ACCEPT: { DEBUGOUT( "Accepting...." ); ZEROMEMORY( &m_SAddr_Client, sizeof(m_SAddr_Client) ); int len = sizeof(m_SAddr_Client); m_SocketData = ::accept( m_SocketConnect, (sockaddr*)&m_SAddr_Client, &len ); // 接続用ソケットは閉じる ::shutdown( m_SocketConnect, SD_BOTH ); CLOSESOCKET( m_SocketConnect ); if( m_SocketData == INVALID_SOCKET ) { DEBUGOUT( "failed.\n" ); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } DEBUGOUT( "done.\n" ); // Nagleアルゴリズムの無効化 unsigned long ulOpt = 1; if( ::setsockopt( m_SocketData, IPPROTO_TCP, TCP_NODELAY, (const char*)&ulOpt, sizeof(ulOpt) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:setsockopt failed.\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } // ブロッキングモード設定 unsigned long ulArg = 1; if( ::ioctlsocket( m_SocketData, FIONBIO, &ulArg ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:ioctlsocket failed.\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } // 非同期イベントの設定 if( ::WSAAsyncSelect( m_SocketChat, m_hWnd, WM_NETPLAY, FD_READ ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.[CONNECT chat]\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } // 非同期イベントの設定 if( ::WSAAsyncSelect( m_SocketData, m_hWnd, WM_NETPLAY, FD_CLOSE ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.[ACCEPT data]\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } m_bConnect = TRUE; if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ACCEPT, 0, 0 ); } break; } case FD_CONNECT: { DEBUGOUT( "Connection done.\n" ); // 1バイトだけダミーデータを送る(何か間違ってる気もする…) if( ::sendto( m_SocketChat, (char*)"", 1, 0, (struct sockaddr *)&m_SAddr_Server, sizeof(m_SAddr_Server) ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:send failed. Client:[%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } // 非同期イベントの設定 if( ::WSAAsyncSelect( m_SocketChat, m_hWnd, WM_NETPLAY, FD_READ ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.[CONNECT chat]\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } // 非同期イベントの設定 if( ::WSAAsyncSelect( m_SocketData, m_hWnd, WM_NETPLAY, FD_CLOSE ) == SOCKET_ERROR ) { DEBUGOUT( "CNetPlay:WSAAsyncSelect failed.[CONNECT data]\n" ); Disconnect(); if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_ERROR, 0, 0 ); } return 0L; } m_bConnect = TRUE; if( m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_CONNECT, 0, 0 ); } break; } case FD_CLOSE: { DEBUGOUT( "Connection close.\n" ); if( m_bConnect && m_hWndMsg ) { ::PostMessage( m_hWndMsg, WM_NETPLAY_CLOSE, 0, 0 ); } Disconnect(); break; } case FD_READ: { // error is ignored :p // DEBUGOUT( "FD_READ\n" ); INT recvsize = 0; INT size = sizeof(struct sockaddr_in); CHAR szBuf[256+1]; if( m_bServer ) { recvsize = ::recvfrom( m_SocketChat, (char*)szBuf, sizeof(szBuf)-1, 0, (struct sockaddr *)&m_SAddr_Client, &size ); } else { recvsize = ::recvfrom( m_SocketChat, (char*)szBuf, sizeof(szBuf)-1, 0, (struct sockaddr *)&m_SAddr_Server, &size ); } if( recvsize == SOCKET_ERROR ) { if( m_bServer ) { DEBUGOUT( "FD_READ failed. Server [%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); } else { DEBUGOUT( "FD_READ failed. Client [%s]\n", SocketErrorDump( ::WSAGetLastError() ) ); } } else if( recvsize > 0 ) { szBuf[recvsize] = '\0'; if( m_hWndChat && ::IsWindow( m_hWndChat ) ) { COPYDATASTRUCT cds; cds.dwData = 0; cds.lpData = (void*)szBuf; cds.cbData = ::strlen(szBuf)+1; // 終端のNULLも送る // 文字列送信 ::SendMessage( m_hWndChat, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds ); } } break; } default: DEBUGOUT( "Unknown message.\n" ); break; } return 0L; } static char* SocketErrorDump( int eno ) { switch( eno ) { case 0: return "No error"; #if defined(_DEBUG)||defined(_DEBUGOUT) case WSAEINTR: return "Interrupted system call"; case WSAEBADF: return "Bad file number"; case WSAEACCES: return "Permission denied"; case WSAEFAULT: return "Bad address"; case WSAEINVAL: return "Invalid argument"; case WSAEMFILE: return "Too many open sockets"; case WSAEWOULDBLOCK: return "Operation would block"; case WSAEINPROGRESS: return "Operation now in progress"; case WSAEALREADY: return "Operation already in progress"; case WSAENOTSOCK: return "Socket operation on non-socket"; case WSAEDESTADDRREQ: return "Destination address required"; case WSAEMSGSIZE: return "Message too long"; case WSAEPROTOTYPE: return "Protocol wrong type for socket"; case WSAENOPROTOOPT: return "Bad protocol option"; case WSAEPROTONOSUPPORT: return "Protocol not supported"; case WSAESOCKTNOSUPPORT: return "Socket type not supported"; case WSAEOPNOTSUPP: return "Operation not supported on socket"; case WSAEPFNOSUPPORT: return "Protocol family not supported"; case WSAEAFNOSUPPORT: return "Address family not supported"; case WSAEADDRINUSE: return "Address already in use"; case WSAEADDRNOTAVAIL: return "Can't assign requested address"; case WSAENETDOWN: return "Network is down"; case WSAENETUNREACH: return "Network is unreachable"; case WSAENETRESET: return "Net connection reset"; case WSAECONNABORTED: return "Software caused connection abort"; case WSAECONNRESET: return "Connection reset by peer"; case WSAENOBUFS: return "No buffer space available"; case WSAEISCONN: return "Socket is already connected"; case WSAENOTCONN: return "Socket is not connected"; case WSAESHUTDOWN: return "Can't send after socket shutdown"; case WSAETOOMANYREFS: return "Too many references, can't splice"; case WSAETIMEDOUT: return "Connection timed out"; case WSAECONNREFUSED: return "Connection refused"; case WSAELOOP: return "Too many levels of symbolic links"; case WSAENAMETOOLONG: return "File name too long"; case WSAEHOSTDOWN: return "Host is down"; case WSAEHOSTUNREACH: return "No route to host"; case WSAENOTEMPTY: return "Directory not empty"; case WSAEPROCLIM: return "Too many processes"; case WSAEUSERS: return "Too many users"; case WSAEDQUOT: return "Disc quota exceeded"; case WSAESTALE: return "Stale NFS file handle"; case WSAEREMOTE: return "Too many levels of remote in path"; case WSAEDISCON: return "Graceful shutdown in progress"; case WSASYSNOTREADY: return "Network system is unavailable"; case WSAVERNOTSUPPORTED: return "Winsock version out of range"; case WSANOTINITIALISED: return "WSAStartup not yet called"; case WSAHOST_NOT_FOUND: return "Host not found"; case WSATRY_AGAIN: return "WSATRY_AGAIN"; case WSANO_RECOVERY: return "WSANO_RECOVERY"; case WSANO_DATA: return "No host data of that type was found"; #endif default: return "Unknown"; break; } return ""; }