如何在服务器和客户端中启用 TCP_NODELAY 选项?

时间:2021-01-20 11:42:51

标签: c++ windows tcp

我正在 Windows 10 中使用 TCP 实现通信系统(tx、rx)。我的问题是当 tx 向 rx 发送消息时,消息被延迟接收。当 tx 发送多条消息时,rx 仅在发送多条消息后才开始接收。我的猜测是 tx 一直等到它的缓冲区填满,然后才开始发送消息(在我的平台缓冲区长度为 512)。

如下图所示,在开始接收之前,出现这个错误:

ERROR receiving TCP, error #: 10014

Here is a picture of the tx and rx:

我试图通过启用 TCP_NODELAY 选项来解决这个问题,以便立即发送消息(但没有成功)。然而,我对 TCP 的了解很浅,我不确定我是否做得对。

这是我的问题:

  1. 延迟发送(或接收)是否与 TCP 的 TCP_NODELAY 选项有关?

  2. 如果是,我是否正确启用了 TCP_NODELAY 如下:

    int 是 = 1;

    int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int));    // 1 - on, 0 - off
    
    if (resultt < 0)
        std::cerr << "Error: TCP_NODELAY";
    
  3. 我是否将 TCP_NODELAY 选项放在服务器和客户端代码中的正确位置?

这是我的客户代码:

size_t IPTunnel::ipTunnelSend_tcp(size_t process) {

    size_t remaining = process;
    size_t result{ 0 };
    size_t sent{ 0 };
    while (remaining > 0) {
        result = send(clientSocket[0], &(ip_tunnel_buffer[0]) + sent, (int) remaining, 0);
        if (result >= 0) {
            remaining -= result;
            sent += result;
        }
        else { 
            if (getLogValue()) {
                if ( result == 0) std::cerr << "No data send through TCP" << std::endl;
                else std::cerr << "ERROR sending TCP, error #: " << WSAGetLastError() << std::endl;
            }
            break;
        }
    }
    return sent;
}

和:

bool IPTunnel::client_tcp() {

    // STEP 1 - Initialize Winsock
    WSADATA wsData;                         
    WORD ver = MAKEWORD(2, 2);                  
    int wsResult = WSAStartup(ver, &wsData);    
    if (wsResult != 0)                          
    {
        std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
        return false;
    }

    // STEP 2 - Create a socket         
    SOCKET s = socket(AF_INET, SOCK_STREAM, 0);

    int yes = 1;
    int resultt = setsockopt(clientSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, 
    sizeof(int));
    if (resultt < 0)
        std::cerr << "Error: TCP_NODELAY";


    clientSocket.push_back(s);
        
    if (clientSocket[0] == INVALID_SOCKET)
    {
        std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
        WSACleanup();
        return false;
    }

    // STEP 3 - Connect to the server
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons((u_short)localMachinePort);
    inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);

    int connResult{ -2 };
    while (connResult != 0 || numberOfTrials == 0) {
        connResult = connect(clientSocket[0], (sockaddr*)& hint, sizeof(hint));
        if (connResult == SOCKET_ERROR)
        {
            std::cerr << "Can't connect to server, Err #" << WSAGetLastError() << std::endl;
            std::cerr << "Waiting " << timeIntervalSeconds << " seconds." << std::endl;
            Sleep(timeIntervalSeconds * 1000);
        }

        if (--numberOfTrials == 0) {
            std::cerr << "Reached maximum number of attempts." << std::endl;
        }
    }
    std::cerr << "Connected!\n";
    return true;
}

这是我的服务器代码:

size_t IPTunnel::ipTunnelRecv_tcp(size_t space) {
    char* recv_buffer = &ip_tunnel_buffer[0];
    size_t remaining{ 0 };
    if (outputSignals[0]->getValueType() == (signal_value_type::t_message))
    {
        remaining = ip_tunnel_buffer.size();
    }
    else
    {
        remaining = space * outputSignals[0]->valueTypeSizeOf();
    }
    size_t received{ 0 };
    while (remaining > 0) {
        auto aux{ 0 };
        aux = recv(serverSocket[1], recv_buffer + received, (int) remaining, 0);
        if (aux > 0) {
            received += aux;
            remaining -= received;
        }
        else {
            if (getLogValue()) {
                if (aux == 0) std::cerr << "No data received through TCP" << std::endl;
                else std::cerr << "ERROR receiving TCP, error #: " << WSAGetLastError() << std::endl;
            }
            break;
        }
    }
    return received;
}

和: bool IPTunnel::server_tcp() {

    // STEP 1 - Initialize Winsock

    WSADATA wsData;                             
    WORD ver = MAKEWORD(2, 2);                  
    int wsResult = WSAStartup(ver, &wsData);    
    if (wsResult != 0)                          
    {
        std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
        return false;
    }

    // STEP 2 - Create a socket                 

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    serverSocket.push_back(s);
    
    if (serverSocket[0] == INVALID_SOCKET)
    {
        std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
        WSACleanup();
        return false;
    }

    // STEP 3 - Bind the socket
    
    sockaddr_in hint;
    hint.sin_family = AF_INET; // AF_INET=2, IPv4
    inet_pton(AF_INET, localMachineIpAddress.data(), &hint.sin_addr.S_un.S_addr);
    hint.sin_port = ntohs((u_short)localMachinePort);
    
    char ipAddress[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &hint.sin_addr.S_un.S_addr, ipAddress, INET_ADDRSTRLEN);
    std::cerr << "The TCP server is running on IP address: " << ipAddress;
    std::cerr << " Port: " << htons(hint.sin_port) << std::endl;

    if (bind(serverSocket[0], (sockaddr*)& hint, sizeof(hint)) < 0) {
        std::cerr << "Bind failed with error code #" << WSAGetLastError() << std::endl;
        return false;
    }

    // STEP 4 - Listen on the socket for a client

    if (listen(serverSocket[0], SOMAXCONN) == -1) {
            std::cerr << "Listen error!" << std::endl;
            return false;
    }

    // STEP 5 - Accept a connection from a client
    sockaddr_in client;
    int clientSize = sizeof(client);

    s = accept(serverSocket[0], (sockaddr*) &client, &clientSize);
    serverSocket.push_back(s);

    // Provides information
    char host[NI_MAXHOST];
    char service[NI_MAXSERV];

    ZeroMemory(host, NI_MAXHOST);
    ZeroMemory(service, NI_MAXSERV);

    if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
    {
        std::cerr << host << " connected on port " << service << std::endl;
    }
    else
    {
        inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
        std::cerr << host << " connected on port " << ntohs(client.sin_port) << std::endl;
    }

    int yes = 1;
    int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int));    // 1 - on, 0 - off

    if (resultt < 0)
        std::cerr << "Error: TCP_NODELAY_Server";
    
    return true;
}

这里是发送消息(tx)的代码:

bool LoadFromCommandWindow::runBlock(void) {
    
    int space = outputSignals[0]->space();
    if (!space) return false;

    if (flag && flag1)
    {
        std::getline(std::cin, plainData);
    }
    
    if (plainData.length() == 0)
    {
        flag = false;
        return false;
    }
    else 
    {
        data = plainData.substr(k, paddedDataLength);
        if (data.length() != paddedDataLength) paddedData = padTo(data, paddedDataLength, '\0');
        else paddedData = data;

        outputSignals[0]->bufferPut((std::byte*) paddedData.c_str(), paddedDataLength); \\ This line puts the message into buffer
        k += data.length();
        if (k != plainData.length()) flag1 = false;
        else
        {
            flag1 = true;
            k = 0;
        }

    }

    return flag;
}

std::string LoadFromCommandWindow::padTo(std::string str, const size_t num, const char paddingChar = '\0')
{
    if (num > str.size())
        str.insert(str.size(), num - str.size(), paddingChar);
    return str;
}

这里是接收消息的代码(rx):

bool PrintData::runBlock(void) {

    int ready = inputSignals[0]->ready();
    int space = outputSignals[0]->space();
    int process = std::min(ready, space);

    if (process == 0) return false;

    inputSignals[0]->bufferGet((std::byte*)decryptedData, decryptedDataLength);

    decryptedDataLength = unpad(decryptedDataLength, decryptedData, '\0');

    for (size_t i = 0; i < decryptedDataLength; i++)
    {
        std::cout << decryptedData[i];
    }
    std::cout << std::endl;

    outputSignals[0]->bufferPut((std::byte*)decryptedData, decryptedDataLength);

    decryptedDataLength = 496;
    return true;
}

谢谢!

1 个答案:

答案 0 :(得分:0)

套接字应该是

expose 80

应该够了,但要看情况
防弹的东西是使用整个缓冲区传输,缓冲区大小为系统最大值
https://stackoverflow.com/a/64726689/7172363

相关问题