winsock2 を使用してサーバー/クライアント システムをプログラミングしていますが、クライアントをサーバー名またはサーバー IPv6 アドレスに接続するとうまく機能します。ただし、サーバーの IPv4 アドレスを使用すると、クライアントの connect() への呼び出しから「接続が拒否されました」というエラーが表示されます。
このエラーは、クライアントまたは telnet を使用している場合に発生します。ただし、IPv4 または IPv6 の 3 つの名前のいずれかを使用して、サーバーに正常に ping を実行できます。
サーバーとクライアントの両方を同じマシン上、別のマシン上で実行し、すべてのマシンでファイアウォールを非アクティブ化して、これを試しました。
これは、サーバーの初期化とリスニング コードの抜粋です。
SOCKET sockfd = INVALID_SOCKET, in_socketID;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
sockaddr_storage incoming_addr;
int addr_size;
int tmp_err;
const char *sPort = "20152";
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
tmp_err = getaddrinfo(NULL, sPort, &hints, &servinfo);
if (tmp_err != 0)
throw exception("ERROR: getaddrinfo failed");
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
ip = p;
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == INVALID_SOCKET)
{
cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
} // end if
else if (bind(sockfd, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
{
cerr << "ERROR on bind(): " << WSAGetLastError() << endl;
closesocket(sockfd);
sockfd = INVALID_SOCKET;
} // end if
} // end for
if (sockfd == INVALID_SOCKET)
{
// looped off the end of the list with no successful bind
throw exception("ERROR: Failed to bind socket");
}
// clean up
if (servinfo)
freeaddrinfo(servinfo);
if (listen(sockfd, SOMAXCONN ) == SOCKET_ERROR)
throw exception("Listen failed");
while (true)
{
memset(&incoming_addr, 0, sizeof(incoming_addr));
addr_size = sizeof(incoming_addr);
in_socketID = accept(socketID, (sockaddr *)&incoming_addr, &addr_size);
// do stuff with incoming connection
}
これは私のクライアントコードです:
int sockfd = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
int tmp_err;
const char *sHost = "192.168.1.136";
const char *sPort = "20152";
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP
tmp_err = getaddrinfo(sHost, // web address or ip to connect to
sPort, // port or protocol
&hints, // initialized hints structure
&servinfo); // return structure
if (tmp_err != 0)
throw exception("ERROR: getaddrinfo failed");
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
ip = p;
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == INVALID_SOCKET)
{
cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
//continue;
} // end if
else if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0)
{
cerr << "ERROR on connect(): " << WSAGetLastError() << endl;
closesocket(sockfd);
sockfd = INVALID_SOCKET;
//continue;
} // end if
} // end for
if (sockfd == INVALID_SOCKET)
throw exception("ERROR: Failed to connect");
// clean up
if (servinfo)
freeaddrinfo(servinfo);
// do stuff with new socket
サイトで同様の質問をいくつか読みましたが、この問題に答えたものはありません。
サーバーの IPv4 アドレスにも接続するにはどうすればよいですか? 助けが必要です、お願いします。
ありがとう。
編集:
ユーザー Sorayuki からの提案から、彼の理論が正しいかどうかをテストするためだけにいくつかの変更を加えました。
サーバーの変更でIPv4に接続できました
hints.ai_family = AF_UNSPEC;
に
hints.ai_family = AF_INET;
もちろんうまくいくことはわかっていましたが、これをやるともちろんIPv6はうまくいきません。
ユーザーSorayukiが正しく、私のループはIPv6に接続していたようです。
IPv6 と IPv4 を統合する簡単な方法はないようです。ソケットはどちらかをリッスンする必要があり、プロセスが本当に面倒になります。
ドキュメントによると、IPv4 と IPv6 の両方をリッスンする古いスタイルは、それぞれにソケットを作成し、両方をリッスンすることです。これは、Windows Server 2003 および Windows XP SP1 用です。
推奨される最新のスタイル (Windows Vista、7、および 8) は、ソケットをデュアル ソケットに変えることであり、IPv4 と IPv6 の両方をリッスンします。ただし、クライアントもデュアル ソケットをセットアップできる必要があるため、アプリケーションが古いクライアントにサービスを提供している場合、古い方法にとらわれてしまいます。
ありがとう!