并非所有数据都通过Socket传输

时间:2017-11-16 17:05:30

标签: c++ sockets

我无法通过我的设置发送过大的数据包(当前发送到127.0.0.1),大约30kB时此功能开始失败。为了测试,我有一个应用程序只启动一个Receiver和一个Sender,启动两个线程,一个用于发送,一个用于接收,当两个线程完成时,比较发送字符串是否与接收到的字符串相同。

void SenderThread(int count)
{
    messageOut = "";
    messageOut.append(count, 'A');

    sender->sendData(messageOut);
}

void ReceivingThread()
{
    receiver->ReceiveData(message);
}

main()
{
    receiver = new utility::Receiver();
    sender = new utility::Sender();

    receiver->startSocket(9000);
    sender->connectToSocket("127.0.0.1", 9000);
    receiver->accept();

    for (int count = 100; count < 1024 * 1024; count += 100)
    {
        std::thread sendThread(SenderThread, count);
        std::thread recvThread(ReceivingThread);

        sendThread.join();
        recvThread.join();

        printf("Sent data of length %d ", messageOut.length());
        if (message == messageOut)
            printf("successfully.\n");
        else
        {
            printf("not successfully.\n");
            printf("Length of original message: %d, Length of received message: %d.\n", messageOut.length(), message.length());
            break;
        }
    }
    delete receiver;
    delete sender;
}

我的发送套接字代码如下:

bool utility::Sender::sendData(const std::string & message)
{
    int numBytes = 0;
    int totalSent = 0;

    // Break condition: send() fails, or whole message was transfered

    while (totalSent < message.length() && send(message.substr(totalSent).c_str(), message.length() - totalSent, numBytes))
    {
        totalSent += numBytes;
    }

    return false;
}

bool utility::Sender::send(const char* pBuffer, int32_t lengthOfBuffer, int32_t &numBytes)
{
    numBytes = ::send(connectSocket, pBuffer, lengthOfBuffer, 0);

    if (numBytes == SOCKET_ERROR)
        return false;

    return true;
}

接收方:

bool utility::Receiver::ReceiveData(std::string& message)
{
    int32_t numBytes = 0;

    char data[defaultBufferLength];

    // Set to blocking for the first data package

    u_long iMode = 0;
    ioctlsocket(tcpSocket, FIONBIO, &iMode);

    bool success = receive(data, defaultBufferLength, numBytes);
    message = std::string(data, numBytes);

    // Set to non-blocking for the rest of the journey
    iMode = 1;
    ioctlsocket(tcpSocket, FIONBIO, &iMode);

    while (numBytes == defaultBufferLength && receive(data, defaultBufferLength, numBytes))
    {
        message.append(data, numBytes);
    }

    return success;
}

bool utility::Receiver::receive(char* pBuffer, int32_t lengthOfBuffer, int32_t& numBytes)
{
    int32_t flags = 0;

    numBytes = recv(tcpSocket, pBuffer, lengthOfBuffer, flags);
    if (numBytes == -1)
    {
        numBytes = 0;
        if (errno == EAGAIN || errno == EWOULDBLOCK)
            return false;
        else
            close();
    }

    return true;
}

我得到的输出是

Sent data of length 39200 successfully.
Sent data of length 39300 successfully.
Sent data of length 39400 successfully.
Sent data of length 39500 successfully.
Sent data of length 39600 successfully.
Sent data of length 39700 successfully.
Sent data of length 39800 successfully.
Sent data of length 39900 successfully.
Sent data of length 40000 successfully.
Sent data of length 40100 successfully.
Sent data of length 40200 successfully.
Sent data of length 40300 successfully.
Sent data of length 40400 successfully.
Sent data of length 40500 not successfully.
Length of original message: 40500, Length of received message: 29200.

最令人恼火的事情,可能是其原因,是:: send(...)。我可以给它2 MB的char *,它只会一举发送它(但接收器失败了)。我该怎么办?

2 个答案:

答案 0 :(得分:3)

TCP是一种面向字节的协议,而不是面向消息的协议。

send不会创建消息。 recv未收到消息。它们处理字节块,并且可以在网络层组合多个send调用(为了提高效率)或将其分解为多个TCP数据包。实际上,即使您关闭Nagle算法,如果帧在物理层丢失并且TCP必须重试传输,则重传将包括随后添加到缓冲区的尽可能多的数据,因为它可以适合于传出数据报。

因此,您无法依赖发送呼叫和recv呼叫之间的任何特定映射。唯一的保证是字节按照它们发送的顺序传送到您的套接字。如果边界很重要,您必须自己创建它们。长度前缀与TCP结合使用很常见,特殊的帧序列则不那么容易。

你已经有了一个重新组合消息的循环......但是当你看到EAGAIN / EWOULDBLOCK或部分填充的缓冲区时,你就会突破循环,并继续处理。这是一个问题,因为那时你只有一个部分信息。您需要一种方法来延迟处理,直到您有完整的消息。

答案 1 :(得分:0)

添加到ben-voigt答案,您需要为套接字创建更高级别的消息系统,这样您就可以向服务器发送消息大小,并在套接字接收方法上创建一个会话或缓冲存储,您追加接收数据直到消息大小与收到的总数据相匹配,一旦满足该要求,您就可以处理数据