【问题标题】:How do you avoid data loss when peer resets TCP connection in winsock 2?winsock 2中peer重置TCP连接时如何避免数据丢失?
【发布时间】:2012-03-18 03:34:28
【问题描述】:

我正在编写一个 UPnP 客户端,而我的一个测试路由器总是“猛击”连接关闭,而不是在发送响应后进行正常的关闭发送。这导致我的 recv 调用无法获取数据。

我知道数据在那里,因为我可以在数据包嗅探器中看到它。

如果我的代码在连接重置之前以足够快的速度提前接收数据,那么我就会得到数据。在许多情况下,对等方在我可以接收连接之前重置连接,导致没有数据复制到我的接收缓冲区,并且接收到 WSAECONNRESET 错误。

有什么想法可以解决我的问题,以容忍 netgear 路由器中编写错误的 UPnP 实现?

我尝试使用 WSAEventSelect 并使读取异步,这似乎有所帮助,但并不总是有效。

// Object that manages reliably sending and receiving, even if the
// peer does stupid things like slamming connection shut at EOF
class Transceiver
{
    SOCKET sock;
    AutoWSACloseEvent syncEvent;

    // Buffer pool
    template<size_t bufferSize>
    struct Buffer
        : public SLIST_ENTRY
        , public AutoPool<Buffer<bufferSize>, false>
    {
        char data[bufferSize];

        size_t size() const
        {
            return bufferSize;
        }
    };

public:
    Transceiver() : sock(INVALID_SOCKET)
    {
    }
    int Init(SOCKET sock);
    int SendAll(const std::string &data);
    int ReceiveAll(std::string &data);
};



int UpnpNat::Transceiver::Init(SOCKET sock)
{
    int err;

    this->sock = sock;
    syncEvent = WSACreateEvent();
    if (!syncEvent)
        return ErrorHook(WSAGetLastError());
    err = WSAEventSelect(sock, syncEvent, FD_READ | FD_CLOSE);
    if (err == SOCKET_ERROR)
        return ErrorHook(WSAGetLastError());

    return NO_ERROR;
}

int UpnpNat::Transceiver::SendAll(const std::string &request)
{
    for (int ofs = 0; ofs < (int)request.length(); )
    {
        auto xferSize = send(sock, &request[ofs], (int)request.length() - ofs, 0);
        if (xferSize == SOCKET_ERROR)
            return ErrorHook(WSAGetLastError());
        ofs += xferSize;
    }

    return NO_ERROR;
}

int UpnpNat::Transceiver::ReceiveAll(std::string &response)
{
    int err = NO_ERROR;
    int xferSize;

    auto responseBuf = MakeAutoDelete(new Buffer<16384>());
    if (!responseBuf)
        return ErrorHook(ERROR_OUTOFMEMORY);

    bool needRecvWait = false;

    for (;;)
    {
        if (needRecvWait)
        {
            needRecvWait = false;
            if (WSAWaitForMultipleEvents(1, syncEvent.Storage(),
                    FALSE, 30000, FALSE) != WAIT_OBJECT_0)
            {
                err = WSAETIMEDOUT;
                return ErrorHook(err);
            }

            WSANETWORKEVENTS wsane;
            ZeroInit(&wsane);
            err = WSAEnumNetworkEvents(sock, syncEvent, &wsane);
            if (err == SOCKET_ERROR)
                return ErrorHook(WSAGetLastError());

            if (wsane.lNetworkEvents & FD_CLOSE)
            {
                err = wsane.iErrorCode[FD_CLOSE_BIT];
                break;
            }

            if ((wsane.lNetworkEvents & FD_READ) == 0)
            {
                continue;
            }

            if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR)
                return ErrorHook(wsane.iErrorCode[FD_READ_BIT]);
        }

        xferSize = recv(sock, responseBuf->data, (int)responseBuf->size(), 
                MSG_PARTIAL);
        if (xferSize == SOCKET_ERROR)
        {
            err = WSAGetLastError();
            if (err == WSAEWOULDBLOCK)
            {
                needRecvWait = true;
                continue;
            }
            // Workaround for crap routers that slam connection shut at EOF
            if (err == WSAECONNRESET && response.length() > 0)
                return NO_ERROR;

            return ErrorHook(WSAGetLastError());
        }
        if (xferSize <= 0)
            break;

        response.append(responseBuf->data, 0, (std::string::size_type)xferSize);
    }

    return ErrorHook(err);
}

【问题讨论】:

    标签: tcp router upnp winsock2


    【解决方案1】:

    如果“slams”是指“发送 RST”,那么您无能为力。如果收到 RST,TCP 堆栈有义务中止连接并丢弃所有未决数据。

    【讨论】:

    • 是的,我的意思是一个 RST 数据包。我通过尽快发布异步接收来缓解它,所以我现在通常会获取数据,但你最终是正确的,(Windows)TCP 堆栈在收到 RST 时确实丢弃了缓冲的数据。
    • @doug65536 这是一种奇怪的表达方式。这句话总是正确的。
    猜你喜欢
    • 2017-09-08
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 2020-09-23
    • 2011-06-13
    • 2022-06-13
    • 1970-01-01
    相关资源
    最近更新 更多