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

公衆號:晟夏的葉
相關文章
相關標籤/搜索