linux网络编程系列(十三)--缓冲区设计及收发大量数据

1. 自定义缓冲区

咱们在使用TCP/IP编程的时候除了socket有收发数据缓冲区以外,一般咱们还要本身定一个数据的收发缓冲区。python

1.1 为何要自定义缓冲区

假设应用程序须要发送40kB数据,可是操做系统的TCP发送缓冲区只有25kB剩余空间,那么剩下的15kB数据怎么办?若是等待OS缓冲区可用,会阻塞当前线程,由于不知道对方何时收到并读取数据。所以网络库应该把这15kB数据缓存起来,放到这个TCP链接的应用层发送缓冲区中,等socket变得可写的时候马上发送数据,这样“发送”操做不会阻塞。若是应用程序随后又要发送50kB数据,而此时发送缓冲区中尚有未发送的数据(若干kB),那么网络库应该将这50kB数据追加到发送缓冲区的末尾,而不能马上尝试write(),由于这样有可能打乱数据的顺序。c++

另外的话,假如一次读到的数据不够一个完整的数据包,那么这些已经读到的数据是否是应该先暂存在某个地方,等剩余的数据收到以后再一并处理。编程

1.2 缓冲区设计的原则

  • 一方面但愿减小系统调用,一次读的数据越多越划算,那么应该准备一个大的缓冲区;
  • 另外一方面,但愿减小内存占用。若是有10000个并发链接,每一个链接一创建就分配各50kB的读写缓冲区(s)的话,将占用1GB内存,而大多数时候这些缓冲区的使用率很低,能够用readv(2)结合栈上空间解决了这个问题;

1.3 创建缓冲区的方式

1.3.1 每次都从新申请缓冲区

每次接收到数据的时候开辟一个缓冲区,而后将接收到的数据填入缓冲区,把缓冲区和IP信息付给任务,压入到任务队列,等任务线程处理; 发送亦然;(小数据能够用栈拷贝的形式) 好处:是接收线程能够一直接收,任务线程一直处理,除了任务锁没有其余交互; 缺点: 每次都从新申请空间,malloc(或new)消耗量大(可使用内存池优化);缓存

1.3.2 预先申请缓冲区

预先申请一块大的缓冲区(每一个链接各申请一个接收缓冲区和发送缓冲区),接收线程有新数据到来的时候从缓冲区中数据结尾得到可用空间插入数据,把链接信息和整个缓冲区压入任务队列,任务线程处理一个任务的数据,就清空缓冲区该段的数据,而后将缓冲区中后面的数据前移(因此每次都是处理的第一个数据区的数据) 好处:减小了malloc 缺点:在数据插入和使用的时候都使用的锁,并且有严重的拷贝复制状况,(若是想任务处理和数据接收不互相锁,必须使用多的一份儿数据拷贝,状况就更糟)网络

1.3.3 使用线程池

使用线程池,每一个线程独立的读数据,当数据知足一个包的时候就当作任务处理;而后将连接信息和用户缓冲区添加到监听队列中;有新的数据来到就激活用一个新的线程处理。 好处:仅使用了线程间的锁,(可使用无锁队列优化),减小数据拷贝; 缺点:线程上下文切换开销大, 数据接收分散;并发

2. 使用read和write读写大量内容

2.1 读大量内容

ssize_t readn(int fd, char *buf, int size)
{
char *pbuf = buf;
int total ,nread;
for(total = 0; total < size; )
{
nread=read(fd,pbuf,size-total);
if(nread==0)
       return total;
    if(nread == -1)
    {
      if(errno == EINTR)
          continue;
      else
         return -1;
    }
    total += nread;
    pbuf += nread;
    }
   return total;
}
复制代码

2.2 写大量内容

ssize_t writen(int fd, char *buf, int size)
{
   char *pbuf=buf;
   int total ,nwrite;
   for(total = 0; total < size; )
   {
      nwrite=write(fd,pbuf,size-total);
  if( nwrite <= 0 )
  {
   if( nwrite == -1 && errno == EINTR )
continue;
   else
    return -1;
  }
     total += nwrite;
     pbuf += nwrite;
   }
  return total;
}
复制代码

更多c++及python系列文章,请关注个人公众号:晟夏的叶。 ​socket

公众号:晟夏的叶
相关文章
相关标签/搜索