【问题标题】:Get Sender's IP Address using Overlapped IO使用 Overlapped IO 获取 Sender 的 IP 地址
【发布时间】:2011-12-20 21:50:14
【问题描述】:

我知道 WSARecvFrom 有一个参数,用于返回发件人的 IP 地址。但是,当我将它与重叠 io 一起使用时,我传递给它的变量不会被填充。

WSARecvFrom(udpSocket, &receiveBuffer, 1, 0, &flags, (sockaddr*)&incomingAddress, &SocketAddressSize, &receiveOverlapped, 0)

...

WSAWaitForMultipleEvents(2, networkEvents, false, WSA_INFINITE, false)

...

WSAGetOverlappedResult(udpSocket, &receiveOverlapped, &transferCount, true, &flags);

...

char* incomingAddressString = inet_ntoa(incomingAddress.sin_addr);

incomingAddressString 现在等于“204.204.204.204”

我错过了什么吗?

谢谢

【问题讨论】:

标签: c++ networking winsock winsock2 overlapped-io


【解决方案1】:

根据documentation

另外,lpFrom 和 lpFromlen 指示的值不会更新 直到完成本身指示。应用程序不得使用或 扰乱这些值,直到它们被更新,因此 应用程序不得使用自动(即基于堆栈的)变量 对于这些参数。

通常,在使用重叠 I/O 时,您应该定义一个自定义结构,其中包含 OVERLAPPEDWSAOVERLAPPED(取决于您使用的 API - 在这种情况下为 WSAOVERLAPPED)以及您需要的任何其他数据(就像你在这种情况下的 sockaddr_in 缓冲区)。然后分配该结构的动态实例并将其数据成员传递给WSARecvFrom() 并等待操作完成,然后再为该结构释放内存。这样,当操作重叠的操作自己进行时,内存仍然有效。例如:

struct MyOverlappedInfo
{
    WSAOVERLAPPED Overlapped;
    DWORD Flags;
    sockaddr_in IncomingAddress;
    int IncomingAddressSize; 
    BYTE Data[1024];
    DWORD DataSize;
    WSABUF Buffer;

    MyOverlappedInfo()
    {
        memset(this, 0, sizeof(this));
        Overlapped.hEvent = WSACreateEvent();
        Buffer.len = sizeof(Data);
        Buffer.buf = (char*) Data;
    }

    ~MyOverlappedInfo()
    {
        WSACloseEvent(Overlapped.hEvent);
    }
};

MyOverlappedInfo info = new MyOverlappedInfo;

WSARecvFrom(udpSocket, &info->Buffer, 1, NULL, &info->Flags, (sockaddr*)&info->IncomingAddress, &info->IncomingAddressSize, &info->Overlapped, NULL);
... 
WSAWaitForMultipleEvents(2, networkEvents, false, WSA_INFINITE, false) 
... 
WSAGetOverlappedResult(udpSocket, &info->Overlapped, &info->DataSize, TRUE, &info->Flags); 
... 
char* incomingAddressString = inet_ntoa(info->IncomingAddress.sin_addr); 
delete info;

如果您通过CreateIOCompletionPort()GetQueuedCompletionStatus() 为您的套接字I/O 使用I/O 完成端口,而不是使用WSAWaitForMultipleEvents()WSAGetOverlappedResult(),这种方法会更有用。阅读这篇文章了解更多详情:

Windows Sockets 2.0: Write Scalable Winsock Apps Using Completion Ports

【讨论】:

  • 我所有的网络都在一个类中,我没有传递任何堆栈变量。我测试了将一个新变量传递给 lpFromLen 但它会出错,除非它被设置为地址结构的大小,这让我怀疑那里的文档的准确性。
  • 这是你应该做的。我在回答中链接到的文档说明了很多:“lpFromlen 指向的值被初始化为此缓冲区的大小,并在完成时被修改以指示存储在那里的地址的实际大小。”
  • 是的,我指的是“lpFromlen 在完成之前不会更新”,这没有任何意义(为什么会改变?)。但是我们在这里离开主题......
  • 您可以将更大的缓冲区传递给WSARecvFrom(),如果收到较小的地址,它将截断大小。例如,当传递一个足以容纳 IPv6 地址的缓冲区时,会收到一个 IPv4 地址。某些操作系统版本支持双栈套接字,用于在单个套接字上处理 IPv4 和 IPv6 流量。要支持这两个地址,您的lpFrom 缓冲区应该足够大以容纳SOCKADDR_STORAGE 结构。
  • 204.204.204.204 的 IPv4 地址意味着您有一个用 0xCC 字节填充的 4 字节地址。一些内存管理器使用0xCC 来填充已释放的内存,作为检测以后对已释放内存的访问的一种手段。我会开始在那里寻找。确保WSARecvFrom() 忙时您的类对象没有被释放。此外,您正在等待多个网络事件,所以您确定WSARecvFrom() 事件是您调用WSAGetOverlappedResult() 时实际发出的信号吗?您是否在进行错误处理以确保 WSAGetOverlappedResult() 没有失败?
猜你喜欢
  • 1970-01-01
  • 2011-02-10
  • 2019-12-11
  • 2010-11-04
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2014-09-27
  • 2011-02-06
相关资源
最近更新 更多