<3>:阻塞IO的试验-echo程序

阻塞意味着会阻塞,而且可能永远阻塞,
看下echo的原型,每个线程一个客户端。
代码位置recipes/tpc/{echo.cc, echo_client.cc}

测试

服务端
[root@bogon bin]# ./echo
客户端
[root@bogon bin]# ./echo_client localhost 1024(1024000,10240000,20488888(20M))

这里写图片描述

可以观察到20MB的时候已经阻塞掉了,why?
观察结果如下:

可以看到服务端的接受队列约有6MB,发送队列大概有4MB,于是有4MB的数据存在发送缓冲区里,接受缓冲区还有6MB的数据等待读取;客户接受缓冲区有900多K的数据,发送缓冲区有4MB的数据。

原因解释:

客户端发(send)完才会读取(receive),
客户端在发的时候,数据先进入内核缓冲区,缓冲区满了后就会阻塞掉;服务端读,并会发送,
客户端发完(send)20MB之前是不会读的(receive),
估算下,如果缓冲区10MB,则发送完10MB,服务端就send,阻塞在send上了,因为它等待client读取,但是client必须send完毕才会读取;所以显示server阻塞在send上,然后它就不会去收(receive)客户端的数据了,它不reveive,则client就阻塞在send上。同时阻塞在send上。

阻塞IO看起来简单,可是一旦阻塞了就没有办法解脱呀,20MB是本机测试的结果,如果用两台机器测试的数据会显著小于此机器(待测试。。。)这个和内核参数有关,主要是TCP的相关设置,如下

[david@localhost ~]$ sysctl -A | grep tcp.*mem
sysctl: permission denied on key 'fs.protected_hardlinks'
sysctl: permission denied on key 'fs.protected_symlinks'
sysctl: permission denied on key 'kernel.cad_pid'
sysctl: permission denied on key 'kernel.usermodehelper.bset'
sysctl: permission denied on key 'kernel.usermodehelper.inheritable'
sysctl: permission denied on key 'net.ipv4.tcp_fastopen_key'
net.ipv4.tcp_mem = 42339    56452   84678
//接受缓冲区,6M多
net.ipv4.tcp_rmem = 4096    87380   6291456
//发送缓冲区,4M多
net.ipv4.tcp_wmem = 4096    16384   4194304

哪里出问题呢?client还是server呢?首先client发送20MB的数据没问题,问题是出在服务端,看待echo服务器每次读4K的数据,读完就发送出去,问题在于没有完整的读取请求。

协议设计:

客户端先发送header,再发送payload。服务器在收到header的时候,先准备一个相应长度的缓冲区,接受到这么多的数据后,开始计算,算出一个响应;拿前面的例子来说,比如说15MB,算完之后将15MB数据发给客户端,这样的话server发的时候也发一个Header,再发送payload。字节流协议需要加上自己的分包措施才行。

这里写图片描述

参考陈硕[网络编程实践]课程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用`std::future`来获取线程池中任务的返回值。在`enqueue`函数中,任务被包装在`std::packaged_task`中,这个包装器可以将一个函数包装成一个可调用对象,并且可以使用`std::future`来获取函数的返回值。 在`enqueue`函数中,我们使用`std::make_shared`创建了一个`std::packaged_task`,并将要执行的任务`f`和其参数`args`绑定在一起。然后,我们将这个`std::packaged_task`封装在一个`std::shared_ptr`中,以便可以在其他线程中访问它。 接下来,我们使用`std::future`获取`std::packaged_task`的返回值。`std::future`是一个异步结果的占位符,可以用来检查任务是否已经完成,并且可以获取任务的返回值。 具体地,我们可以在调用`enqueue`函数后,使用返回的`std::future`对象的`get()`函数来获取任务的返回值。`get()`函数会阻塞当前线程,直到任务执行完毕并返回结果。 例如,假设我们要执行一个函数`int add(int x, int y)`,我们可以使用以下方式来获取其结果: ```c++ ThreadPool pool(4); // 创建线程池,有4个线程 // 将任务加入线程池,并获取返回值的future对象 auto result = pool.enqueue(add, 3, 4); // 等待任务执行完成,并获取返回值 int res = result.get(); std::cout << "3 + 4 = " << res << std::endl; ``` 这里的`result.get()`会阻塞当前线程,直到任务执行完毕并返回结果。最后,我们将`res`输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值