【问题标题】:winsock2: recvfrom() function ends with error 10022 (Invalid argument)winsock2:recvfrom() 函数以错误 10022 结束(无效参数)
【发布时间】:2017-11-20 01:18:08
【问题描述】:

我正在使用 C++ 开发一个 UDP 套接字,它既是客户端又是服务器。我正在使用 Visual Studio 2017 并在 Windows 7 上运行它。recvfrom() 函数有一些问题,以“无效参数”错误结尾。我认为套接字没问题,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;
}

【问题讨论】:

  • 你没有在检查错误bind()。您应该在调用printf() 或任何其他系统调用之前调用WSAGetLastError()之前,并且每次循环时都应该重新初始化addrSize
  • 并且循环直到接收计数为零是徒劳的,除非您知道 (a) 对等方将发送一个长度为零的数据包以终止发送并且 (b) 它没有被重新排序在你不知道的数据报之前。

标签: c++ sockets udp visual-studio-2017 winsock2


【解决方案1】:

错误代码 10022 是 WSAEINVAL。根据recvfrom() 文档:

WSAEINVAL

套接字未绑定bind(),或指定了未知标志,或为启用了SO_OOBINLINE 的套接字指定了MSG_OOB,或(对于字节流样式仅限套接字)len 为零或负数。

您没有进行任何错误处理以确保您的bind() 调用没有失败。或者甚至是您开始调用正确的bind() 函数。 WinSock 和 STL 都有自己的bind() 函数,但你的use of using namespace std masks that fact

另外,每次调用recvfrom() 时都需要重置addrSize,因为它可能会修改值。它希望您传入最大缓冲区大小,然后输出实际写入缓冲区的大小。

另外,您没有在正确的时间致电WSAGetLastError()。您需要在 WinSock 调用失败后立即调用它,然后再执行任何操作。换句话说,在调用WSAGetLastError() 之前不要调用printf(),因为它可能会重置错误代码。

此外,如果recvfrom() 读取成功,则您假设数据为空终止,但这不能保证。您需要将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;
}

【讨论】:

    【解决方案2】:

    在包含类似文件后,我遇到了同样的问题

    #include <thread> /*or*/ #include <future>
    

    原来是bind()std::bind() 之间的混淆。

    删除using namespace std; 或直接使用::bind()

    【讨论】:

      猜你喜欢
      • 2020-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-30
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      相关资源
      最近更新 更多