winsock2:recvfrom() 函数以错误 10022(无效参数)结束
winsock2: recvfrom() function ends with error 10022 (Invalid argument)
我正在使用 C++ 开发一个 UDP 套接字,它既是客户端又是服务器。我在 Visual Studio 2017 和 运行 上使用 Windows 7。我在使用 recvfrom() 函数时遇到了一些问题,它以 "invalid argument" 错误结尾。我认为套接字没问题,sockaddr_in 结构没问题,线程没问题,甚至带有 sendto() 函数的 while 循环也没问题。但是线程在调用一个recvfrom()函数后结束,我真的不知道问题出在哪里...
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <thread>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
void printMessage(SOCKET s, SOCKADDR_IN destAddr) {
char message[100];
int result, error;
int addrSize = sizeof(destAddr);
do {
printf("som v threade\n");
result = recvfrom(s, message, 100, 0, (SOCKADDR*)&destAddr, &addrSize);
printf("%d\n", result);
if (result < 0) {
error = WSAGetLastError();
printf("%d\n", error);
}
if (result > 0)
printf("%s", message);
} while (result > 0);
}
int main(int argc, char *argv[]) {
WORD dllVersion = MAKEWORD(2, 1);
WSADATA wsaData;
if (WSAStartup(dllVersion, &wsaData) != 0) {
printf("Winsock startup failed");
exit(1);
}
const char *ip = "127.0.0.1";
int dstPort = 49999;
int srcPort = 49999;
SOCKADDR_IN destaddr, srcaddr;
SOCKET s;
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0) {
printf("Error creating socket");
exit(1);
}
destaddr.sin_family = AF_INET;
destaddr.sin_addr.s_addr = inet_addr(ip);
destaddr.sin_port = htons(dstPort);
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srcaddr.sin_port = htons(srcPort);
bind(s, (SOCKADDR*)&srcaddr, sizeof(srcaddr));
thread receiving (printMessage, s, destaddr);
receiving.detach();
while (1) {
char message[100];
printf("Write a message:");
scanf("%s", message);
if (sendto(s, message, sizeof(message), 0, (SOCKADDR *) &destaddr, sizeof(destaddr)) < 0) {
printf("Message not sent");
exit(1);
}
}
return 0;
}
错误代码 10022 是 WSAEINVAL
。根据 recvfrom()
文档:
WSAEINVAL
The socket has not been bound with bind()
, or an unknown flag was specified, or MSG_OOB
was specified for a socket with SO_OOBINLINE
enabled, or (for byte stream-style sockets only) len
was zero or negative.
您没有进行任何错误处理来确保您的 bind()
调用没有失败。或者甚至您正在调用正确的 bind()
函数开始。 WinSock 和 STL 都有自己的 bind()
函数,但是你的 use of using namespace std
masks that fact。
此外,您每次调用recvfrom()
时都需要重新设置addrSize
,因为它可能会修改值。它希望您传入最大缓冲区大小,然后输出实际写入缓冲区的大小。
此外,您没有在正确的时间调用 WSAGetLastError()
。您需要在 WinSock 调用失败后立即调用它,然后再进行任何操作。换句话说,不要在调用 WSAGetLastError()
之前调用 printf()
,因为它可能会重置错误代码。
此外,如果 recvfrom()
读取成功,则您假设数据以 null 终止,但这并不能保证。您需要将 result
作为参数传递给 printf()
.
而且,您真的不应该将 C 风格 I/O 与 C++ 混合使用。请改用 C++ 样式 I/O。
试试像这样的东西:
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
void printMessage(SOCKET s)
{
char message[100];
int result, error, addrSize;
SOCKADDR_IN clientaddr;
do {
std::cout << "som v threade" << std::endl;
addrSize = sizeof(clientaddr);
result = recvfrom(s, message, 100, 0, (SOCKADDR*)&clientaddr, &addrSize);
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Receive failed. Error: " << error << std::endl;
continue;
}
std::cout << "Received " << result << " byte(s) from " << inet_ntoa(clientaddr.sin_addr) << ":" << ntohs(clientaddr.sin_port) << std::endl;
if (result > 0) {
std::cout.write(message, result);
std::cout << std::endl;
}
}
while (result > 0);
}
int main(int argc, char *argv[]) {
WORD dllVersion = MAKEWORD(2, 1);
WSADATA wsaData;
int result, error;
result = WSAStartup(dllVersion, &wsaData);
if (result != 0) {
std::cout << "Winsock startup failed. Error: " << result << std::endl;
exit(1);
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s == INVALID_SOCKET) {
error = WSAGetLastError();
std::cout << "Socket creation failed. Error: " << error << std::endl;
exit(1);
}
const char *ip = "127.0.0.1";
unsigned short dstPort = 49999;
unsigned short srcPort = 49999;
SOCKADDR_IN destaddr = {};
SOCKADDR_IN srcaddr = {};
destaddr.sin_family = AF_INET;
destaddr.sin_addr.s_addr = inet_addr(ip);
destaddr.sin_port = htons(dstPort);
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = INADDR_ANY;
srcaddr.sin_port = htons(srcPort);
result = ::bind(s, (SOCKADDR*)&srcaddr, sizeof(srcaddr));
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Socket bind failed. Error: " << error << std::endl;
exit(1);
}
std::thread receiving(printMessage, s);
receiving.detach();
std::string message;
do {
std::cout << "Write a message: ";
std::getline(std::cin, message);
result = sendto(s, message.c_str(), message.size(), 0, (SOCKADDR *) &destaddr, sizeof(destaddr));
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Message not sent. Error: " << error << std::endl;
exit(1);
}
}
while (true);
return 0;
}
在包含像
这样的文件后我遇到了同样的问题
#include <thread> /*or*/ #include <future>
原来这是 bind()
和 std::bind()
之间的混淆。
删除 using namespace std;
或简单地使用 ::bind()
.
我正在使用 C++ 开发一个 UDP 套接字,它既是客户端又是服务器。我在 Visual Studio 2017 和 运行 上使用 Windows 7。我在使用 recvfrom() 函数时遇到了一些问题,它以 "invalid argument" 错误结尾。我认为套接字没问题,sockaddr_in 结构没问题,线程没问题,甚至带有 sendto() 函数的 while 循环也没问题。但是线程在调用一个recvfrom()函数后结束,我真的不知道问题出在哪里...
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <thread>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
void printMessage(SOCKET s, SOCKADDR_IN destAddr) {
char message[100];
int result, error;
int addrSize = sizeof(destAddr);
do {
printf("som v threade\n");
result = recvfrom(s, message, 100, 0, (SOCKADDR*)&destAddr, &addrSize);
printf("%d\n", result);
if (result < 0) {
error = WSAGetLastError();
printf("%d\n", error);
}
if (result > 0)
printf("%s", message);
} while (result > 0);
}
int main(int argc, char *argv[]) {
WORD dllVersion = MAKEWORD(2, 1);
WSADATA wsaData;
if (WSAStartup(dllVersion, &wsaData) != 0) {
printf("Winsock startup failed");
exit(1);
}
const char *ip = "127.0.0.1";
int dstPort = 49999;
int srcPort = 49999;
SOCKADDR_IN destaddr, srcaddr;
SOCKET s;
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0) {
printf("Error creating socket");
exit(1);
}
destaddr.sin_family = AF_INET;
destaddr.sin_addr.s_addr = inet_addr(ip);
destaddr.sin_port = htons(dstPort);
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srcaddr.sin_port = htons(srcPort);
bind(s, (SOCKADDR*)&srcaddr, sizeof(srcaddr));
thread receiving (printMessage, s, destaddr);
receiving.detach();
while (1) {
char message[100];
printf("Write a message:");
scanf("%s", message);
if (sendto(s, message, sizeof(message), 0, (SOCKADDR *) &destaddr, sizeof(destaddr)) < 0) {
printf("Message not sent");
exit(1);
}
}
return 0;
}
错误代码 10022 是 WSAEINVAL
。根据 recvfrom()
文档:
WSAEINVAL
The socket has not been bound with
bind()
, or an unknown flag was specified, orMSG_OOB
was specified for a socket withSO_OOBINLINE
enabled, or (for byte stream-style sockets only)len
was zero or negative.
您没有进行任何错误处理来确保您的 bind()
调用没有失败。或者甚至您正在调用正确的 bind()
函数开始。 WinSock 和 STL 都有自己的 bind()
函数,但是你的 use of using namespace std
masks that fact。
此外,您每次调用recvfrom()
时都需要重新设置addrSize
,因为它可能会修改值。它希望您传入最大缓冲区大小,然后输出实际写入缓冲区的大小。
此外,您没有在正确的时间调用 WSAGetLastError()
。您需要在 WinSock 调用失败后立即调用它,然后再进行任何操作。换句话说,不要在调用 WSAGetLastError()
之前调用 printf()
,因为它可能会重置错误代码。
此外,如果 recvfrom()
读取成功,则您假设数据以 null 终止,但这并不能保证。您需要将 result
作为参数传递给 printf()
.
而且,您真的不应该将 C 风格 I/O 与 C++ 混合使用。请改用 C++ 样式 I/O。
试试像这样的东西:
#include <iostream>
#include <string>
#include <thread>
#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
void printMessage(SOCKET s)
{
char message[100];
int result, error, addrSize;
SOCKADDR_IN clientaddr;
do {
std::cout << "som v threade" << std::endl;
addrSize = sizeof(clientaddr);
result = recvfrom(s, message, 100, 0, (SOCKADDR*)&clientaddr, &addrSize);
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Receive failed. Error: " << error << std::endl;
continue;
}
std::cout << "Received " << result << " byte(s) from " << inet_ntoa(clientaddr.sin_addr) << ":" << ntohs(clientaddr.sin_port) << std::endl;
if (result > 0) {
std::cout.write(message, result);
std::cout << std::endl;
}
}
while (result > 0);
}
int main(int argc, char *argv[]) {
WORD dllVersion = MAKEWORD(2, 1);
WSADATA wsaData;
int result, error;
result = WSAStartup(dllVersion, &wsaData);
if (result != 0) {
std::cout << "Winsock startup failed. Error: " << result << std::endl;
exit(1);
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s == INVALID_SOCKET) {
error = WSAGetLastError();
std::cout << "Socket creation failed. Error: " << error << std::endl;
exit(1);
}
const char *ip = "127.0.0.1";
unsigned short dstPort = 49999;
unsigned short srcPort = 49999;
SOCKADDR_IN destaddr = {};
SOCKADDR_IN srcaddr = {};
destaddr.sin_family = AF_INET;
destaddr.sin_addr.s_addr = inet_addr(ip);
destaddr.sin_port = htons(dstPort);
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = INADDR_ANY;
srcaddr.sin_port = htons(srcPort);
result = ::bind(s, (SOCKADDR*)&srcaddr, sizeof(srcaddr));
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Socket bind failed. Error: " << error << std::endl;
exit(1);
}
std::thread receiving(printMessage, s);
receiving.detach();
std::string message;
do {
std::cout << "Write a message: ";
std::getline(std::cin, message);
result = sendto(s, message.c_str(), message.size(), 0, (SOCKADDR *) &destaddr, sizeof(destaddr));
if (result == SOCKET_ERROR) {
error = WSAGetLastError();
std::cout << "Message not sent. Error: " << error << std::endl;
exit(1);
}
}
while (true);
return 0;
}
在包含像
这样的文件后我遇到了同样的问题#include <thread> /*or*/ #include <future>
原来这是 bind()
和 std::bind()
之间的混淆。
删除 using namespace std;
或简单地使用 ::bind()
.