Winsock経由でUDPソケット(_AF_INET
_、_SOCK_DGRAM
_、_IPPROTO_UDP
_)を作成し、このソケットでrecvfrom
を試行していますが、常に-1を返し、WSAEINVALを取得します(10022)。どうして?
ポートをbind()
すると、それは起こりませんが、クライアントのソケットをバインドするのは非常に不自由だと読んでいます。
サーバーにデータを送信していますが、サーバーが応答するか、少なくとも試行します。
_Inc::STATS CConnection::_RecvData(sockaddr* addr, std::string &strData)
{
int ret; // return code
int len; // length of the data
int fromlen; // sizeof(sockaddr)
char *buffer; // will hold the data
char c;
//recv length of the message
fromlen = sizeof(sockaddr);
ret = recvfrom(m_InSock, &c, 1, 0, addr, &fromlen);
if(ret != 1)
{
#ifdef __MYDEBUG__
std::stringstream ss;
ss << WSAGetLastError();
MessageBox(NULL, ss.str().c_str(), "", MB_ICONERROR | MB_OK);
#endif
return Inc::ERECV;
}
...
_
これは少し前に書いた実際の例であり、クライアントでbind()
を呼び出さなくても機能します。
_#pragma comment(lib, "Ws2_32.lib")
#define WIN32_LEAN_AND_MEAN
#include <WS2tcpip.h>
#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
SOCKET sock;
addrinfo* pAddr;
addrinfo hints;
sockaddr sAddr;
int fromlen;
const char czPort[] = "12345";
const char czAddy[] = "some ip";
WSADATA wsa;
unsigned short usWSAVersion = MAKEWORD(2,2);
char Buffer[22] = "TESTTESTTESTTESTTEST5";
int ret;
//Start WSA
WSAStartup(usWSAVersion, &wsa);
//Create Socket
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//Resolve Host address
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_socktype = SOCK_DGRAM;
if(getaddrinfo(czAddy, czPort, &hints, &pAddr))
{
std::cerr << "Could not resolve address...\n";
std::cin.get();
return 1;
}
//Start Transmission
while(1)
{
ret = sendto(sock, Buffer, sizeof(Buffer), 0, pAddr->ai_addr,
pAddr->ai_addrlen);
if(ret != sizeof(Buffer))
{
std::cerr << "Could not send data\n";
std::cin.get();
return 1;
}
fromlen = sizeof(SOCKADDR);
ret = recvfrom(sock, Buffer, sizeof(Buffer), 0, &sAddr, &fromlen);
if(ret != sizeof(Buffer))
{
std::cout << "Could not receive data - error: " <<
WSAGetLastError() << std::endl;
std::cin.get();
return 1;
}
Buffer[ret-1] = '\0';
std::cout << "Received: " << Buffer << std::endl;
}
return 0;
}
_
UDPでは、bind()
クライアントのソケットを使用する必要があります。これは、UDPがコネクションレスであるため、特定のポートにデータグラムを配信するプログラムをスタックが知る他の方法がないためです。
recvfrom()
なしでbind()
できる場合、本質的には、スタックに、そのコンピューターに送信されたすべてのUDPデータグラムをプログラムに提供するように依頼することになります。スタックはデータグラムを1つのプログラムのみに配信するため、これによりDNS、Windowsのネットワークコンピュータ、ネットワーク時刻の同期が壊れます。
ネット上のどこかでクライアントのバインディングが不十分であると読んでいたかもしれませんが、そのアドバイスはTCP接続にのみ適用されます。
sendto
の前にrecvfrom
を使用しているため、他のコードサンプルが機能します。 UDPソケットがバインドされておらず、sendto
またはconnect
が呼び出された場合、システムは自動的にバインドします。したがって、後のrecvfrom
呼び出しは成功します。 recvfrom
はソケットをバインドしません。これは、この呼び出しがソケットが既にバインドされていることを予期しているためです。そうでない場合、エラーがスローされます。
数週間前に同じ問題が発生しましたが、明示的なbind()呼び出しが必要かどうかを理解するのに次のコメントが役立ちました。
クライアントアプリケーションでは、明示的なバインドはお勧めしません。この関数を使用するクライアントアプリケーションの場合、ソケットはsendto、WSASendTo、またはWSAJoinLeaf。
注ソケットが開かれると、setsockopt呼び出しが行われ、次にsendto呼び出しが行われると、Windows Socketsは暗黙的なbind関数呼び出しを実行します。ソケットがバインドされていない場合、一意の値がシステムによってローカル関連付けに割り当てられ、ソケットはバインド済みとしてマークされます。
ここ それは次のことを言っています:
パラメーター
s [in]:バインドされたソケットを識別する記述子。
...
戻り値
WSAEINVAL:ソケットがバインドでバインドされていないか、不明フラグが指定されているか、SO_OOBINLINEが有効になっているソケットにMSG_OOBが指定されているか、(バイトストリームスタイルのソケットのみ)lenがゼロまたは負でした。
覚えている限り、バインド呼び出しはスタックによって行われるため、UDPソケットにはバインドは必要ありません。 recvfrom呼び出しで使用されるソケットにバインドを要求するのはWindowsのことだと思います。