socket中用户自定义缓冲区的原因及方式
2017-03-21 17:42
176 查看
我们在使用TCP/IP编程的时候除了socket有收发数据缓冲区之外,通常我们还要自己定一个数据的收发缓冲区:
1,为什么系统已经有收发缓冲区了,我们还要自定义收发缓冲区呢?
a.假设应用程序需要发送40kB数据,但是操作系统的TCP发送缓冲区只有25kB剩余空间,那么剩下的15kB数据怎么办?如果等待OS缓冲区可用,会阻塞当前线程,因为不知道对方什么时候收到并读取数据。因此网络库应该把这15kB数据缓存起来,放到这个TCP连接的应用层发送缓冲区中,等socket变得可写的时候立刻发送数据,这样“发送”操作不会阻塞。如果应用程序随后又要发送50kB数据,而此时发送缓冲区中尚有未发送的数据(若干kB),那么网络库应该将这50kB数据追加到发送缓冲区的末尾,而不能立刻尝试write(),因为这样有可能打乱数据的顺序。
b. 假如一次读到的数据不够一个完整的数据包,那么这些已经读到的数据是不是应该先暂存在某个地方,等剩余的数据收到之后再一并处理
2.在非阻塞网络编程中,如何设计并使用缓冲区?
一方面希望减少系统调用,一次读的数据越多越划算,那么应该准备一个大的缓冲区。
另一方面,希望减少内存占用。如果有10000个并发连接,每个连接一建立就分配各50kB的读写缓冲区(s)的话,将占用1GB内存,而大多数时候这些缓冲区的使用率很低。 可以用readv(2)结合栈上空间解决了这个问题。
3.一下是几种建立缓冲区的方式:
a.方法1: 每次接收到数据的时候开辟一个缓冲区(数据头是数据大小的);然后接受数据填入缓冲区,把缓冲区和IP信息付给任务,压入到任务队列,等任务线程处理; 发送亦然;(小数据可以用栈拷贝的形式)
好处:是接收线程可以一直接收,任务线程一直处理,除了任务锁没有其他交互;
缺点: 每次都重新申请空间,malloc(或new)消耗量大(可以使用内存池优化)
b.方法2: 预先申请一块大的缓冲区(每个连接各申请一个接收缓冲区和发送缓冲区),接收线程有新数据到来的时候从缓冲区中数据结尾获得可用空间插入数据,把连接信息和整个缓冲区压入任务队列,任务线程处理一个任务的数据,就清空缓冲区该段的数据,然后将缓冲区中后面的数据前移(所以每次都是处理的第一个数据区的数据)
好处:减少了malloc
缺点:在数据插入和使用的时候都使用的锁,而且有严重的拷贝复制情况,(如果想任务处理和数据接收不互相锁,必须使用多的一份儿数据拷贝,情况就更糟)
c.方法3:使用线程池,每个线程独立的读数据,当数据满足一个包的时候就当做任务处理;然后将链接信息和用户缓冲区添加到监听队列中;有新的数据来到就激活用一个新的线程处理
好处:仅使用了线程间的锁,(可以使用无锁队列优化),减少数据拷贝
缺点:线程上下文切换开销大, 数据接收分散
D.方法4: 使用绑定线程(一个CPU核一个线程),线程分摊活动连接句(使用管道分发(hash)),单线程接收数据到缓存,然后结合协程做任务处理。
优缺点:还没实现过,不确定
1,为什么系统已经有收发缓冲区了,我们还要自定义收发缓冲区呢?
a.假设应用程序需要发送40kB数据,但是操作系统的TCP发送缓冲区只有25kB剩余空间,那么剩下的15kB数据怎么办?如果等待OS缓冲区可用,会阻塞当前线程,因为不知道对方什么时候收到并读取数据。因此网络库应该把这15kB数据缓存起来,放到这个TCP连接的应用层发送缓冲区中,等socket变得可写的时候立刻发送数据,这样“发送”操作不会阻塞。如果应用程序随后又要发送50kB数据,而此时发送缓冲区中尚有未发送的数据(若干kB),那么网络库应该将这50kB数据追加到发送缓冲区的末尾,而不能立刻尝试write(),因为这样有可能打乱数据的顺序。
b. 假如一次读到的数据不够一个完整的数据包,那么这些已经读到的数据是不是应该先暂存在某个地方,等剩余的数据收到之后再一并处理
2.在非阻塞网络编程中,如何设计并使用缓冲区?
一方面希望减少系统调用,一次读的数据越多越划算,那么应该准备一个大的缓冲区。
另一方面,希望减少内存占用。如果有10000个并发连接,每个连接一建立就分配各50kB的读写缓冲区(s)的话,将占用1GB内存,而大多数时候这些缓冲区的使用率很低。 可以用readv(2)结合栈上空间解决了这个问题。
3.一下是几种建立缓冲区的方式:
a.方法1: 每次接收到数据的时候开辟一个缓冲区(数据头是数据大小的);然后接受数据填入缓冲区,把缓冲区和IP信息付给任务,压入到任务队列,等任务线程处理; 发送亦然;(小数据可以用栈拷贝的形式)
好处:是接收线程可以一直接收,任务线程一直处理,除了任务锁没有其他交互;
缺点: 每次都重新申请空间,malloc(或new)消耗量大(可以使用内存池优化)
b.方法2: 预先申请一块大的缓冲区(每个连接各申请一个接收缓冲区和发送缓冲区),接收线程有新数据到来的时候从缓冲区中数据结尾获得可用空间插入数据,把连接信息和整个缓冲区压入任务队列,任务线程处理一个任务的数据,就清空缓冲区该段的数据,然后将缓冲区中后面的数据前移(所以每次都是处理的第一个数据区的数据)
好处:减少了malloc
缺点:在数据插入和使用的时候都使用的锁,而且有严重的拷贝复制情况,(如果想任务处理和数据接收不互相锁,必须使用多的一份儿数据拷贝,情况就更糟)
c.方法3:使用线程池,每个线程独立的读数据,当数据满足一个包的时候就当做任务处理;然后将链接信息和用户缓冲区添加到监听队列中;有新的数据来到就激活用一个新的线程处理
好处:仅使用了线程间的锁,(可以使用无锁队列优化),减少数据拷贝
缺点:线程上下文切换开销大, 数据接收分散
D.方法4: 使用绑定线程(一个CPU核一个线程),线程分摊活动连接句(使用管道分发(hash)),单线程接收数据到缓存,然后结合协程做任务处理。
优缺点:还没实现过,不确定
相关文章推荐
- 网络(4):socket中用户自定义缓冲区的原因及方式
- wordpress自定义后台用户联系方式 添加qq、微博、微信
- 从源代码剖析Struts2中用户自定义配置转换器的两种方式——基于字段的配置转换器和基于类型的配置转换器(解决了实际系统中,因没有区分这两种工作方式的生命周期而引起的异常错误问题)
- Wpf和WinForm在窗体中处理用户自定义消息的不同方式
- 用户自定义类型与C#基础类型转换的方式
- Oracle数据库用户锁定原因以及处理方式(ORA-28000)
- Socket 字节缓冲区第二种方式
- (3)自定义服务器,用户提交数据两种方式get,post区别
- ORACLE 存储过程中保存用户自定义异常信息的一种方式
- python 使用socket通过用户自定义输入获得远程设备IP
- Socket缓冲区探讨,是否有拆包的方式?
- go数据类型之结构体(用户自定义类型两种方式)
- MVVM模式下,自定义用户控件不执行COMMAND的原因
- Socket 字节缓冲区第二种方式
- django项目培训站-17-用户登陆-自定义用户验证方式-错误提示信息传前端
- 关闭Socket的正确方式及ECONNRESET,WSAECONNRESET产生的原因
- 获取JavaScript用户自定义类的类名称
- SqlServer2000中用户自定义数据类型的使用
- 使用委托在用户自定义控件中实现事件响应
- WebService的两种用户验证方式