【问题标题】:C++ UDP recvfrom is acting strange, WSAGetLastError = 10014C++ UDP recvfrom 行为奇怪,WSAGetLastError = 10014
【发布时间】:2013-06-25 19:59:29
【问题描述】:

我的代码在 Windows 上表现得非常奇怪,但在 Linux 上工作...... 这是我的 server.cpp:

#include <cstdio>
#include "packet.h"
#include "socket.h"

int main(int argc, char *argv[])
{
Socket s;

s.bindAt(1337);
for (int i = 0; i < 20; i++) {
    Packet p;
    int32_t a;
    char *b;

    int abc = s.receive();
    printf("abc = %d\n", abc);
    printf("error = %d\n", WSAGetLastError());
    p.getInt(&a);
    p.getString(&b);

    printf("int = %d\nstring = %s\n", a, b);
    delete[] b;
}

return 0;
}

这里是socket.cpp:

Socket::Socket()
{
#ifdef _WIN32
WSADATA wsa;
if (sockNum == 0 && WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    throw 1;
#endif

sock = socket(AF_INET, SOCK_DGRAM, 0);
#ifdef _WIN32
if (sock == INVALID_SOCKET)
#else
if (sock == -1)
#endif
    throw 2;

addrlen = 0;
sockNum++;
}


int Socket::bindAt(unsigned short port)
{
struct sockaddr_in sa = { 0 };
memset(&sa, 0, sizeof(sa));

sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
return bind(sock, (struct sockaddr *) &sa, sizeof(sa));
}

ssize_t Socket::receive()
{
ssize_t n;

#ifdef _WIN32

char msg[100];
n = recvfrom(sock,msg, sizeof(msg), 0,(SOCKADDR*) &addr, &addrlen);
#else
n = recvfrom(sock, p->buf, p->bufSize, 0,
        (struct sockaddr *) &addr, &addrlen);
#endif
/*if (n < 0)
    p->bufSize = 0;
else
    p->bufSize = n;*/
return n;
}

基本上是它的标题:

typedef SOCKET socket_t;
typedef int ssize_t;

class Socket
{
public:
socket_t sock;
socklen_t addrlen;
struct sockaddr_in addr;

Socket();
~Socket();

int connect(const char *ip, unsigned short port);
int bindAt(unsigned short port);
ssize_t send(Packet *p);
ssize_t receive();
};

如果我将 recvfrom、(SOCKADDR*) &addr 和 &addrlen 的最后 2 个参数更改为 NULL,它可以工作,但是这 2 个参数有什么问题?

【问题讨论】:

  • 你试过用 sizeof(addr) 初始化 addrlen 吗?
  • addrlen = sizeof(addr);哇,这行得通!,怎么样???
  • @dtb 查看前面的 MSDN。在SOCK_DGRAM 下显示此套接字类型使用Internet 地址系列(AF_INET 或AF_INET6)的用户数据报协议(UDP)。

标签: c++ udp


【解决方案1】:

而不是这个:

addrlen = 0;

这样做:

addrlen = sizeof(sockaddr_in)

它应该可以工作,因为您正确识别了输出地址缓冲区指针的大小。

【讨论】:

  • recvfrom()的最后一个参数是一个in/out参数,所以它是通过指针传递的。在输入时,它指定缓冲区的总大小。在输出时,它返回实际使用的缓冲区空间量。为该值使用类成员不是一个好主意,除非每次调用 recvfrom() 时都重新初始化该值。只在构造函数中初始化是不够的。
  • @RemyLebeau 无论是类成员还是普通变量,都不需要重新初始化吗?
  • @Barmar:每次调用recvfrom() 时都需要。这就是为什么我说只在构造函数中初始化它是不够的。第一次调用 recvfrom() 时,变量会更新,如果不重新初始化,下次调用 recvfrom() 时可能会有错误的值。
  • @RemyLebeau 我不是在质疑是否有必要每次都重新初始化它,我是在质疑你的说法,即使用类成员不是一个好主意。如果addr 是类成员,addrlen 也应该是,这样你就可以将两者保持在一起。
  • @Barmar:就我个人而言,我会将addr 成员更改为SOCKADDR_STORAGE,根本不用担心跟踪addrlen。然后Socket::Receive() 可以在调用recvfrom() 时为addrlen 使用局部变量。
【解决方案2】:

来自MSDN 描述WSAEFAULT (10014):

系统在尝试使用调用的指针参数时检测到无效的指针地址。如果应用程序传递了无效的指针值,或者缓冲区的长度太小,则会出现此错误。例如,如果参数的长度(即 sockaddr 结构)小于 sizeof(sockaddr)。

所以如果你提供一个未初始化的值addrlen,它可能太小了,导致这个错误。

如果addrNULL,表示你不想填写地址,所以addrlen 被忽略。

【讨论】:

  • 感谢您提供有用的信息顺便说一句(有点晚了:P)
猜你喜欢
  • 2014-12-12
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 1970-01-01
  • 2017-02-03
  • 1970-01-01
相关资源
最近更新 更多