请问一个线程同步发送,一个线程同步接受,但是延迟很大,为什么?

hehefly1234 2022-12-31 10:55:49

局域网内两机收发包用boost::asio延迟在1ms左右。

如果只用RAW socket的同步收发,就是发一个包然后就收一个返回,延迟在300us左右。

但是,现在把收包和发包分在两个线程,延迟就到了好几毫秒,为什么?

#include <time.h>
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "../../test_socket1/test.h"

Record send_buffer[NUM*2];
Record recv_buffer[NUM*2];

//时间基线
static long long base = -1;

class Client
{
public:
    Client()
    {
        //SOCKET前的一些检查,检查协议库的版本,为了避免别的版本的socket,并且通过
        //WSAStartup启动对应的版本,WSAStartup的参数一个是版本信息,一个是一些详细的细节,注意高低位
        //WSAStartup与WSACleanup对应
        int err;
        WORD versionRequired;
        WSADATA wsaData;
        versionRequired = MAKEWORD(1, 1);
        err = WSAStartup(versionRequired, &wsaData);//协议库的版本信息

        //通过WSACleanup的返回值来确定socket协议是否启动
        if (!err)
        {
            printf("客户端嵌套字已经打开:%s:%d %d %d!\n", SERVER, PORT, NUM, sizeof(Record));
        }
        else
        {
            printf("客户端的嵌套字打开失败!\n");
            //return 0;//结束
        }
    }

    ~Client()
    {
        //关闭套接字
        closesocket(sock);
        //关闭服务
        WSACleanup();
    }

    void Connect(const char * ip, int port)
    {
        //创建socket这个关键词,这里想一下那个图形中的socket抽象层
        //注意socket这个函数,他三个参数定义了socket的所处的系统,socket的类型,以及一些其他信息
        sock = socket(AF_INET, SOCK_STREAM, 0);

        int opt_val;
        int opt_len = sizeof(opt_val);
        if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&opt_val, &opt_len) == SOCKET_ERROR)
            printf("ERROR: getsockopt SO_SNDBUF\n");
        else
            printf("getsockopt SO_SNDBUF:%d\n", opt_val);
        if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&opt_val, &opt_len) == SOCKET_ERROR)
            printf("ERROR: getsockopt SO_RCVBUF\n");
        else
            printf("getsockopt SO_RCVBUF:%d\n", opt_val);
        if (getsockopt(sock, SOL_SOCKET, SO_SNDLOWAT, (char *)&opt_val, &opt_len) == SOCKET_ERROR)
            printf("ERROR: getsockopt SO_SNDLOWAT\n");
        else
            printf("getsockopt SO_SNDLOWAT%d\n", opt_val);
        if (getsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, (char *)&opt_val, &opt_len) == SOCKET_ERROR)
            printf("ERROR: getsockopt SO_RCVLOWAT\n");
        else
            printf("getsockopt SO_RCVLOWAT%d\n", opt_val);

        int yes = 1;
        if (setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(char*)&yes,sizeof(int)) == SOCKET_ERROR)
            printf("ERROR: setsockopt TCP_NODELAY\n");
        else
            printf("setsockopt TCP_NODELAY%d\n", yes);

        //socket编程中,它定义了一个结构体SOCKADDR_IN来存计算机的一些信息,像socket的系统,
        //端口号,ip地址等信息,这里存储的是服务器端的计算机的信息
        SOCKADDR_IN clientsock_in;
        clientsock_in.sin_addr.S_un.S_addr = inet_addr(ip);
        clientsock_in.sin_family = AF_INET;
        clientsock_in.sin_port = htons(port);

        //前期定义了套接字,定义了服务器端的计算机的一些信息存储在clientsock_in中,
        //准备工作完成后,然后开始将这个套接字链接到远程的计算机
        //也就是第一次握手
        connect(sock, (SOCKADDR*)&clientsock_in, sizeof(SOCKADDR));//开始连接
    }

    void Start()
    {
        thd_send = std::thread(std::bind(&Client::Thread_Send, this));
        thd_recv = std::thread(std::bind(&Client::Thread_Recv, this));
    }

    void SendData(const char * data, int len)
    {
        memcpy(send_buffer + send_pos, data, len);
        send_pos++;
    }

    void OnRecv(const char * data, int len)
    {
        ((Record *)data)->acked = GetSysTimeMicros() - base; //((Record *)data)->ref * 102;
    }

private:
    void Thread_Recv()
    {
        printf("Thread_Recv: %lld\n", GetSysTimeMicros() - base);
        int    recv_pos = 0;
        while (true)
        {
            int n = recv(sock, (char *)(recv_buffer + recv_pos), sizeof(Record), 0);
            if (n < 0)
                break;
          //printf("%d-%d recv\n", recv_pos, recv_buffer[recv_pos].ref);
            OnRecv((char *)(recv_buffer + recv_pos), sizeof (Record));
            recv_pos++;
            if (recv_pos > NUM)
				break;
        }
    }

    void Thread_Send()
    {
        printf("Thread_Send: %lld\n", GetSysTimeMicros() - base);
        int i = 0;
        while (true)
        {
			if (i >= NUM)
				break;

            if (i < send_pos)
            {
                send_buffer[i].sent = GetSysTimeMicros() - base;
                int n = send(sock, (char *)(send_buffer + i), sizeof(Record), 0);
                if (n > 0)
                {
                  //printf("%d-%d sent\n", i, send_buffer[i].ref);
                    i++;
                }
                else
                {
                    printf("send error:%d\n", WSAGetLastError());
                }
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::microseconds(1));
                printf("sleep 1 us after %d\n", i);
            }
        }
    }

private:
    SOCKET sock;
    std::thread thd_send;
    std::thread thd_recv;

    std::atomic<int> send_pos = 0;
};


int main()
{
    base = GetSysTimeMicros();
    printf("main base: %lld\n", base);

    Client * client = new Client;
    client->Connect(SERVER, PORT);
    client->Start();

    Sleep(30);  //必须要sleep,否则上面Start里的两个线程还没启动,但是下面的SendData已经给数据打开始的点了;Sleep之后,上面的两个线程也就启动了
    Record data_send;
    time_t start = time(nullptr);
    for (int i = 0; i < NUM; i++)
    {
        data_send.ref = i;
        data_send.put = GetSysTimeMicros() - base;// i * 100;
        client->SendData((char *)(&data_send), sizeof (Record));
    }
    time_t end = time(nullptr);
    printf("%d processed %d seconds\n", NUM, end-start);

    getchar();
    double total = 0;
    printf("recv send ack-sent sent-put  put  send  ack\n");
    for (int i = 0; i < NUM; i++)
    {
        int t1 = recv_buffer[i].acked - send_buffer[i].sent;
        total += t1;
        int t2 = recv_buffer[i].sent - send_buffer[i].put;
        printf("%3d %3d %8d %8d %lld %lld %lld\n", recv_buffer[i].ref, send_buffer[i].ref, t1, t2, recv_buffer[i].put, recv_buffer[i].sent, recv_buffer[i].acked);
    }
    printf("total %.2f\t avg: %.2fus\n", total, total*1.0 / NUM);

    getchar();
    return 0;
}

 

...全文
149 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧