วิธีส่งแพ็กเก็ต TCP 2 อันใน c ++

เป็นไปได้ไหมที่จะส่งแพ็กเก็ต TCP 2 ชุดติดต่อกันดังที่เห็นในภาพนี้: ป้อนคำอธิบายรูปภาพที่นี่

ขณะนี้ฉันได้ตั้งค่า TCP_NODELAY เป็นจริงและ SO_SNDBUF เป็น 0 ฉันยังเรียก send ในโปรแกรมของฉัน 2x นี่คือผลลัพธ์ที่ฉันได้รับ:

ป้อนคำอธิบายรูปภาพที่นี่

ปัญหาหลักที่นี่คือความล่าช้าในการตอบรับซึ่งทำให้ประสิทธิภาพเครือข่ายช้าลงในภาพหน้าจอที่ 2

รหัสสำหรับเซิร์ฟเวอร์:

DWORD WINAPI ServerHandler(void *lp){
    //The port you want the server to listen on
    int host_port = 1852;

    //Initialize socket support WINDOWS ONLY!
    unsigned short wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD( 2, 2 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 || ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )) 
    {
        printf("Could not find useable sock dll %d\n",WSAGetLastError());
        return 0;
    }

    //Initialize sockets and set any options
    int hsock;
    BOOL bOptVal = true;
    int bOptLen = sizeof (BOOL);
    int iResult = 0;

    hsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(hsock == INVALID_SOCKET)
    {
        printf("Error initializing socket %d\n",WSAGetLastError());
        return 0;
    }

    iResult = setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char *) &bOptVal, bOptLen);
    if (iResult == SOCKET_ERROR)
        printf("setsockopt for SO_REUSEADDR failed with error: %d\n", WSAGetLastError());
    else
        printf("Set SO_REUSEADDR: ON\n");

    iResult = setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &bOptVal, bOptLen);
    if (iResult == SOCKET_ERROR)
        printf("setsockopt for SO_KEEPALIVE failed with error: %d\n", WSAGetLastError());
    else
        printf("Set SO_KEEPALIVE: ON\n");

    //Bind and listen
    struct sockaddr_in my_addr;

    my_addr.sin_family = AF_INET ;
    my_addr.sin_port = htons(host_port);

    memset(&(my_addr.sin_zero), 0, 8);
    my_addr.sin_addr.s_addr = INADDR_ANY ;

    if( bind( hsock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == SOCKET_ERROR )
    {
        printf("Error binding to socket, make sure nothing else is listening on this port %d\n",WSAGetLastError());
        closesocket(hsock);
        return 0;
    }
    if( listen( hsock, MAXCONN) == SOCKET_ERROR )
    {
        printf("Error listening %d\n",WSAGetLastError());
        closesocket(hsock);
        return 0;
    }

    //Now lets to the server stuff

    int* csock;
    sockaddr_in sadr;
    int addr_size = sizeof(SOCKADDR);
    
    printf("waiting for a connection\n");

    while(true)
    {            
        csock = (int*)malloc(sizeof(int));
        if((*csock = accept( hsock, (SOCKADDR*)&sadr, &addr_size))!= INVALID_SOCKET )
        {
            printf("Received connection from %s, %u @ socket %d\n", inet_ntoa(sadr.sin_addr), sadr.sin_port, *csock);
            
            BOOL bOptVal = true;            
            int iResult = setsockopt(*csock, SOL_SOCKET, TCP_NODELAY, (char *) &bOptVal, sizeof(bOptVal));
            if (iResult == SOCKET_ERROR)
                printf("setsockopt for TCP_NODELAY failed with error: %d\n", WSAGetLastError());
            else
                printf("Set TCP_NODELAY: TRUE\n");

            int sendBuf = 0;
            iResult = setsockopt(*csock, SOL_SOCKET, SO_SNDBUF, (char *) &sendBuf, sizeof(sendBuf));
            if (iResult == SOCKET_ERROR)
                printf("setsockopt for SO_SNDBUF failed with error: %d\n", WSAGetLastError());
            else
                printf("Setsockopt for SO_SNDBUF set to 0\n");


            CreateThread(0,0,&SocketHandler, (void*)csock , 0,0);
        }
        else
        {
            printf("Error accepting %d\n",WSAGetLastError());
        }
    }
    WSACleanup();
}

รหัสที่ฉันใช้ในการส่งข้อมูล:

int send_TCP_2(int cs, char responseLength[], char data[], int respond_length, int data_length)
{   
    int size = respond_length + data_length;
    int index = 0;

    // combined 10 byte response with data as 1 packet
    std::vector<char> packet(size);

    for(int i=0; i<respond_length; i++)
    {
        packet[index] = responseLength[i];
        index++;
    }

    for(int i=0; i<data_length; i++)
    {
        packet[index] = data[i];
        index++;
    }
    
    int status;
    char *data_ptr = &packet[0];
    while(size > 0)
    {
        status = send(cs, data_ptr, size, 0);
        if(status > 0)
        {
            data_ptr += status;
            size -= status;
        }
        else if (status == SOCKET_ERROR)
        {
            int error_code = WSAGetLastError();
            printf("send_TCP_2 failed with error code: %d\n", error_code);
            return 0;   // send failed
        }
    }
    return 1;   // send successful  
}

ป้อนคำอธิบายรูปภาพที่นี่

ฉันได้แนบภาพหน้าจอเมื่อฉันไม่ได้ปิดการใช้งาน Nagle และไม่ได้แตะ SO_SNDBUF


person user990639    schedule 15.03.2012    source แหล่งที่มา
comment
อะไรทำให้คุณคิดว่าขนาดของแพ็กเก็ต (ไม่ใช่อย่างอื่น) กำลังส่งผลต่อประสิทธิภาพของเครือข่าย ภาพหน้าจอทั้งสองจากคอมพิวเตอร์คู่เดียวกันบนเครือข่ายเดียวกันหรือไม่   -  person Adam Liss    schedule 15.03.2012
comment
TCP_NODELAY น่าจะเพียงพอแล้ว   -  person Nikolai Fetissov    schedule 15.03.2012
comment
@AdamLiss โอ้ไม่ ฉันไม่ได้บอกว่าขนาดของแพ็กเก็ตส่งผลกระทบต่อเครือข่าย ฉันกำลังพยายามดึง 2 แพ็กเก็ตออกมาติดต่อกันเพื่อรับ ACK ทันที วิธีนี้จะลบความล่าช้า 200ms ที่เห็นในภาพที่ 2 (0.011 ถึง 0.210)   -  person user990639    schedule 15.03.2012
comment
คุณคิดว่าการล่าช้า ack จะทำให้ประสิทธิภาพเครือข่ายช้าลงในทางใด สแต็ก TCP ที่ส่งไม่ควรรอรับ ack ที่ล่าช้าก่อนที่จะส่งคำสั่งถัดไป   -  person Kaz    schedule 15.03.2012
comment
TCP เป็นโปรโตคอลหน้าต่างแบบเลื่อน ไม่ใช่โปรโตคอลแบบหยุดและรอ   -  person Kaz    schedule 15.03.2012
comment
@NikolaiNFetissov ฉันได้เปิดใช้งาน TCP_NODELAY ในซ็อกเก็ตไคลเอนต์แล้ว   -  person user990639    schedule 15.03.2012
comment
นอกจากนี้ จาก man 7 socket บน Linux: `SO_SNDBUF ตั้งค่าหรือรับบัฟเฟอร์การส่งซ็อกเก็ตสูงสุดเป็นไบต์ เคอร์เนลจะเพิ่มค่านี้เป็นสองเท่า (เพื่อให้มีพื้นที่สำหรับค่าใช้จ่ายในการทำบัญชี) เมื่อตั้งค่าโดยใช้ setsockopt(2) และค่าสองเท่านี้จะถูกส่งกลับโดย getsockopt(2) ค่าดีฟอลต์ถูกกำหนดโดยไฟล์ /proc/sys/net/core/wmem_default และค่าสูงสุดที่อนุญาตถูกกำหนดโดยไฟล์ /proc/sys/net/core/wmem_max ค่าต่ำสุด (สองเท่า) สำหรับตัวเลือกนี้คือ 2048   -  person Kaz    schedule 15.03.2012
comment
เช่น. การตั้งค่า SO_SNDBUF เป็น 0 อาจใช้ไม่ได้ทุกที่ นั่นเป็นเอกสารเกี่ยวกับระบบปฏิบัติการที่คุณใช้อยู่หรือไม่?   -  person Kaz    schedule 15.03.2012
comment
@Kaz ฉันไม่แน่ใจเกี่ยวกับ TCP มากเกินไป แต่เหตุใดเซิร์ฟเวอร์จึงส่งแพ็กเก็ตถัดไปเกือบจะในทันที (เวลา 0.221) หลังจากที่ไคลเอนต์ส่ง ACK (เวลา 0.210)   -  person user990639    schedule 15.03.2012
comment
ให้เราสนทนาต่อในการแชท   -  person user990639    schedule 15.03.2012
comment
อย่างที่คนอื่นๆ พูดไปแล้ว TCP_NODELAY ควรเป็นสิ่งที่คุณกำลังมองหาเพื่อให้สามารถส่งข้อมูลกลับไปกลับมาได้ หากไม่ได้ผลสำหรับคุณคุณอาจทำผิด เราขอดูโค้ดหน่อยได้ไหม?   -  person Guy Sirton    schedule 15.03.2012
comment
@GuySirton ฉันได้แนบรหัสเซิร์ฟเวอร์ที่แสดงตำแหน่งที่ฉันตั้งค่า TCP_NODELAY และ SO_SNDBUF   -  person user990639    schedule 15.03.2012
comment
ลบการเปลี่ยนแปลงการตั้งค่าทั้งหมดของคุณ จากนั้นให้ทดสอบอีกครั้ง หากคุณมีปัญหาด้านประสิทธิภาพ ให้โพสต์การติดตามเพื่อให้เราทราบว่ามีอะไรผิดปกติ แต่การมองดูสแต็กในแง่ร้าย คุณกำลังสร้างปัญหาที่คุณเห็นและทำลายความหวังในการค้นหาและแก้ไขปัญหาเหล่านั้น (เป็นไปได้มากว่าปัญหาที่คุณพบในขณะนี้มีสาเหตุมาจากการเขียนเพียงเล็กน้อย การปิดใช้งาน Nagle และบัฟเฟอร์ขนาดเล็ก ทั้งหมดนี้เป็นปัญหาที่คุณสร้างขึ้น)   -  person David Schwartz    schedule 15.03.2012
comment
@DavidSchwartz ฉันเข้าใจปัญหาที่เกิดขึ้นเมื่อฉันส่งขนาดเล็กและปิดการใช้งาน Nagle และแม้แต่ทำให้บัฟเฟอร์การส่งเป็น 0 อย่างไรก็ตาม ฉันสนใจว่าเซิร์ฟเวอร์อื่นสามารถส่งแพ็กเก็ตที่มีขนาดเล็กกว่าและในเวลาที่เร็วกว่าได้อย่างไรเมื่อเทียบกับของฉัน . เพราะจากที่เห็นในภาพแรกเซิร์ฟเวอร์สามารถส่งแพ็กเก็ตแยกกันขนาด 206 ไบต์ได้ ฉันได้ลองปิดการใช้งาน Nagle แล้ว และสิ่งที่ฉันเห็นคือบางแพ็กเก็ตยังคงรวมตัวกันเป็นแพ็กเก็ตขนาดที่ใหญ่ขึ้น   -  person user990639    schedule 15.03.2012
comment
@ user990639: คุณจะไม่สามารถทราบการเปลี่ยนแปลงการตั้งค่าทั้งหมดที่คุณได้ทำซึ่งทำให้ประสิทธิภาพการทำงานแย่ลง ทำทุกอย่างถูกต้อง จากนั้นแก้ไขปัญหาด้านประสิทธิภาพการทำงานที่คุณพบ ทุกสิ่งที่คุณทำจนถึงตอนนี้ทำให้ประสิทธิภาพแย่ลง ดังนั้นจึงไม่น่าแปลกใจที่ประสิทธิภาพของคุณจะแย่ เปิดใช้งาน Nagle ส่งข้อมูลให้ได้มากที่สุดในการโทรแต่ละครั้ง อย่าย่อขนาดบัฟเฟอร์ จากนั้น หากคุณประสบปัญหาด้านประสิทธิภาพ เราสามารถช่วยคุณค้นหาและแก้ไขได้   -  person David Schwartz    schedule 15.03.2012


คำตอบ (3)


ปัญหาหลักที่นี่คือความล่าช้าในการตอบรับซึ่งทำให้ประสิทธิภาพเครือข่ายช้าลงในภาพหน้าจอที่ 2

ไม่มันจะไม่ คุณคิดผิดเกี่ยวกับเรื่องนั้น คุณไม่สามารถควบคุมการแบ่งแพ็กเก็ต TCP หรือการแบ่งส่วนได้ และคุณไม่จำเป็นต้องใช้มัน TCP เป็นโปรโตคอลการถ่ายโอนสตรีมที่ได้รับการปรับให้เหมาะสมที่สุดซึ่งพัฒนาขึ้นมากว่า 30 ปี

person user207421    schedule 15.03.2012
comment
+1,000 คุณกำลังพยายามซ่อมแซมบางสิ่งที่ไม่เสียหาย อย่างดีที่สุด คุณจะไม่ทำลายมัน (อย่างไรก็ตาม มีความเป็นไปได้มากกว่าที่คุณ จะ ทำลายมันด้วยการบังคับให้เขียนเพียงเล็กน้อย) - person David Schwartz; 15.03.2012
comment
TCP ไม่สมบูรณ์แบบ TCP คือค้อนที่เรามี ดังนั้นเราจึงตอกตะปูสกรูเข้ากับผนัง สำหรับการสื่อสารคำขอ/ตอบกลับ TCP มีข้อบกพร่องที่น่าสังเกต โดยเฉพาะอย่างยิ่งในส่วนที่เกี่ยวกับหน้าต่าง มีการพูดคุยกันมากมายบนเว็บเกี่ยวกับข้อเสียของโครงร่างหน้าต่าง TCP ร่วมกับแนวโน้มของ HTTP สมัยใหม่ในการเปิดการเชื่อมต่อ TCP จำนวนมาก - person Dave Dopson; 18.05.2012

TCP_NODELAY option set to TRUE ควรแก้ไขปัญหาล่าช้า_ACK ครั้งหนึ่งฉันต้องส่งแพ็กเก็ตเครือข่ายสองครั้ง แต่ฉันทำได้ใน ETHERNET (driver) layer และมันใช้งานได้ที่นั่น (delayed_ACK เกิดจากอีกฝ่าย) แต่ในเลเยอร์นี้ (เลเยอร์ SOCKET) คุณไม่สามารถทำสิ่งนั้นได้ นอกจากนี้อย่าตั้งค่า SO_SNDBUF เป็น 0...

person Malkocoglu    schedule 15.03.2012

ขอบคุณสำหรับคำแนะนำทั้งหมด! การตั้งค่า TCP_NODELAY เป็นจริงจะทำงานตามที่คนส่วนใหญ่กล่าวถึง ฉันทำผิดพลาดอย่างโง่เขลาใน setsockopt!

ฉันควรจะใส่ IPPROTO_TCP แทน SOL_SOCKET

person user990639    schedule 19.03.2012