码迷,mamicode.com
首页 > 其他好文 > 详细

lwip 之 select 暨 keepalive 笔记

时间:2014-08-15 22:24:59      阅读:2389      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   使用   文件   数据   ar   art   

       最近在使用國人自己的實時作業系統rt_thread,在stm32f103上寫一些應用。其中使用到了網絡編程。

   當仁不讓,最基本的select()逃不掉;setsockopt()也逃不掉。下面就把自己的使用情況記錄如下。

       先說說select()函數。

       其實它不限於網絡平台,主要是把永遠阻塞變成某個時間段的等待,所謂超時機制是也---其實,更好理解的是:把無期徒刑變有期徒刑。它是這樣的:它會去一個大的集合裡面檢測這個集合裡面元素的操作性質,一旦檢測出了某個或者某幾個元素操作性質有變化了,就會馬上返回一個值,這個值代表集合中操作屬性有變化的元素個數。然後用戶就根據自己的心情,按照可操作的性質,就操作這些元素即可。

      那麼,這個集合是什麼呢?類似一個數組。數組的每一個格格裝一個元素。

  那麼元素是什麼呢? 文件描述符:比如 打開一個文本文件,會產生一個描述符;打開一個uart,也會產生一個描述符;打開一個socket套接字,那也是一個描述符... 這些描述符就是這些元素,可多可少.. 傳說在1024個左右。是否為真,還得查下書。

    那麼這些元素,或者說這些文件描述符的操作屬性是什麼呢?  嗯,linux下似乎只有這三個rwx,或者421.而在這裡,這些文件描述符的屬性包括: 可讀屬性 <"r">、可寫屬性<"w">、異常屬性<不好意思,不是 "x" 屬性了>。

  那麼,這些屬性什麼時候會發生變動呢? 比如,串口uart在等待數據的時候,描述符的讀狀態為0.  突然,對端設備拋一串數據過來了,於是這個時候,描述符的讀狀態就變成 1了; tcp server和一個client連接好以後,然後等待client端的數據,這個時候server的套接字狀態讀狀態為0,當client突發扔一串數據過來了,這樣一沖,server的套接字的讀狀態就變為1了。<以上語言僅僅是假設,具體文件描述符的狀態是怎麼設置的,這個要看linux kernel中是怎麼實現的>。

  於是乎,讀狀態從0到1這樣一個狀態的變化,就引起了select()的注意---在後面,它會把這個情況告訴給程序員,程序員自己去處理了:讀屬性發生了變化,那就讀數據嘛,寫狀態發生了變化,那就寫數據嘛,表廢話。但有一點需要存疑:當select()發現一個描述符的讀或者寫狀態屬性發生了變化后,是馬上就返回,還是會繼續監測整個集合中的元素,當監測完了后,再繼續返回呢?<不是等到超時才返回>

   好吧,該說說程序員的心情問題了。什麼樣的心情呢? 我只關心uart數據是否可讀了,不關心uart數據是否可寫;我只關心我的套接字是否能往對端寫東西了,才不管是否有數據可讀了.. 或者,我不放心打開的這個文本文件會不會鬼混去了<異常>.. 或者,我精力無限,這些通通都要關心一把.. 也行,沒問題,select已經考慮好了。

  既然該說的都說了,那是否就該立刻用select()來整一段高大上的代碼?別急,還有一個致命的一點: 

     那就是select()它也是有性格的---每執行一次,它的某些參數都要重置一次,否則,它就會罷工的<當然,在一個菜鳥的我手裡,曾經它罷工的平率是1,不存在 小於1的情況,這樣是這篇筆記產生的原因>。

   好吧,其他方面的,網絡資料無限,書上也說的非常明白了,而且注意點,也說的非常明白了。不再多說了,下面直接上一小段代碼,主要是針對參數重置部分的while(1)循環步驟:

  

 1 int client_hearbet(void)
 2 {
 3   int retval = -1, maxfdp = -1, heart_count = 0;
 4   struct timeval timeout;
 5   fd_set readset;
 6 /**/
 7 //......
 8   maxfdp = client_sock + 1;
 9   FD_ZERO(&readset);
10   FD_SET(client_sock, &readset);
11   while(1) {
12 //......
13       timeout.tv_sec = 20;
14       timeout.tv_usec = 0;
15       maxfdp = client_sock + 1;
16       FD_ZERO(&readset);
17       FD_SET(client_sock, &readset);      
18       retval = select(maxfdp, &readset, NULL, NULL, &timeout);
19 //....
20       else if( !retval ) {
21 //....
22         FD_CLR(client_sock,&readset);
23         continue ;
24       }
25       else {
26         if(FD_ISSET(client_sock, &readset)) {
27            /*recv*/ 
28     
29             recv_bytes = recv(client_sock, heart_buff, LEN, 0);
30    //.....
31              FD_CLR(client_sock,&readset);
32     //.....
33         }
34     //......    
35         FD_CLR(client_sock,&readset);
36       }
37   }
38 }

弄的是網絡編程的吧,其實也就是lwip socket編程了。其中注意13 ~17 行,是在while(1)循環裡面;注意第15行,是當前最大描述符 + 1 --> 這個就代表當前集合的大小了吧。

嗯,曠日持久的select()罷工問題就這麼解決了。并寫下了這部分筆記,以供需要的朋友參考<高手飄過了~>

 

--------------------------------------------------------------------------------

  下面來說說使用lwip的setsockopt()的問題。

  lwip也是一個tcp/ip的協議棧,不過稍微悲慘的是,它沒有pc上的tcp/ip那樣細膩與完整---畢竟是一個經過壓縮,要跑在嵌入式設備上的tcp/ip版本。鑒於此,某些地方,就需要自己動手,豐衣足食了。

     下面直接寫結果吧:

     要啟用端口和地址複用功能,一般的都是setsockopt()設置  SO_REUSEADDR 選項;要想在處於連接狀態下,能馬上感覺有人把網線給拔掉了,一般會使用setsockopt()設置keepalive選項。當然,這在lwip裡面也已經支持了。

  看看怎麼個豐衣足食法:

  1   在 opt.h 里面打开 以下这几个宏  SO_REUSE   LWIP_TCP_KEEPALIVE 。<把這倆宏定義為1,默認為0,當然,其他的一些選項也在這個opt.h文件里>

  2   在 tcp_impl.h 里面, 调整了以下三个宏的值:

  

1 A     #define TCP_KEEPIDLE_DEFAULT 3000UL /7200000 ms ---> 3000ms/
2 
3 B     #define TCP_KEEPINTVL_DEFAULT 1000UL /75000 ms ---> 1000ms/
4 
5 C     #define TCP_KEEPCNT_DEFAULT 3U /9次 --> 3次/

當然,這三個宏其實是關乎keepalive的。就在這裡干吧。

代碼里大概就這麼干吧:

 1   /*sock_reuse*/
 2    err = setsockopt(client_sock, SOL_SOCKET, 
 3                    SO_REUSEADDR, 
 4                    (const char*)&sock_reuse, sizeof(sock_reuse));
 5   if(err < 0) {
 6     MY_DEBUG("setsockopt faild!\n\r");
 7     return FALSE_REUSEADDR;    
 8   }
 9 
10 
11       int keepalive = 1; // 开启keepalive属性
12     //  int keepidle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
13     //  int keepinterval = 5; // 探测时发包的时间间隔为5 秒
14     //  int keepcount = 3; // 探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发。
15       err = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));
16       if(err < 0) {
17         MY_DEBUG("%s, %d: Keep alive faild !\n\r",__func__,__LINE__);
18         return FALSE_KEEPALIVE;
19       }

OVER!

 

ps: 個人使用的版本為 rt_thread 1.2.2   + lwip 1.4.1 <也許其他版本有微調>

 

關鍵字: select lwip keepalive  SO_REUSE

 

lwip 之 select 暨 keepalive 笔记,布布扣,bubuko.com

lwip 之 select 暨 keepalive 笔记

标签:style   blog   color   使用   文件   数据   ar   art   

原文地址:http://www.cnblogs.com/chineseboy/p/3915723.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!