【问题标题】:D receiveFrom not recieving full packetD receiveFrom 没有收到完整的数据包
【发布时间】:2016-04-30 20:16:05
【问题描述】:

我目前正在使用 D 为游戏编写一个小型 UDP 服务器。问题是收到的一些数据包与实际数据包的长度不匹配(通过wireshark查看)。

例如,客户端通过网络发送一个 110 字节的数据包,该数据包显示在 Wireshark 下。但是 D 码只接收 7 个字节!没有其他 7 字节数据包从客户端通过网络发送。

D 中的 7 个字节与 110 字节数据包中的前 7 个字节匹配。我认为这是套接字库的问题,因为我想不出其他任何可能导致此问题的东西。

问题总是发生在同一点和完全相同的数据包。如果忽略此问题,某些数据包仍会出现问题。

请注意,该项目中有多个文件,因此我在下面包含了代码片段:

this(in Logger logger, string bindInterface = "0.0.0.0", ushort bindPort = 19132) {
    this.logger = logger;
    socket = new UdpSocket(AddressFamily.INET);
    bindAddress = new InternetAddress(bindInterface, bindPort);
}

void bind(uint sendBufferSize = 1024 * 1024, uint recvBufferSize = 1024 * 1024) {
    socket.bind(bindAddress);

    socket.setOption(SocketOptionLevel.SOCKET, SocketOption.BROADCAST, true);
    socket.setOption(SocketOptionLevel.SOCKET, SocketOption.SNDBUF, sendBufferSize);
    socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVBUF, recvBufferSize);
    socket.blocking = false;
}

bool recv(ref Address address, ref byte[] buffer) {
    auto length = socket.receiveFrom(buffer, SocketFlags.NONE, address);
    if(length > 0) {
        buffer.length = length;
        debug logger.logDebug(to!string(length) ~ " Packet IN: " ~ to!string(cast(ubyte[]) buffer));
        return true;
    }
    buffer = null;
    return false;
}

...

Address a;
    byte[] data = new byte[1024 * 1024];
    while(max-- > 0 && socket.recv(a, data)) {
        handlePacket(a, data);
    }

完整源码可以在here.找到

任何帮助将不胜感激。

客户是

【问题讨论】:

  • 您能看到正在发送的整条消息的确切内容吗?不是您认为的那样,而是实际发送的内容是什么?
  • 不太可能。 UDP 传递完整的消息或什么都不传递。这是 UDP 做出的唯一保证。因此,如果您说的是真的,那么您的网络基础设施、操作系统或代码编译器中的某些东西已经严重损坏。
  • @zipzit 如果您的意思是通过wireshark,是的,整个内容都会显示出来。有问题的数据是 RakNet 协议的二进制握手包的一部分。
  • @jgauffin 我以前在这台计算机上使用其他语言从未遇到过这个问题,这就是为什么我认为这是一个 D 问题。
  • @jython234 我的错。我不喜欢 D,但我想知道的是,如果您在 SENDER 程序中执行相当于 log.console(value_about_to_be_sent); 的操作,您会看到什么?这与您认为您发送的内容完全一致吗?我的猜测是您错误地发送了未知(控制)字符? (但这只是一个疯狂的猜测?)

标签: sockets networking udp d


【解决方案1】:

问题出在这一行:

bool recv(ref Address address, ref byte[] buffer)

使用ref,您不会进行切片,但您确实修改了原始缓冲区,因此如果前一个数据包长 7 个字节,您将无法再接收超过 7 个字节

因此您可以更改代码以删除 ref 并返回长度而不是纯 bool

size_t recv(ref Address address, byte[] buffer);

然后:

Address a;
byte[] data = new byte[1024 * 1024];
size_t len;
while(max-- > 0 && (len = socket.recv(a, data))) {
    handlePacket(a, data[0 .. len]);
}

但这可能不适用于您的 handlePacket 函数,因为另一个 ref 所以另一个变体是不要更改您的 recv 但只有部分代码围绕 handlePacket

Address a;
byte[] data = new byte[1024 * 1024];
size_t arr = data[];
while(max-- > 0 && socket.recv(a, arr)) {
    handlePacket(a, arr);
    arr = data[];
}

【讨论】:

  • 他不能简单地删除 ref,因为他需要为 handlePacket 更新 .length。但确实有一个问题,在第一次 recv 调用之后,传递给 receiveFromdata 缓冲区太小了。可能的解决方案:删除ref,但从recv 返回更新后的切片并将其用于handlePacket。或者保持recv 不变,并将data 重置为handlePacket 之后的完整缓冲区大小。
  • @jpf 是的,我修改了答案以使其更准确
  • 您能否更深入地了解为什么 ref byte[] buffer 只接受 7 个字节?这对我来说没有任何意义。
  • @MaikKlein 当您查看 github.com/dlang/phobos/blob/v2.071.0/std/socket.d#L3041 时,您会看到使用了 buf.length。在上面的代码中,他将 buf.length 更改为最后一个数据包的长度。所以当他接收到大小为 7 字节的数据包时,比在下一次调用中只会从套接字读取 7 个字节。
猜你喜欢
  • 2015-10-11
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 2017-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多