Tcp socket send() slow

Nhờ mọi người giúp mình vấn đề này với. Mình đang code thử một chương trình giao tiếp với thiết bị ngoại vi sử dụng tcp socket. App của mình đóng vai trò là server socket.
Khi nhận được data từ client gửi lên, mình sẽ gửi phản hồi lại cho client, sử dụng hàm:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

buffer length của mình (khoảng 4kB) > MTU (1500 byte) nên TCP chia nhỏ thành các packet nhỏ hơn. Mình dùng wireshark để bắt gói tin thì thấy thời gian gửi giữa các packet nhỏ cách nhau khoảng 0,3s. Nếu gửi số lượng lớn (khoảng vài chục Mb) thời gian gửi rất lâu ~ vài tiếng.
Nhờ mọi người giúp mình vấn đề này với T_T

P/s: Mình đã thử set lại socket option TCP_NODELAY mà vẫn không ăn thua.

EDIT:
Đây là file tcpdump của mình. Phần đánh dấu đỏ là thời gian gửi tcp frame trong một lần gọi hàm send() của mình. Cách nhau ~0.2s.

Đây là code của mình:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

void createServerSocket();
void acceptConnect(int serverSock);
void receiveDataFromSocket(int socket);
void sendDataOverSocket(int clientSock);

void createServerSocket() {
    int serverSockFd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    std::string address = "fd53:7cb8:383:5::73";
    int port = 42519;

    if (serverSockFd < 0)
    {
        printf("Create Server socket fail");
        return;
    }
    struct sockaddr_in6 serverAddress;
    (void)memset(&serverAddress, 0, sizeof(sockaddr_in6));
    serverAddress.sin6_family = AF_INET6;
    serverAddress.sin6_port = htons(port);
    int result = inet_pton(AF_INET6, address.c_str(), &serverAddress.sin6_addr);

    if (result <= 0)
    {
        printf("inet_pton() failed portnumber: %d, address: %s \n", port, address.c_str());
        return;
    }

    // setting socket options
    int flag = 1;

    if(setsockopt(serverSockFd,IPPROTO_TCP,TCP_QUICKACK  ,(char *)&flag,sizeof(flag)) == -1)
    {
        printf("setsockopt TCP_QUICKACK failed for server socket on address %s \n", address.c_str());
    }
    if(setsockopt(serverSockFd,IPPROTO_TCP,TCP_CORK,(char *)&flag,sizeof(flag)) == -1)
    {
        printf("setsockopt TCP_CORK failed for server socket on address %s \n", address.c_str());
    }
    if(setsockopt(serverSockFd,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(flag)) == -1)
    {
        printf("setsockopt TCP_NODELAY failed for server socket on address %s \n", address.c_str());
    }

    result = bind(serverSockFd, (struct sockaddr*)&serverAddress, sizeof(sockaddr_in6));

    if (result != 0)
    {
        printf("bind() failed portnumber: %d, address: %s \n", port, address.c_str());
        return ;
    }

    result = listen(serverSockFd, 10);
    if (result != 0) {
        printf("listen() failed portnumber: %d, address: %s \n", port, address.c_str());
        return ;
    }

    acceptConnect(serverSockFd);
}

void acceptConnect(int serverSock)
{
    struct sockaddr_in6 clientAddress;
    socklen_t len = sizeof(sockaddr_in6);
    memset(&clientAddress, 0, sizeof(sockaddr_in6));

    const int clientSocket = accept(serverSock, (struct sockaddr*)&clientAddress, &len);

    if(clientSocket >= 0) {
        char str_addr[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, &(clientAddress.sin6_addr),
                  str_addr, sizeof(str_addr));
        printf("New connection from: %s:%d ...\n", str_addr, ntohs(clientAddress.sin6_port));
        receiveDataFromSocket(clientSocket);
    }
}

void receiveDataFromSocket(int socket)
{
    int SOCKET_BUFFER_MAX_SIZE = 8*1024;

    char buffer[SOCKET_BUFFER_MAX_SIZE];
    memset(buffer, '\0', SOCKET_BUFFER_MAX_SIZE);
    //Receive data from sock
    while (true) {
        int dataLen = recv(socket, buffer, SOCKET_BUFFER_MAX_SIZE, 0);
        printf("Receive data from socket: %d, msgLength = %d\n", socket, dataLen);
        sendDataOverSocket(socket);
    }
}

void sendDataOverSocket(int clientSock)
{
    int dataLen = 4*1024 + 7;
    char *buf = new char[dataLen];
    memset(buf, 'a', 4*1024 + 7);
    int ret;
    ret = send(clientSock, buf, dataLen, 0);

    if (ret <= 0) {
        printf("ERROR Send message over socket");
        return;
    }

    int error_code;
    socklen_t error_code_size = sizeof(sockaddr_in6);
    getsockopt(clientSock, SOL_SOCKET, SO_ERROR, &error_code, &error_code_size);
    printf("Error code size: %d, error code: %d\n", error_code_size, error_code);
}

http://www.stuartcheshire.org/papers/nagledelayedack/

Nagle chờ ACK gói trước rồi mới gửi một gói nhỏ. Delayed ACK sẽ gửi ACK kèm payload.

Có thể do Delayed ACK (chờ payload hoặc một gói nữa đến để ACK trong <=500ms) với Nagle xung nhau. Nên dùng TCP_QUICKACK (bên nhận) hơn là TCP_NODELAY.

Còn có thể do có tường lửa hay anti đứng giữa.

3 Likes

Mình đã set tất cả các cờ có thể: TCP_NODELAY, TCP_QUICKACK, TCP_CORK. Nhưng vẫn ko ăn thua +_+. P/s: mình đã post code của mình lên. Bạn xem giúp mình với

TCP_CORK còn ác hơn Nagle (default) nữa. Còn TCP_QUICKACK là dành cho bên nhận.

1 Like

ác hơn ý là gì hả bạn? Mình search around thì thấy cả TCP_NODELAY lẫn TCP_CORK đều disable nagle mà

TCP_CORK sẽ đợi đến khi đầy MTU hay timeout trước khi gửi 1 gói.
Nagle đợi ACK hay timeout mới gửi tiếp.

3 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?