一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

linux下使用write\send發(fā)送數(shù)據(jù)報(bào) EAGAIN : Resource temporarily unavailable 錯(cuò)

 gljin_cn 2016-06-14

http://badqiu./blog/1149965


linux下使用write\send發(fā)送數(shù)據(jù)報(bào) EAGAIN : Resource temporarily unavailable 錯(cuò)

首先是我把套接字設(shè)置為異步的了,然后在使用write發(fā)送數(shù)據(jù)時(shí)采取的方式是循環(huán)發(fā)送大量的數(shù)據(jù);由于是異步的,write\send將要發(fā)送的數(shù)據(jù)提交到發(fā)送緩沖區(qū)后是立即返回的,并不需要對(duì)端確認(rèn)數(shù)據(jù)已接收。在這種情況下是很有可能出現(xiàn)發(fā)送緩沖區(qū)被填滿,導(dǎo)致write\send無法再向緩沖區(qū)提交要發(fā)送的數(shù)據(jù)。因此就產(chǎn)生了Resource temporarily unavailable的錯(cuò)誤,EAGAIN 的意思也很明顯,就是要你再次嘗試。

把發(fā)送部分修改如下

  1. int SeanSend(int fd, void *buffer, int length)  
  2. {  
  3.     int bytes_left;   
  4.     int written_bytes;   
  5.     char *ptr;   
  6.     ptr=(char *)buffer;   
  7.     bytes_left=length;   
  8.     while(bytes_left>0)   
  9.     {   
  10.         /* 開始寫*/   
  11.         written_bytes=write(fd, ptr, bytes_left);   
  12.         if(written_bytes<=0) /* 出錯(cuò)了*/   
  13.         {   
  14.             if(errno==EINTR) /* 中斷錯(cuò)誤 我們繼續(xù)寫*/   
  15.             {  
  16.                 continue;  
  17.                 printf("[SeanSend]error errno==EINTR continue\n");  
  18.             }  
  19.             else if(errno==EAGAIN) /* EAGAIN : Resource temporarily unavailable*/   
  20.             {  
  21.                 sleep(1);//等待一秒,希望發(fā)送緩沖區(qū)能得到釋放  
  22.                 continue;  
  23.                 printf("[SeanSend]error errno==EAGAIN continue\n");  
  24.             }  
  25.             else /* 其他錯(cuò)誤 沒有辦法,只好退了*/   
  26.             {  
  27.                 printf("[SeanSend]ERROR: errno = %d, strerror = %s \n"  
  28.                                 , errno, strerror(errno));  
  29.                 return(-1);  
  30.             }  
  31.         }  
  32.         bytes_left-=written_bytes;   
  33.         ptr+=written_bytes;/* 從剩下的地方繼續(xù)寫?? */   
  34.     }   
  35.     return length;   

非阻塞socket編程問題小結(jié)

http://blog.sina.com.cn/s/blog_4462f8560100tvu4.html


項(xiàng)目需要寫一個(gè)主動(dòng)連接且定時(shí)發(fā)送數(shù)據(jù)的客戶端程序,并保證傳輸數(shù)據(jù)的可靠性和穩(wěn)定性。

注意的問題有:

1.connect返回值判定

之前的程序

if(connect(tcp_client_sock,(structsockaddr*)&server, server_length) <0)

//向服務(wù)器發(fā)起連接,連接成功后client_socket代表了客戶機(jī)和服務(wù)器的一個(gè)socket連接
     {
       printf("Can Not Connect To %s!\n",SERVE_IP);
       close(tcp_client_sock);

    return-1;
       //exit(1);
     }
     else
     {
     TCP_CONNET_FLAG = 1;//建立連接

...

}

但是在網(wǎng)上查詢發(fā)現(xiàn):當(dāng)我們以非阻塞的方式來進(jìn)行連接的時(shí)候,返回的結(jié)果如果是-1,這并不代表這次連接發(fā)生了錯(cuò)誤,如果它的返回結(jié)果是EINPROGRESS,那么就代表連接還在進(jìn)行中。后面可以通過poll或者select來判斷socket是否可寫,如果可以寫,說明連接完成了。

更改如下:

//先定位為非阻塞模式,立即返回狀態(tài);如有錯(cuò)誤存為SO_ERROR值     

if((flags =fcntl(tcp_client_sock,F_GETFL, 0 )) < 0)
        {
       perror("fcntl");
       return -1;
        }
        flags |= O_NONBLOCK;
        if(fcntl(tcp_client_sock, F_SETFL, flags) < 0)//設(shè)置socket為非阻塞模式
       {
         perror("fcntl");
         return -1;
       }

 

 if(connect(tcp_client_sock,(structsockaddr*)&server, server_length) <0) //向服務(wù)器發(fā)起連接
         

        if(errno != EINPROGRESS)//非等待狀態(tài)
           {
             perror("connect error");

          close(tcp_client_sock);//下一步重連

         //return -1;
           }
           else//EINPROGRESS:正常處理連接
           {

           perror("connect");//查詢ERROR值
              printf("check delay connected\n");
              goto done;//進(jìn)一步檢查是否握手完成
           }
       }

else
       {
    done:

        ...//下一步的select判定connect是否完成及tcp_client_sock可寫性

    }

這里,perror("connect")語(yǔ)句查詢ERROR值為“Operation now inprogress”,表明非阻塞connect立即返回的狀態(tài)為正在建立三次握手;如果不想出現(xiàn)這種情況,可以將以上關(guān)于非阻塞設(shè)置socket的語(yǔ)句放到connect之后,就會(huì)在阻塞方式下等待connect完成,但仍需要作進(jìn)一步檢查。對(duì)于非阻塞方式,下一步就可以通過select自定義超時(shí)時(shí)間(通常比阻塞方式下connect超時(shí)時(shí)間短),并進(jìn)一步檢查是否連接錯(cuò)誤和規(guī)定時(shí)間內(nèi)套接口可讀寫性。

2.select超時(shí)設(shè)置問題

如果設(shè)置connect為非阻塞函數(shù)后,進(jìn)行select時(shí)只關(guān)注writefds,忽略readfds,exceptfds,可能出現(xiàn)一個(gè)問題:本來不想由于connect阻塞等太久,結(jié)果用select后反而傻等了。

如果在connect后開始select,只關(guān)注writefds,設(shè)置的超時(shí)是10秒,在connect發(fā)出[SYN]后:假定目標(biāo)IP的主機(jī)不存在或者是目標(biāo)端口給防火墻過濾了,那么你等再久也不會(huì)有任何回復(fù),這時(shí)候如果是阻塞connect可能要15秒才返回,那么你10秒就返回了,這種情況就賺了5秒。

然而假定connect的目標(biāo)IP主機(jī)是存在的也沒防火墻,只是端口是沒打開的,在connect發(fā)出[SYN]后的1秒系統(tǒng)已經(jīng)收到目標(biāo)主機(jī)回復(fù)的[RST,ACK],也就是說系統(tǒng)此時(shí)已經(jīng)知道這個(gè)端口是連接不上的了,但是應(yīng)用程序只關(guān)注writefds,后面的9秒鐘select就會(huì)傻傻的等待下去……原本以為用select來減少不必要的等待時(shí)間,如果不設(shè)置參數(shù)exceptfds,這時(shí)候反而浪費(fèi)時(shí)間。 

3.send/recv 返回值

由于send、recv函數(shù)用于已連接的數(shù)據(jù)報(bào)或流式套接口s進(jìn)行數(shù)據(jù)的接收。所以在非阻塞socket的客戶端程序中recv、send函數(shù)成功返回并不代表對(duì)端一定收到了發(fā)送的消息。tcp協(xié)議本身是可靠的,并不等于應(yīng)用程序用tcp發(fā)送數(shù)據(jù)就一定是可靠的。不管是否阻塞,send發(fā)送的大小,并不代表對(duì)端recv到多少的數(shù)據(jù).

關(guān)于recv返回值,百度的解釋:

     1).若無錯(cuò)誤發(fā)生,recv()返回讀入的字節(jié)數(shù)。

     2).如果連接已中止,返回0。

     3).否則的話,返回SOCKET_ERROR錯(cuò)誤。

  如果套接口為SOCK_STREAM類型,并且遠(yuǎn)端“優(yōu)雅”地中止了連接,那么recv()一個(gè)數(shù)據(jù)也不讀取,立即返回。如果立即被強(qiáng)制中止,那么recv()將以WSAECONNRESET錯(cuò)誤失敗返回。

   接收數(shù)據(jù)時(shí)perror時(shí)常遇到"Resource temporarilyunavailable"的提示,errno代碼為11(EAGAIN)。這表明你在非阻塞模式下調(diào)用了阻塞操作,在該操作沒有完成就返回這個(gè)錯(cuò)誤,這個(gè)錯(cuò)誤不會(huì)破壞socket的同步,不用管它,下次循環(huán)接著recv就可以。對(duì)非阻塞socket而言,EAGAIN不是一種錯(cuò)誤。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。其實(shí)這算不上錯(cuò)誤,只是一種異常而已。

  另外,如果出現(xiàn)EINTR即errno為4,錯(cuò)誤描述Interrupted systemcall,即由于信號(hào)中斷導(dǎo)致操作失敗,也應(yīng)該繼續(xù)。

      val= recv(client_sock,info,length,MSG_NOSIGNAL);
      if(val< 0)//判斷是否網(wǎng)絡(luò)無數(shù)據(jù)或接收緩沖區(qū)是否準(zhǔn)備好
      
            if(val==EAGAIN||EWOULDBLOCK||EINTR)
            {
                 printf("Recvdata Timeout.Waiting...\n");
                 return1;
             }
             else//網(wǎng)絡(luò)異常斷開或阻塞;需重新連接
             {
                 perror("recv");
                 return-1;
             }
      }

      elseif(val == 0)//server端正常關(guān)閉,需重新連接

         

        TCP_CONNET_FLAG=0;//關(guān)閉TCP連接flag
         printf("socketclose nomally\n");
         return0;
       }

      else//返回接收字節(jié)大于零,正常接收數(shù)據(jù)
       

           ...//對(duì)接收緩沖區(qū)info的每個(gè)字節(jié)作讀寫操作處理

          return 1;

       }

 

關(guān)于send函數(shù)在阻塞模式和非阻塞模式下的區(qū)別:

      在阻塞模式下,send函數(shù)的過程是將應(yīng)用程序請(qǐng)求發(fā)送的數(shù)據(jù)拷貝到發(fā)送緩存中發(fā)送并得到確認(rèn)后再返回.但由于發(fā)送緩存的存在,表現(xiàn)為:如果發(fā)送緩存大小比請(qǐng)求發(fā)送的大小要大,那么send函數(shù)立即返回,同時(shí)向網(wǎng)絡(luò)中發(fā)送數(shù)據(jù);否則,send向網(wǎng)絡(luò)發(fā)送緩存中不能容納的那部分?jǐn)?shù)據(jù),并等待對(duì)端確認(rèn)后再返回(接收端只要將數(shù)據(jù)收到接收緩存中,就會(huì)確認(rèn),并不一定要等待應(yīng)用程序調(diào)用recv);
      在非阻塞模式下,send函數(shù)的過程僅僅是將數(shù)據(jù)拷貝到協(xié)議棧的緩存區(qū)而已,如果緩存區(qū)可用空間不夠,則盡能力的拷貝,返回成功拷貝的大小;如緩存區(qū)可用空間為0,則返回-1,同時(shí)設(shè)置errno為EAGAIN.

 

當(dāng)客戶通過Socket提供的send函數(shù)發(fā)送大的數(shù)據(jù)包時(shí),就可能返回一個(gè)EGGAIN的錯(cuò)誤。該錯(cuò)誤產(chǎn)生的原因是由于send函數(shù)中的size變量大小超過了tcp_sendspace的值。tcp_sendspace定義了應(yīng)用在調(diào)用send之前能夠在kernel中緩存的數(shù)據(jù)量。當(dāng)應(yīng)用程序在socket中設(shè)置了O_NDELAY或者O_NONBLOCK屬性后,如果發(fā)送緩存被占滿,send就會(huì)返回EAGAIN的錯(cuò)誤。

為了消除該錯(cuò)誤,有三種方法可以選擇:
  1).調(diào)大tcp_sendspace,使之大于send中的size參數(shù)
  ---no -p -otcp_sendspace=65536
  2).在調(diào)用send前,在setsockopt函數(shù)中為SNDBUF設(shè)置更大的值

  intopt=SO_REUSEADDR;
     setsockopt(tcp_client_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
          opt = 256*1024;//512k
          int optlen = sizeof(int);
          setsockopt(tcp_client_sock,SOL_SOCKET,SO_SNDBUF,&opt,sizeof(int));
          getsockopt(tcp_client_sock,SOL_SOCKET,SO_SNDBUF,&opt,&optlen); 
  3).使用write替代send,因?yàn)閣rite沒有設(shè)置O_NDELAY或者O_NONBLOCK

 在CSDN中看到有這樣一種情況:

假如發(fā)送端流量大于接收端的流量(意思是epoll所在的程序讀比轉(zhuǎn)發(fā)的socket要快),由于是非阻塞的socket,那么send()函數(shù)雖然返回,但實(shí)際緩沖區(qū)的數(shù)據(jù)并未真正發(fā)給接收端,這樣不斷的讀和發(fā),當(dāng)緩沖區(qū)滿后會(huì)產(chǎn)生EAGAIN錯(cuò)誤,同時(shí),不理會(huì)這次請(qǐng)求發(fā)送的數(shù)據(jù).

所以,需要根據(jù)send()函數(shù)返回值及errno值作進(jìn)一步處理。遇到該情況,函數(shù)要求盡量將數(shù)據(jù)寫完再返回,或通過更改發(fā)送緩沖區(qū)大小。當(dāng)寫緩沖已滿(send()返回-1,且errno為EAGAIN),那么會(huì)等待后再重試send().這種方式并不很完美,在理論上可能會(huì)長(zhǎng)時(shí)間的阻塞在socket的send()中,但暫沒有更好的辦法.

        SendFlag&=TCP_CONNET_FLAG;//測(cè)試tcp_client_sock描述符可寫且TCP連接存在
           if(SendFlag==1)//嘗試發(fā)送
           {
              res=send_client_info(tcp_client_sock,buf,45);            
               if(res<0)
               {
                 if(errno == EINTR)//當(dāng)socket是非阻塞時(shí),如返回此錯(cuò)誤,表示寫緩沖隊(duì)列已滿,返回后判斷網(wǎng)絡(luò)狀態(tài)再重試.
                   return -1;
                 if(errno == EAGAIN)//發(fā)送緩沖區(qū)剩余0字節(jié),延時(shí)等待發(fā)送;
                 {
                      usleep(10000);
                      res=send_client_info(tcp_client_sock,buf,45);
                 }
               }
          }
          else
          {
          ...//發(fā)送緩沖區(qū)數(shù)據(jù)暫存,等待連接正常再發(fā)
          

linux編程環(huán)境中,如果TCP連接斷開后繼續(xù)發(fā)數(shù)據(jù)的時(shí)候,不僅send()的返回值會(huì)有反映,而且還會(huì)像系統(tǒng)發(fā)送一個(gè)異常消息,如果不作處理,系統(tǒng)會(huì)出BrokePipe,程序會(huì)退出。為此,send()函數(shù)的最后一個(gè)參數(shù)可以設(shè)MSG_NOSIGNAL,禁止send()函數(shù)向系統(tǒng)發(fā)送異常消息。

 

3.close(socket)后send/recv數(shù)據(jù)的問題

如果在發(fā)送數(shù)據(jù)的過程中send()沒有完成,還有數(shù)據(jù)沒發(fā)送,而調(diào)用了closesocket(),以前一般采取的措施是shutdown(s,SD_BOTH),但是數(shù)據(jù)將會(huì)丟失。

  如果設(shè)計(jì)程序功能要求待未發(fā)送完的數(shù)據(jù)發(fā)送出去后再關(guān)閉socket,進(jìn)行如下操作:

  struct linger {

  u_short l_onoff;

  u_short l_linger;

  };

  linger m_sLinger;

  m_sLinger.l_onoff = 1;//在調(diào)用closesocket()時(shí)還有數(shù)據(jù)未發(fā)送完,允許等待

  //若m_sLinger.l_onoff=0;則調(diào)用closesocket()后強(qiáng)制關(guān)閉

  m_sLinger.l_linger = 5;//設(shè)置等待時(shí)間為5秒

  setsockopt( s, SOL_SOCKET, SO_LINGER,( const char* )&m_sLinger, sizeof( linger ) );

若設(shè)置了SO_LINGER并確定了非零的超時(shí)間隔,則closesocket()調(diào)用阻塞進(jìn)程,直到所剩數(shù)據(jù)發(fā)送完畢或超時(shí)。這種關(guān)閉稱為“優(yōu)雅的”關(guān)閉。請(qǐng)注意如果套接口置為非阻塞且SO_LINGER設(shè)為非零超時(shí),則closesocket()調(diào)用將以WSAEWOULDBLOCK錯(cuò)誤返回。 

WSAEWOULDBLOCK:該套接口設(shè)置為非阻塞方式且SO_LINGER設(shè)置為非零超時(shí)間隔。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    久久这里只精品免费福利| 老司机精品视频在线免费看| 国产日韩欧美国产欧美日韩| 欧美大粗爽一区二区三区| 成年人视频日本大香蕉久久| 欧美熟妇喷浆一区二区| 久久成人国产欧美精品一区二区 | 福利专区 久久精品午夜| 中文字幕一区二区免费| 热情的邻居在线中文字幕| 国产偷拍盗摄一区二区| 国产在线一区二区三区不卡| 搡老熟女老女人一区二区| 殴美女美女大码性淫生活在线播放 | 日韩午夜老司机免费视频| 91精品国产综合久久福利| 不卡一区二区在线视频| 国产精品久久香蕉国产线| 亚洲日本久久国产精品久久| 亚洲天堂男人在线观看| 老司机亚洲精品一区二区| 国产免费操美女逼视频| 伊人久久五月天综合网| 国产视频福利一区二区| 少妇福利视频一区二区| 亚洲综合香蕉在线视频| 欧美特色特黄一级大黄片| 精品午夜福利无人区乱码| 日本美国三级黄色aa| 翘臀少妇成人一区二区| 一区二区免费视频中文乱码国产| 99久久人妻中文字幕| 日本中文字幕在线精品| 精品国产亚洲av成人一区| 少妇视频一区二区三区| 午夜国产福利在线播放| 色综合伊人天天综合网中文| 欧美日韩国产午夜福利| 我想看亚洲一级黄色录像| 日韩无套内射免费精品| 在线欧美精品二区三区|