TCP/IP IOCP 接收到的数据有时会损坏 - Windows 上的 Visual C++

2024-01-28

我正在编写一个简单的测试 ICOP 客户端和服务器,以确保我正确使用 API 并且服务器正确接收客户端发送的数据。我已经包含了这个问题的所有代码。

这就是我遇到一些问题的地方,接收缓冲区内的数据有时似乎已损坏(损坏是因为有时缓冲区内的数据块可能乱序或丢失)。需要明确的是,这是各个接收缓冲区内的数据,我并不是指由于线程调度问题而导致多个缓冲区之间的顺序混乱。我之前发布过一个与此相关的问题here https://stackoverflow.com/questions/27742668/tcp-ip-server-using-iocp-occasional-data-corruption-in-receive-buffers。不过,我在获取正确的代码示例方面做了更多工作,因此我发布了一个新问题并将链接到此问题。我希望其他人能够运行这段代码并体验到同样奇怪的行为。

测试代码

测试应用程序可以在两种模式下运行:客户端和服务器。运行服务器并开始侦听,运行客户端并连接到服务器,一旦连接就会开始以允许的速度向服务器抛出数据。然后,服务器验证调用 WSARecv 后从 GetQueuedCompletionStatus 返回的每个缓冲区内的数据。每次 WSASend 完成时,我都会将结构的 OVERLAPPED 部分清零,并使用原始数据缓冲区再次调用 WSASend。

客户端发送的每个数据缓冲区都是一系列字节,这些字节依次递增直至达到指定的最大值。我不会发送完整范围的 0..255,以防大小完全适合数据包的倍数,并以某种方式隐藏问题,因此在我的示例代码字节范围为 0..250。对于构建的每个发送缓冲区,我重复该模式 numberOfGroups 次。

这种格式应该意味着我可以有多个 WSARecv 未完成,然后完全独立于任何其他缓冲区来验证返回缓冲区中的数据,这意味着不需要同步或重建顺序。也就是说,我可以从第一个字节开始,验证它们是否依次递增到最大值,然后重置为 0。一旦我的测试没有问题,我就可以进行更复杂的操作,对接收到的缓冲区进行排序并验证更复杂的数据。

您可以在命令行上指定可以有多少个同时未完成的 WSASend 和 WSARecv 调用。这个问题似乎发生了far当有 2 个或更多未完成的 WSARecv 调用时,这种情况会更常见。使用 1 时,它可以运行相当长的时间,然后偶尔会检测到问题。

我一直在 Windows 7 上并使用 Visual Studio 2010 C++ 进行测试。

客户端和服务器中同时调用的数量似乎都有影响。对两者使用 2 似乎比某些组合更容易产生损坏数据。

套接字和 IOCP 似乎需要大量样板代码才能启动并运行一个非常基本的客户端和服务器应用程序。接收缓冲区的实际代码只有几行,涉及调用 WSARecv 并处理来自 GetQueuedCompletionStatus 的已完成调用。

此代码调用 WSARecv

void IOCPConnection::postRecv(PTestOverlapped overlapped)
{
    DWORD numberOfBytesTransferred = 0;
    DWORD flags = 0;
    if (overlapped == nullptr)
    {
        overlapped = new TestOverlapped(receiveBufferSize);
        overlapped->connection = this;
    }
    else
    {
        overlapped->reset();
    }
    overlapped->operation = soRecv;
    auto returnCode = WSARecv(socket, &(overlapped->buffer), 1, &numberOfBytesTransferred, &flags, (LPWSAOVERLAPPED) overlapped, nullptr);
}

当 WSARecv 调用完成时,它们由工作线程处理 - 我删除了与从此代码段接收数据无关的行

void IOCPWorker::execute()
{
    bool quit = false;
    DWORD numberOfBytesTransferred = 0;
    ULONG_PTR completionKey = NULL;
    PTestOverlapped overlapped = nullptr;
    while (!quit)
    {
        auto queueResult = GetQueuedCompletionStatus(manager->iocp, &numberOfBytesTransferred, &completionKey, (LPOVERLAPPED *)&overlapped, INFINITE);
        if (queueResult)
        {
            switch (overlapped->operation)
            {
                case soRecv:
                {
                    IOCPConnection *connection = overlapped->connection;
                    connection->onRecv(overlapped, numberOfBytesTransferred); // This method validates the received data

                    connection->postRecv(overlapped);
                    overlapped = nullptr;
                    break;
                }
                default:;
            }
        }
    }
}

对connection->onRecv 的调用是我验证数据的地方。这里看起来有什么明显错误吗?

我已经提供了完整的代码供参考,如果您喜欢冒险,应该编译。


完整源码供参考

服务器示例侦听端口 3000,最多有 2 个未完成的 WSARecv 调用

> IOCPTest.exe server 3000 2

连接到端口 3000 上的 127.0.0.1 的客户端示例,最多有 2 个未完成的 WSASend 调用

> IOCPTest.exe client 127.0.0.1 3000 2

该计划由少量课程组成

IOCPConnectionManager

此类处理连接监听并启动工作线程。

IOCPConnection

只需跟踪 SOCKET 和一些处理异步调用的方法。当 WSARecv 返回并验证缓冲区内的数据时,将调用 IOCPConnection::onRecv。它只是打印一条消息,如果发现数据乱序则返回。

IOCPWorker

工作线程。 IOCPWorker::execute() 是调用 GetQueuedCompletionStatus 的地方。

TestOverlapped

所需的重叠结构。

您还需要为链接器包含 Ws2_32.lib 和 Mswsock.lib。

主.cpp文件

/************************************************************************
*                                                                       *
*  Test IOCP Client and Server - David Shaw                             *
*                                                                       *
*  There is limited error handling here and it assumes ideal conditions *
*  Some allocated objects are not freed at the end, this is a test only *
*                                                                       *
************************************************************************/

#include "stdafx.h"
#include <iostream>
#include <string>
#include "IOCPTest.h"
#include <Windows.h>

void printUse()
{
    std::cout << "Invalid arguments" << std::endl;
    std::cout << "This test app has very limited error handling or memory management" << std::endl;
    std::cout << "Run as client or server (run the server first) e.g." << std::endl << std::endl;
    std::cout << "To run as server listening on port 3000 with 2 pending receives:" << std::endl;
    std::cout << "> IOCPTester.exe server 3000 2" << std::endl << std::endl;
    std::cout << "To run as client connected to 127.0.0.1 on port 3000 with 2 pending sends:" << std::endl;
    std::cout << "> IOCPTester.exe client 127.0.0.1 3000 2" << std::endl << std::endl;
    std::cout << "Hit enter to exit" << std::endl;
    std::cin.ignore();
}

int main(int argc, char *argv[])
{
    if (argc < 4)
    {
        printUse();
        return 0;
    }
    std::string mode(argv[1]);
    if ((mode.compare("client") != 0) && (mode.compare("server") != 0))
    {
        printUse();
        return 0;
    }

    IOCPTest::IOCPConnectionManager *manager = new IOCPTest::IOCPConnectionManager();

    bool server = mode.compare("server") == 0;
    if (server)
    {
        std::string listenPort(argv[2]);
        std::string postedReceiveCount(argv[3]);

        manager->listenPort = atoi(listenPort.c_str());
        manager->postedReceiveCount = atoi(postedReceiveCount.c_str());
        manager->postedSendCount = 1; // Not really used in this mode
        manager->startListening();
    }
    else
    {
        if (argc < 5)
        {
            printUse();
            return 0;
        }

        std::string host(argv[2]);
        std::string port(argv[3]);
        std::string postedSendCount(argv[4]);

        manager->postedReceiveCount = 1; // Not really used in this mode
        manager->postedSendCount = atoi(postedSendCount.c_str());

        IOCPTest::IOCPConnection *connection = manager->createConnection();

        connection->host = host;
        connection->port = atoi(port.c_str());
        connection->connect();
    }
    std::cout << "Hit enter to exit" << std::endl;
    std::cin.ignore();
}

IOCP测试.h

/************************************************************************
*                                                                       *
*  Test IOCP Client and Server - David Shaw                             *
*                                                                       *
*  There is limited error handling here and it assumes ideal conditions *
*  std::cout might not be the best approach in a multithreaded          *
*  environment but this is just a simple test app.                      *
*  Some allocated objects are not cleaned up at the end either, but     *
*  again this is just a test.                                           *
*                                                                       *
************************************************************************/

#ifndef IOCPTestH
#define IOCPTestH
#endif

#include <WinSock2.h> // Include before as otherwise Windows.h includes and causes issues
#include <Windows.h>
#include <string>

namespace IOCPTest
{

class IOCPConnection;

enum IOCPSocketOperation
{
    soUnknown,
    soAccept,
    soConnect,
    soDisconnect,
    soSend,
    soRecv,
    soQuit
};

struct TestOverlapped
{
    OVERLAPPED overlapped;
    WSABUF buffer;
    IOCPSocketOperation operation;
    IOCPConnection *connection;
    bool resend; // Set this to keep sending the same data over and over

    TestOverlapped(int bufferSize);
    ~TestOverlapped();
    void reset();
};

typedef TestOverlapped *PTestOverlapped;

class IOCPConnectionManager
{
public:
    static const int NUMACCEPTS = 5;

    WSADATA wsaData;
    HANDLE iocp;
    SOCKET listenSocket;
    USHORT listenPort;
    int postedReceiveCount;
    int postedSendCount;

    void startListening();
    void postAcceptEx();

    IOCPConnection *createConnection();

    IOCPConnectionManager();
};

class IOCPConnection
{
public:
    SOCKET socket;
    IOCPConnectionManager *manager;
    std::string host;
    USHORT port;

    void onAcceptEx(PTestOverlapped overlapped, DWORD numberOfBytesTransferred);
    void postRecv(PTestOverlapped overlapped = nullptr);
    void onRecv(PTestOverlapped overlapped, DWORD numberOfBytesTransferred);
    void onConnect(PTestOverlapped overlapped, DWORD numberOfBytesTransferred);
    void send(PTestOverlapped overlapped);
    void onSent(PTestOverlapped overlapped, DWORD numberOfBytesTransferred);

    void connect();
};

class IOCPWorker
{
public:
    HANDLE threadHandle;
    DWORD threadId;
    IOCPConnectionManager *manager;

    IOCPWorker(bool suspended);

    void start();
    void execute();
};

}

IOCP测试.cpp

#include "stdafx.h"
#include "IOCPTest.h"
#include <iostream>
#include <Mswsock.h>
#include <WS2tcpip.h>
#include <sstream>

namespace IOCPTest
{

LPFN_ACCEPTEX fnAcceptEx = nullptr;
LPFN_CONNECTEX fnConnectEx = nullptr;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
GUID GuidConnectEx = WSAID_CONNECTEX;
const byte maxByteExpected = 250;
const int numberOfGroups = 4096;
const int receiveBufferSize = 0x100000;

BOOL AcceptEx
(
    SOCKET sListenSocket,
    SOCKET sAcceptSocket,
    PVOID lpOutputBuffer,
    DWORD dwReceiveDataLength,
    DWORD dwLocalAddressLength,
    DWORD dwRemoteAddressLength,
    LPDWORD lpdwBytesReceived,
    LPOVERLAPPED lpOverlapped
)
{
    if (fnAcceptEx == nullptr)
    {
        DWORD dwBytes;
        int result = WSAIoctl(sListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof (GuidAcceptEx), &fnAcceptEx, sizeof(fnAcceptEx), &dwBytes, NULL, NULL);
        if (result != 0)
        {
            std::cerr << "Error calling WSAIoctl for AcceptEx" << std::endl;
            return false;
        }
    }
    return fnAcceptEx(sListenSocket, sAcceptSocket, lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, lpOverlapped);
}

BOOL ConnectEx(
    SOCKET s,
    const struct sockaddr FAR *name,
    int namelen,
    PVOID lpSendBuffer,
    DWORD dwSendDataLength,
    LPDWORD lpdwBytesSent,
    LPOVERLAPPED lpOverlapped
)
{
    if (fnConnectEx == nullptr)
    {
        DWORD dwBytes;
        int result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidConnectEx, sizeof (GuidConnectEx), &fnConnectEx, sizeof(fnConnectEx), &dwBytes, NULL, NULL);
        if (result != 0)
        {
            std::cerr << "Error calling WSAIoctl for ConnectEx" << std::endl;
            return false;
        }
    }
    return fnConnectEx(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, lpOverlapped);
}

// TestOverlapped

TestOverlapped::TestOverlapped(int bufferSize):
    overlapped(), 
    operation(soUnknown),
    connection(nullptr),
    buffer(),
    resend(false)
{
    if (bufferSize > 0)
    {
        buffer.len = bufferSize;
        buffer.buf = (CHAR*) malloc(bufferSize);
    }
}

TestOverlapped::~TestOverlapped()
{
    if (buffer.buf != nullptr)
    {
        free(buffer.buf);
    }
}

void TestOverlapped::reset()
{
    overlapped = OVERLAPPED();
}

// IOCPConnectionManager

IOCPConnectionManager::IOCPConnectionManager():
    wsaData(),
    listenSocket(0),
    listenPort(0),
    postedReceiveCount(1)
{
    WSAStartup(WINSOCK_VERSION, &wsaData);
    iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);

    SYSTEM_INFO systemInfo = SYSTEM_INFO();
    GetSystemInfo(&systemInfo);

    for (decltype(systemInfo.dwNumberOfProcessors) i = 0; i < systemInfo.dwNumberOfProcessors; i++)
    {
        IOCPWorker* worker = new IOCPWorker(true);
        worker->manager = this;
        worker->start();
    }
}

void IOCPConnectionManager::startListening()
{
    listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    CreateIoCompletionPort((HANDLE)listenSocket, iocp, ULONG_PTR(this), 0);

    sockaddr_in localAddress = sockaddr_in();
    localAddress.sin_family = AF_INET;
    localAddress.sin_addr.s_addr = INADDR_ANY; // Listen on all addresses
    localAddress.sin_port = htons(listenPort);

    if (bind(listenSocket, (SOCKADDR*) &localAddress, sizeof(localAddress)) == SOCKET_ERROR)
    {
        std::cerr << "Error in binding listening socket" << std::endl;
    }
    if (listen(listenSocket, SOMAXCONN) == 0)
    {
        std::cout << "Listening on port " << listenPort << std::endl;
    }
    for (int i = 0; i < NUMACCEPTS; i++)
    {
        postAcceptEx();
    }
}

void IOCPConnectionManager::postAcceptEx()
{
    SOCKET acceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    IOCPConnection *connection = new IOCPConnection();
    connection->manager = this;
    connection->socket = acceptSocket;

    CreateIoCompletionPort((HANDLE) acceptSocket, iocp, ULONG_PTR(connection), 0); // The thread count is ignored in this call when just associating the socket

    PTestOverlapped overlapped = new TestOverlapped(2 * (sizeof(sockaddr_in) + 16)); // As specified in documentation
    overlapped->operation = soAccept;
    overlapped->connection = connection;
    DWORD byesReceived = 0;
    int result = IOCPTest::AcceptEx
    (
        listenSocket,
        acceptSocket,
        overlapped->buffer.buf,
        0, // Size of initial receiving buffer, excluding the space at the end for the two addressed
        sizeof(sockaddr_in) + 16, // Sizes as specified in the Winsock 2.2 API documentation
        sizeof(sockaddr_in) + 16, // Sizes as specified in the Winsock 2.2 API documentation
        &byesReceived,
        (LPOVERLAPPED) overlapped
    );
    if (!result)
    {
        int errorCode = WSAGetLastError();
        if (errorCode != WSA_IO_PENDING)
        {
            std::cerr << "Error calling AcceptEx. Returned errorCode = " << errorCode << std::endl;
        }
    }
}

IOCPConnection *IOCPConnectionManager::createConnection()
{
    IOCPConnection *connection = new IOCPConnection();
    connection->manager = this;

    return connection;
}

// IOCPConnection

void IOCPConnection::onAcceptEx(PTestOverlapped overlapped, DWORD numberOfBytesTransferred)
{
    manager->postAcceptEx(); // Replace this accept
    auto returnCode = setsockopt(socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (const char *)&manager->listenSocket, sizeof(manager->listenSocket));
    if (returnCode == SOCKET_ERROR)
    {
        std::cerr << "SetSockOpt in OnAcceptEx returned SOCKET_ERROR" << std::endl;
    }
    std::cout << "Connection Accepted" << std::endl;
    for (int i = 0; i < manager->postedReceiveCount; ++i)
    {
        postRecv();
    }
}

void IOCPConnection::postRecv(PTestOverlapped overlapped)
{
    DWORD numberOfBytesTransferred = 0;
    DWORD flags = 0;
    if (overlapped == nullptr)
    {
        overlapped = new TestOverlapped(receiveBufferSize);
        overlapped->connection = this;
    }
    else
    {
        overlapped->reset();
    }
    overlapped->operation = soRecv;
    auto returnCode = WSARecv(socket, &(overlapped->buffer), 1, &numberOfBytesTransferred, &flags, (LPWSAOVERLAPPED) overlapped, nullptr);
}

void IOCPConnection::onRecv(PTestOverlapped overlapped, DWORD numberOfBytesTransferred)
{
    if (numberOfBytesTransferred > 0)
    {
        byte *data = (byte *)overlapped->buffer.buf;
        if (data[0] > maxByteExpected)
        {
            std::cerr << "Byte greater than max expected found. Max Expected: " << maxByteExpected << "; Found: " << data[0] << std::endl;
            return;
        }
        byte next = (data[0] == maxByteExpected)?0:data[0] + 1;
        for (decltype(numberOfBytesTransferred) i = 1; i < numberOfBytesTransferred; ++i)
        {
            if (data[i] != next)
            {
                // Not really the best solution for writing data out from multiple threads. Test app only.
                std::cerr << "Invalid data. Expected: " << (int)next << "; Got: " << (int)data[i] << std::endl;
                return;
            }
            else if (next == maxByteExpected)
            {
                next = 0;
            }
            else
            {
                ++next;
            }
        }
        //std::cout << "Valid buffer processed" << std::endl;
    }
}

void IOCPConnection::onConnect(PTestOverlapped overlapped, DWORD numberOfBytesTransferred)
{
    for (int i = 0; i < manager->postedSendCount; ++i)
    {
        // Construct a sequence of incremented byte values 0..maxByteExpected repeated numberOfGroups
        PTestOverlapped sendOverlapped = new TestOverlapped((maxByteExpected + 1) * numberOfGroups);
        sendOverlapped->connection = this;

        for (int j = 0; j < numberOfGroups; ++j)
        {
            for (byte k = 0; k <= maxByteExpected; ++k)
            {
                ((byte *)sendOverlapped->buffer.buf)[(j * (maxByteExpected + 1)) + (int)k] = k;
            }
        }
        sendOverlapped->resend = true; // Repeat sending this data
        send(sendOverlapped);
    }
}

void IOCPConnection::send(PTestOverlapped overlapped)
{
    overlapped->reset();
    overlapped->operation = soSend;

    DWORD bytesSent = 0;
    DWORD flags = 0;

    if (WSASend(socket, &overlapped->buffer, 1, &bytesSent, flags, (LPWSAOVERLAPPED) overlapped, nullptr) == SOCKET_ERROR)
    {
        int errorCode = WSAGetLastError();
        if (errorCode != WSA_IO_PENDING)
        {
            std::cerr << "Error calling WSASend. Returned errorCode = " << errorCode << std::endl;
        }
    }
}

void IOCPConnection::onSent(PTestOverlapped overlapped, DWORD numberOfBytesTransferred)
{
}

void IOCPConnection::connect()
{
    socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socket == INVALID_SOCKET)
    {
        std::cerr << "Error calling socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) in IOCPConnection::connect()" << std::endl;
        return;
    }
    CreateIoCompletionPort((HANDLE)socket, manager->iocp, ULONG_PTR(this), 0); // The thread count is ignored in this call when just associating the socket

    sockaddr_in localAddress = sockaddr_in();
    localAddress.sin_family = AF_INET;
    localAddress.sin_addr.s_addr = INADDR_ANY;
    localAddress.sin_port = 0;

    if (bind(socket, (SOCKADDR *) &localAddress, sizeof(localAddress)) == SOCKET_ERROR)
    {
        std::cerr << "Error calling bind(socket, (SOCKADDR *) &localAddress, sizeof(localAddress) in IOCPConnection::connect()" << std::endl;
        return;
    }

    addrinfo hints = addrinfo();
    addrinfo *remoteAddress = nullptr;

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    std::stringstream ss;
    ss << port;
    //std::cout << ss.str() << std::endl;
    if (getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &remoteAddress) != 0)
    {
        std::cerr << "Error calling getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &remoteAddress) in IOCPConnection::connect()" << std::endl;
        return;
    }

    TestOverlapped *overlapped = new TestOverlapped(0);
    overlapped->connection = this;
    overlapped->operation = soConnect;

    BOOL result = IOCPTest::ConnectEx
    (
        socket,
        remoteAddress->ai_addr,
        remoteAddress->ai_addrlen,
        nullptr,
        0,
        nullptr,
        LPOVERLAPPED(overlapped)
    );
    if (result == FALSE)
    {
        int errorCode = WSAGetLastError();
        if (errorCode != WSA_IO_PENDING)
        {
            //std::cerr << "Error calling ConnectEx. You'll need to add some more code if you want to know why :)" << std::endl;
            std::cerr << "Error calling ConnectEx. Returned errorCode = " << errorCode << std::endl;
        }
    }

    freeaddrinfo(remoteAddress);
}

// IOCPWorker

DWORD WINAPI IOCPWorkerThreadProc(LPVOID lpParam)
{
    ((IOCPWorker*)lpParam)->execute();
    return 0;
}

IOCPWorker::IOCPWorker(bool suspended)
{
    threadHandle = CreateThread(NULL, 0, IOCPWorkerThreadProc, this, (suspended)?CREATE_SUSPENDED:0, &threadId);
}

void IOCPWorker::start()
{
    ResumeThread(threadHandle);
}

void IOCPWorker::execute()
{
    //std::cout << "TMVIOCPWorker::execute()" << std::endl;
    bool quit = false;
    DWORD numberOfBytesTransferred = 0;
    ULONG_PTR completionKey = NULL;
    PTestOverlapped overlapped = nullptr;
    while (!quit)
    {
        auto queueResult = GetQueuedCompletionStatus(manager->iocp, &numberOfBytesTransferred, &completionKey, (LPOVERLAPPED *)&overlapped, INFINITE);
        if (queueResult)
        {
            switch (overlapped->operation)
            {
                case soAccept:
                {
                    IOCPConnection *connection = overlapped->connection;
                    connection->onAcceptEx(overlapped, numberOfBytesTransferred);

                    delete overlapped;
                    overlapped = nullptr;
                    break;
                }
                case soConnect:
                {
                    std::cout << "ConnectEx returned" << std::endl;
                    IOCPConnection *connection = overlapped->connection;
                    connection->onConnect(overlapped, numberOfBytesTransferred); // This method validates the received data
                    delete overlapped;
                    overlapped = nullptr;
                    break;
                }
                case soRecv:
                {
                    //std::cout << "Received Data: " << numberOfBytesTransferred << std::endl;
                    IOCPConnection *connection = overlapped->connection;
                    connection->onRecv(overlapped, numberOfBytesTransferred); // This method validates the received data

                    overlapped->reset();
                    connection->postRecv(overlapped);
                    overlapped = nullptr;
                    break;
                }
                case soSend:
                {
                    IOCPConnection *connection = overlapped->connection;
                    connection->onSent(overlapped, numberOfBytesTransferred);

                    // Send the same data over and over
                    std::cout << "Resending buffer" << std::endl;
                    if (overlapped->resend)
                    {
                        connection->send(overlapped);
                    }
                    else
                    {
                        delete overlapped;
                    }
                    overlapped = nullptr;
                    break;
                }
                default:;
            }
        }
    }
}

}

大多数接收到的缓冲区都是正确的,但是当使用套接字的 2 个接收缓冲区和 2 个发送缓冲区运行时,我仍然有很多像这样的滚动:

Invalid data. Expected: 169; Got: 123
Invalid data. Expected: 114; Got: 89
Invalid data. Expected: 89; Got: 156
Invalid data. Expected: 206; Got: 227
Invalid data. Expected: 125; Got: 54
Invalid data. Expected: 25; Got: 0
Invalid data. Expected: 58; Got: 146
Invalid data. Expected: 33; Got: 167
Invalid data. Expected: 212; Got: 233
Invalid data. Expected: 111; Got: 86
Invalid data. Expected: 86; Got: 153
Invalid data. Expected: 190; Got: 165
Invalid data. Expected: 175; Got: 150
Invalid data. Expected: 150; Got: 217
Invalid data. Expected: 91; Got: 112
Invalid data. Expected: 95; Got: 162
Invalid data. Expected: 207; Got: 182
Invalid data. Expected: 222; Got: 243
Invalid data. Expected: 126; Got: 101
Invalid data. Expected: 157; Got: 132
Invalid data. Expected: 160; Got: 89
Invalid data. Expected: 205; Got: 180
Invalid data. Expected: 113; Got: 134
Invalid data. Expected: 45; Got: 20
Invalid data. Expected: 113; Got: 201
Invalid data. Expected: 64; Got: 198
Invalid data. Expected: 115; Got: 182
Invalid data. Expected: 140; Got: 115

我希望这只是我做错的一些简单的事情。在发送之前,我在数据缓冲区上运行了与接收时相同的验证,以确保我没有在那里做一些愚蠢的事情,但它通过了该检查。我用另一种语言编写了一个服务器not使用IOCP,似乎可以正确接收数据。我还用另一种语言编写了一个客户端,IOCP 服务器似乎也在这种情况下检测到损坏。但话虽如此,客户端和服务器都可能存在问题。我很感激任何人愿意为此花费时间。


好吧,我可能已经发现你的问题了。如果你看一下收到的数据,所有字节都是按顺序排列的,但序列突然跳变,就好像被另一个调用中断了一样。现在,从 MSDN 文档WSASend http://msdn.microsoft.com/en-us/library/windows/desktop/ms742203%28v=vs.85%29.aspx and WSARecv http://msdn.microsoft.com/en-us/library/windows/desktop/ms741688%28v=vs.85%29.aspx :

如果您使用 I/O 完成端口,请注意对 WSASend 的调用顺序也是填充缓冲区的顺序。不应从不同线程在同一套接字上同时调用 WSASend,因为这可能会导致不可预测的缓冲区顺序。

如果您使用 I/O 完成端口,请注意对 WSARecv 的调用顺序也是填充缓冲区的顺序。不应从不同线程在同一套接字上同时调用 WSARecv,因为这可能会导致不可预测的缓冲区顺序。

就是这样。我现在真的不知道你想要什么的好方法,但你所做的可能不是它应该使用的方式。

您是否在真实网络上尝试过此操作?环回接口是一个特殊的电路,其行为可能有所不同,但它仍然是未定义的行为,因此您不应该依赖它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TCP/IP IOCP 接收到的数据有时会损坏 - Windows 上的 Visual C++ 的相关文章

  • 使用 mono/nunit-console/4 在 Mac OS X 控制台上运行测试

    我安装了 Max OS X 10 11 1 上面装有 Xamarin 我编写了简单的测试类 只是为了测试在 Mac OS X 和 Ubuntu 上运行 Nunit 测试 该类实际上有一个返回字符串的方法 using System names
  • 有没有快速创建集合的方法?

    目前我正在创建一个像这样的新集 std set a s s insert a1 s insert a2 s insert a3 s insert a10 有没有办法创建s在一行 int myints 10 20 30 40 50 std s
  • QCombobox 向下箭头图像

    如何更改Qcombobox向下箭头图像 现在我正在使用这个 QSS 代码 但这不起作用 我无法删除向下箭头边框 QComboBox border 0px QComboBox down arrow border 0px background
  • 在 C# 中按元素相乘数组具有意想不到的性能

    我想找到按元素相乘两个数组的最佳方法 这是更广泛项目的一部分 其中性能而不是唯一的考虑因素 我今天开始用 C Linqpad 编写一些函数 因此它还没有以任何方式进行优化 下面代码的输出如下 Environment ProcessorCou
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • 防止 boost::asio::io_context 在空轮询调用时停止

    此代码调用发布的句柄 boost asio io context ioc boost asio post ioc std cout lt lt lol lt lt std endl ioc poll 而这并没有 boost asio io
  • 根据 N 个值中最小的一个返回不同的结果

    不确定如何使标题更具描述性 所以我只是从一个例子开始 我使用下面的代码位 它从枚举中选择一个方向 具体取决于四个轴中哪一个与给定方向相比形成最小角度 static Direction VectorToDirection Vector2 di
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 为什么这个没有特殊字符的正则表达式会匹配更长的字符串?

    我正在使用此方法来尝试查找匹配项 例如 Regex Match A2 TS OIL TS OIL RegexOptions IgnoreCase Success 我得到了真实的结果 我很困惑 我认为这应该返回 false 因为模式中没有特殊
  • fprintf() 线程安全吗?

    我正在为野人就餐问题的某些变量编写一个 C 解决方案 现在 我创建线程 每个线程都将 FILE 获取到同一个调试文件 在线程内我正在使用 fprintf 进行一些打印 打印的语句不受任何类型的互斥锁等保护 我没有在调试文件中观察到任何交错行
  • 单例模式和 std::unique_ptr

    std unique ptr唯一地控制它指向的对象 因此不使用引用计数 单例确保利用引用计数只能创建一个对象 那么会std unique ptr与单例执行相同 单例确保只有一个实例属于一种类型 A unique ptr确保只有一个智能指针到
  • 如何在服务器端按钮点击时关闭当前标签页?

    我尝试在确认后关闭当前选项卡 因此我将以下代码放在确认按钮的末尾 但选项卡没有关闭 string jScript ClientScript RegisterClientScriptBlock this GetType keyClientBl
  • 将二进制数据从 C# 上传到 PHP

    我想将文件从 Windows C 应用程序上传到运行 PHP 的 Web 服务器 我知道 WebClient UploadFile 方法 但我希望能够分块上传文件 以便我可以监控进度并能够暂停 恢复 因此 我正在读取文件的一部分并使用 We
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • 我可以让 ungetc 取消阻止阻塞的 fgetc 调用吗?

    我想在收到 SIGUSR1 后使用 ungetc 将 A 字符重新填充到标准输入中 想象一下我有充分的理由这样做 调用 foo 时 stdin 中的阻塞读取不会被收到信号时的 ungetc 调用中断 虽然我没想到它会按原样工作 但我想知道是
  • 使用restsharp序列化对象并将其传递给WebApi而不是序列化列表

    我有一个看起来像的视图模型 public class StoreItemViewModel public Guid ItemId get set public List
  • C++ Streambuf 方法可以抛出异常吗?

    我正在尝试找到一种方法来获取读取或写入流的字符数 即使存在错误并且读 写结束时间较短 该方法也是可靠的 我正在做这样的事情 return stream rdbuf gt sputn buffer buffer size 但如果streamb
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • 将 char[][] 转换为 char** 会导致段错误吗?

    好吧 我的 C 有点生疏了 但我想我应该用 C 来做我的下一个 小 项目 这样我就可以对其进行抛光 并且我已经有不到 20 行的段错误了 这是我的完整代码 define ROWS 4 define COLS 4 char main map
  • xsi:type 属性搞乱了 C# XML 反序列化

    我使用 XSD exe 根据 XML 架构 xsd 文件 自动生成 C 对象 我正在反序列化 OpenCover 输出 但其中一个部分类未正确生成 这是导致异常的行

随机推荐

  • 什么时候删除 git 功能分支合适?

    我不想结束82 个特色分支悬挂在周围 https stackoverflow com questions 2353804 list git branches that can safely be deleted 所以我想知道将功能分支合并到
  • 如何用C/C++打开Chrome扩展程序中包含的文件?

    我正在尝试在 Chrome 扩展程序中打开一个要解析的文件 例如 config txt 通过 Javascript 这将相当容易 例如 chrome extension getURL config txt 会得到类似的东西chrome ex
  • 对 Intellij 产品中的控制台输出进行着色

    我有一个带有默认输出的自定义脚本 我想对错误 警告和信息进行着色 在 Intellij 产品 IDEA PhpStorm PyCharm 中有办法做到这一点吗 已经有一段时间了 但如果您仍然感兴趣 有一个用于控制台着色的新插件 与 Inte
  • 如何避免 IntelliTrace 事件中的 EF 查询被截断?

    我正在尝试调试实体框架查询 但是 当查询变得太大时 intelliTrace 中显示的查询会被截断 即使我将其复制并粘贴到记事本中 也仅显示查询的一部分 是否有任何解决方法可以获取 EF 生成的整个查询 提前致谢 已经有一段时间了 但它可能
  • 滚动时列表视图图像发生变化

    I am 解析 json数据在一个列表视图 图像视图和文本视图 首先四项列表视图的图像和文本视图正确添加但是当我滚动列表视图时 图像视图中的图像正在发生变化 这是我的代码 适配器 java public class Adapter exte
  • 角度插值无法正常工作?

    我有一个带有插值的样本 当使用任何一个事件时 它会正确显示下拉值 如下所示 sample click me https stackblitz com edit angular 234dlf file src 2Fapp 2Fapp comp
  • MSBuild (TFS) 中的 Nhibernate.Bytecode.Castle 问题

    我们有一个 Fluent NHibernate 映射测试正在本地计算机上通过 但是当我们签入 TFS 时 测试在构建服务器上失败 我们正在使用 MSTest 我们得到的错误是 NHibernate Bytecode UnableToLoad
  • 如何在不修改代码的情况下禁用在 pytest 中跳过测试?

    我继承了一些实现的代码pytest mark skipif进行一些测试 阅读 pytest 文档 我知道我可以添加条件 可能检查环境变量 或者使用更高级的功能pytest mark将测试组一起控制 不幸的是 到目前为止 文档中似乎没有任何内
  • 字幕与文字混合

    我尝试使用以下方法将字幕与纯文本分开two空格处 我的 Rmd 文档的第一部分如下所示 title Script de Limpieza errores de digitalizacion y division de base madre
  • 弱引用而不是 getActivity() (Android 避免内存泄漏)?

    为了避免内存泄漏 我编写了以下方法 该方法将在活动中使用 并且主要在片段中使用 使用继承 该方法应该允许我永远不会通过调用直接引用该活动 this or getActivity 方法是 private WeakReference
  • 以另一个用户身份运行任务

    使用卡皮斯特拉诺v3 我怎样才能跑all远程任务通过su作为另一个用户 我在官方文档中找不到任何内容 http capistranorb com http capistranorb com 对于我的用例 每个虚拟主机都有一个 SSH 用户和
  • 创建事件观察者以获得焦点?

    原型中是否可以有焦点事件冒泡 我试图避免在每个输入元素上分配一个观察者
  • python和node base64解码的区别

    我对这个base64解码问题感到困惑 似乎python和node js的做法不同 我相信 Node 正确地做到了这一点 有人能帮忙指出为什么 python 在这里不起作用吗 谢谢 Node gt console log Buffer fro
  • 构建进度条而不启动新线程

    我需要在 a 中添加一个进度条JFrame但我想更新这个栏而不生成新线程 例如摇摆工人后台更新栏 有没有办法更新当前线程 主 JFrame 的当前线程 中的进度条 详细信息 我有第一类 ChooseGUI 它扩展 JFrame 并调用第二类
  • MongoDB 全文搜索 + 部分单词匹配的解决方法

    由于使用 mongodb 全文搜索无法通过 blue 一词找到 blueberry 因此我想帮助我的用户将 blue 一词补全为 blueberry 为此 是否可以查询 mongodb 全文索引中的所有单词 gt 我可以使用这些单词作为建议
  • 如何使用 Requests 从 Github 下载并写入文件

    假设 github 存储库中有一个文件 https github com someguy brilliant blob master somefile txt https github com someguy brilliant blob
  • 完全禁用 Backbone.js 哈希,但将 PushState 保留在历史记录中?

    我想使用 Backbone 出色的路由系统 但我不想为旧版浏览器提供任何形式的哈希支持 使用时是否可以完全禁用哈希pushState in Backbone History 它没有记录在backbone js 页面上 但我在带注释的源代码中
  • 离子文件下载不起作用

    我正在构建一个用于壁纸的离子应用程序 在应用程序中 显示了存储在 www assets img 中的图像 我在下面构建了 2 个按钮 用于将显示的图像下载和检索到移动设备内存 当我单击下载按钮时 会显示一个对话框 显示 下载成功 Pug j
  • 构建过程中出现“找不到类型或命名空间名称”错误

    我正在 C Windows 应用程序中使用 我已经使用 TempProWin gt 对于 Windows 应用程序项目TempProApp gt 用于数据库和使用 CSLA 方法的类文件等功能 在 TempProWin frmLogin c
  • TCP/IP IOCP 接收到的数据有时会损坏 - Windows 上的 Visual C++

    我正在编写一个简单的测试 ICOP 客户端和服务器 以确保我正确使用 API 并且服务器正确接收客户端发送的数据 我已经包含了这个问题的所有代码 这就是我遇到一些问题的地方 接收缓冲区内的数据有时似乎已损坏 损坏是因为有时缓冲区内的数据块可