Winsock:UDP recvfrom() 未填写用于发送数据的同一 IP 地址
Winsock: UDP recvfrom() not filling in the same IP address that was used to send data
我正在编写一个 UDP 回显客户端和服务器。这些都是 运行 在我的机器上。客户端设置为将消息发送到 IP 地址 ::1。此时我的服务器收到消息并应该打印类似的内容。
Processing client at ::1
相反,服务器不断获取不同的 IP 地址。
Processing client at 132:e3d5::
注意:每次收到的IP地址都不一样
这导致了一个问题,因为我相信当我稍后尝试将消息回显给我的客户端时,我无法将它发送到正确的位置,因为我收到的 IP 地址与 ::1 不同.
这是一些代码:
Client.c
void main(int argc, char* argv[]) // argc is # of strings following command, argv[] is array of ptrs to the strings
{
WSADATA wsaData; // contains details about WinSock DLL implementation
struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
char* serverIPaddr, * phrase, echoBuffer[RCVBUFSIZ];
int serverPort, msgLen, fromSize, echoLen;
// Verify correct number of command line arguments
if (argc != 4) {
printf("Invalid amount of arguments entered. \nPlease make sure to only enter the following: \n\n <application name> <ip address> <port number> <message>");
exit(1);
}
// Retrieve the command line arguments.
// to be converted from char to int.
serverIPaddr = argv[1];
serverPort = atoi(argv[2]);
phrase = argv[3];
msgLen = strlen(phrase) + 1; // We are sending the null terminator
// Initialize Winsock 2.0 DLL.
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
// Failed
printf("Couldn't initialize Winsock 2.0 DLL");
exit(1);
}
// Create an IPv6 UPD stream socket. Now that Winsock DLL is loaded, we can signal any errors as shown on next line:
int sock;
sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
DisplayFatalErr("socket() function failed.");
exit(1);
}
printf("Socket created successfully. Press enter to continue...");
getchar();
memset(&serverInfo, 0, sizeof(serverInfo));
serverInfo.sin6_family = AF_INET6;
serverInfo.sin6_port = htons(serverPort);
inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);
if (msgLen == 0) {
DisplayFatalErr("Message was empty, please make sure to include a message.");
exit(1);
}
// Send data to server
if (sendto(sock, phrase, msgLen, 0, (struct sockaddr*) & serverInfo, sizeof(serverInfo)) != msgLen) {
DisplayFatalErr("sendto() function failed.");
exit(1);
}
fromSize = sizeof(serverInfo);
if ((echoLen = recvfrom(sock, echoBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & serverInfo.sin6_addr, &fromSize)) != msgLen) {
// We lost some data check for error first
if (echoLen < 0) {
DisplayFatalErr("recvfrom() function failed.");
exit(1);
}
}
// Output message (Will create soon)
printf("");
printf("\nData was received from the server. Press enter to continue...");
getchar();
if (closesocket(sock) != 0) {
DisplayFatalErr("closesocket() function failed.");
}
printf("socket closed successfully. Press enter to continue...");
getchar();
if (WSACleanup() != 0) {
DisplayFatalErr("WSACleanup() function failed.");
}
exit(0);
}
Server.c
void main(int argc, char* argv[]) // argc is # of strings following command, argv[] is array of ptrs to the strings
{
WSADATA wsaData; // contains details about WinSock DLL implementation
struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
int serverPort, clientSock, rcvLen, fromSize;
char* rcvBuffer[RCVBUFSIZ];
serverPort = 0;
// Verify correct number of command line arguments
if (argc != 2) {
serverPort = DEFAULT_PORT;
}
else {
serverPort = atoi(argv[1]);
}
// Initialize Winsock 2.0 DLL
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
// Failed
printf("Couldn't initialize Winsock 2.0 DLL");
exit(1);
}
// Create an IPv6 UDP stream socket.
int sock;
sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
DisplayFatalErr("socket() function failed.");
exit(1);
}
// Don't forget any necessary format conversions.
memset(&serverInfo, 0, sizeof(serverInfo));
serverInfo.sin6_family = AF_INET6;
serverInfo.sin6_port = htons(serverPort);
serverInfo.sin6_addr = in6addr_any;
//inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);
// Bind the server socket to the sockadder structure.
if (bind(sock, (struct sockadder*) & serverInfo, sizeof(serverInfo)) == SOCKET_ERROR) {
DisplayFatalErr("bind() function failed.");
exit(1);
}
printf("ST's IPv6 echo server is ready for client connection on port: %i\r\n", serverPort);
// Forever Loop waiting for messages
for (;;) {
struct sockaddr_in6 clientInfo; // Hold client port & adder recvfrom
memset(&clientInfo, 0, sizeof(clientInfo));
clientInfo.sin6_family = AF_INET6;
fromSize = sizeof(clientInfo);
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) { //THIS IS WHERE I BELIEVE THE ERROR TO BE. WHEN LOOKING AT THE ADDRESS ON THE DEBUGGER IT DOESN'T MATCH THE ONE I SENT USING THE CLIENT
DisplayFatalErr("recvfrom() function failed.");
}
// Processing Socket
char* clientAddr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);
int clientPort = ntohs(clientInfo.sin6_port);
printf("Processing the client at %s, client port %i, server port %i.\r\n", clientAddr, clientPort, serverPort);
printf("Message Received: %s\r\n", rcvBuffer);
// Send data back
if (sendto(sock, rcvBuffer, rcvLen, 0, (struct sockaddr*) & clientInfo, sizeof(clientInfo)) != rcvLen) {
DisplayFatalErr("sendto() function failed.");
}
}
}
如果您需要我提供任何其他信息,请告诉我。
你说的对,问题出在这里:
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) {
// ^^^^^^^^^^
你没有超过 sockaddr *
,你超过了 struct in6_addr *
。只需删除 .sin6_addr
部分:
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo, &fromSize)) < 0) {
当服务器调用 recvfrom()
时,您在 from
参数中向其传递 in6_addr*
而不是 sockaddr_in6*
。您需要传入指向整个 sockaddr_in6
的指针,而不是仅指向其 sin6_addr
字段的指针:
recvfrom(..., (struct sockaddr*) &clientInfo, ...) // <-- NOT &clientInfo.sin6_addr!
服务器也有其他问题。
服务器收到消息后,会调用inet_ntop()
将客户端的源IP地址转换为字符串。但是,它传入一个 char*[]
数组,但它需要传入一个 char[]
数组:
char clientAddr[INET6_ADDRSTRLEN]; // <-- char, NOT char*!
inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);
此外,当打印出收到的消息时,您将其视为以 null 结尾的字符串。在这个例子中这很好,因为您的客户端正在发送一个空终止符,但是对于您的服务器来说,这通常不是一个好主意。这是恶意客户端引起缓冲区溢出攻击的好方法。您应该改用 recvfrom()
returns:
的实际字节大小
printf("Message Received: %.*s\r\n", rcvLen, rcvBuffer);
并且在调用 sendto()
将消息回显给客户端时,您应该使用 recvfrom()
报告的实际 fromSize
而不是使用 sizeof(clientInfo)
作为客户端的地址大小:
sendto(..., (struct sockaddr*) &clientInfo, fromSize)
我正在编写一个 UDP 回显客户端和服务器。这些都是 运行 在我的机器上。客户端设置为将消息发送到 IP 地址 ::1。此时我的服务器收到消息并应该打印类似的内容。
Processing client at ::1
相反,服务器不断获取不同的 IP 地址。
Processing client at 132:e3d5::
注意:每次收到的IP地址都不一样
这导致了一个问题,因为我相信当我稍后尝试将消息回显给我的客户端时,我无法将它发送到正确的位置,因为我收到的 IP 地址与 ::1 不同.
这是一些代码:
Client.c
void main(int argc, char* argv[]) // argc is # of strings following command, argv[] is array of ptrs to the strings
{
WSADATA wsaData; // contains details about WinSock DLL implementation
struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
char* serverIPaddr, * phrase, echoBuffer[RCVBUFSIZ];
int serverPort, msgLen, fromSize, echoLen;
// Verify correct number of command line arguments
if (argc != 4) {
printf("Invalid amount of arguments entered. \nPlease make sure to only enter the following: \n\n <application name> <ip address> <port number> <message>");
exit(1);
}
// Retrieve the command line arguments.
// to be converted from char to int.
serverIPaddr = argv[1];
serverPort = atoi(argv[2]);
phrase = argv[3];
msgLen = strlen(phrase) + 1; // We are sending the null terminator
// Initialize Winsock 2.0 DLL.
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
// Failed
printf("Couldn't initialize Winsock 2.0 DLL");
exit(1);
}
// Create an IPv6 UPD stream socket. Now that Winsock DLL is loaded, we can signal any errors as shown on next line:
int sock;
sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
DisplayFatalErr("socket() function failed.");
exit(1);
}
printf("Socket created successfully. Press enter to continue...");
getchar();
memset(&serverInfo, 0, sizeof(serverInfo));
serverInfo.sin6_family = AF_INET6;
serverInfo.sin6_port = htons(serverPort);
inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);
if (msgLen == 0) {
DisplayFatalErr("Message was empty, please make sure to include a message.");
exit(1);
}
// Send data to server
if (sendto(sock, phrase, msgLen, 0, (struct sockaddr*) & serverInfo, sizeof(serverInfo)) != msgLen) {
DisplayFatalErr("sendto() function failed.");
exit(1);
}
fromSize = sizeof(serverInfo);
if ((echoLen = recvfrom(sock, echoBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & serverInfo.sin6_addr, &fromSize)) != msgLen) {
// We lost some data check for error first
if (echoLen < 0) {
DisplayFatalErr("recvfrom() function failed.");
exit(1);
}
}
// Output message (Will create soon)
printf("");
printf("\nData was received from the server. Press enter to continue...");
getchar();
if (closesocket(sock) != 0) {
DisplayFatalErr("closesocket() function failed.");
}
printf("socket closed successfully. Press enter to continue...");
getchar();
if (WSACleanup() != 0) {
DisplayFatalErr("WSACleanup() function failed.");
}
exit(0);
}
Server.c
void main(int argc, char* argv[]) // argc is # of strings following command, argv[] is array of ptrs to the strings
{
WSADATA wsaData; // contains details about WinSock DLL implementation
struct sockaddr_in6 serverInfo; // standard IPv6 structure that holds server socket info
int serverPort, clientSock, rcvLen, fromSize;
char* rcvBuffer[RCVBUFSIZ];
serverPort = 0;
// Verify correct number of command line arguments
if (argc != 2) {
serverPort = DEFAULT_PORT;
}
else {
serverPort = atoi(argv[1]);
}
// Initialize Winsock 2.0 DLL
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
// Failed
printf("Couldn't initialize Winsock 2.0 DLL");
exit(1);
}
// Create an IPv6 UDP stream socket.
int sock;
sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
DisplayFatalErr("socket() function failed.");
exit(1);
}
// Don't forget any necessary format conversions.
memset(&serverInfo, 0, sizeof(serverInfo));
serverInfo.sin6_family = AF_INET6;
serverInfo.sin6_port = htons(serverPort);
serverInfo.sin6_addr = in6addr_any;
//inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);
// Bind the server socket to the sockadder structure.
if (bind(sock, (struct sockadder*) & serverInfo, sizeof(serverInfo)) == SOCKET_ERROR) {
DisplayFatalErr("bind() function failed.");
exit(1);
}
printf("ST's IPv6 echo server is ready for client connection on port: %i\r\n", serverPort);
// Forever Loop waiting for messages
for (;;) {
struct sockaddr_in6 clientInfo; // Hold client port & adder recvfrom
memset(&clientInfo, 0, sizeof(clientInfo));
clientInfo.sin6_family = AF_INET6;
fromSize = sizeof(clientInfo);
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) { //THIS IS WHERE I BELIEVE THE ERROR TO BE. WHEN LOOKING AT THE ADDRESS ON THE DEBUGGER IT DOESN'T MATCH THE ONE I SENT USING THE CLIENT
DisplayFatalErr("recvfrom() function failed.");
}
// Processing Socket
char* clientAddr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);
int clientPort = ntohs(clientInfo.sin6_port);
printf("Processing the client at %s, client port %i, server port %i.\r\n", clientAddr, clientPort, serverPort);
printf("Message Received: %s\r\n", rcvBuffer);
// Send data back
if (sendto(sock, rcvBuffer, rcvLen, 0, (struct sockaddr*) & clientInfo, sizeof(clientInfo)) != rcvLen) {
DisplayFatalErr("sendto() function failed.");
}
}
}
如果您需要我提供任何其他信息,请告诉我。
你说的对,问题出在这里:
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo.sin6_addr, &fromSize)) < 0) {
// ^^^^^^^^^^
你没有超过 sockaddr *
,你超过了 struct in6_addr *
。只需删除 .sin6_addr
部分:
if ((rcvLen = recvfrom(sock, rcvBuffer, RCVBUFSIZ, 0, (struct sockaddr*) & clientInfo, &fromSize)) < 0) {
当服务器调用 recvfrom()
时,您在 from
参数中向其传递 in6_addr*
而不是 sockaddr_in6*
。您需要传入指向整个 sockaddr_in6
的指针,而不是仅指向其 sin6_addr
字段的指针:
recvfrom(..., (struct sockaddr*) &clientInfo, ...) // <-- NOT &clientInfo.sin6_addr!
服务器也有其他问题。
服务器收到消息后,会调用inet_ntop()
将客户端的源IP地址转换为字符串。但是,它传入一个 char*[]
数组,但它需要传入一个 char[]
数组:
char clientAddr[INET6_ADDRSTRLEN]; // <-- char, NOT char*!
inet_ntop(AF_INET6, &(clientInfo.sin6_addr), clientAddr, INET6_ADDRSTRLEN);
此外,当打印出收到的消息时,您将其视为以 null 结尾的字符串。在这个例子中这很好,因为您的客户端正在发送一个空终止符,但是对于您的服务器来说,这通常不是一个好主意。这是恶意客户端引起缓冲区溢出攻击的好方法。您应该改用 recvfrom()
returns:
printf("Message Received: %.*s\r\n", rcvLen, rcvBuffer);
并且在调用 sendto()
将消息回显给客户端时,您应该使用 recvfrom()
报告的实际 fromSize
而不是使用 sizeof(clientInfo)
作为客户端的地址大小:
sendto(..., (struct sockaddr*) &clientInfo, fromSize)