2013年2月17日 星期日

socket API FAQ

我整理一些比較常見的疑問點
Q. TCP server是否一定要呼叫bind()?
A. 不一定,如果不呼叫bind(),系統會自動配置,後面再由getsockname()取得資訊

Q. bind()是甚麼意思?
A. 表示將protocol, IP, port number綁訂在一起,某種程度也是指定介面卡(思考有多張網路卡以及每張網路卡只有單一IP)

Q. UDP的客戶端不需要呼叫,也不可以呼叫bind()?
A. 不是,如前面所說,UDP也可以透過bind()指定介面卡(IP)

Q. close()會關閉連線?
A. 不會,close()會馬上返回程式,他只有關閉了read/write的fd,並且將該fd的reference count減一,只有當reference count為0的時候才會開始關閉連線(FIN),但也不是馬上關閉,kernel還是會嘗試將還存在queue的封包送出或者接收依舊在網路的封包,過程可以由setsockopt()的SO_LINGER控制,如果要馬上關閉必且忽略reference count限制可以呼叫shutdown()

Q. connect()只有TCP用得到,UDP必須使用recvfrom()以及sendto()?
A. connect()在UDP內依舊可以使用,只是語意不如TCP會完成3-way handshake,UDP呼叫connect是將sockaddr註冊給kernel,並取得一socket fd

Q. connect()在UDP使用的方式?
A. 如前面一問題,註冊後可取得socket fd,之後就可以用read()/write()函數來操作,同時,因為recvfrom()跟sendto()每次都要將sockaddr向kernel註冊,使用connect()則不用,在大量訊息傳送的時候使用connect()/read()/write()效能會優於使用recvfrom()跟sendto()

2013年2月16日 星期六

The art of readable code摘要 -- 特定題材


testing and readability (測試以及可靠性)
make tests easy to read and maintain (讓測試容易理解及維護)
測試可以讓人勇敢的修改程式碼,所以可以提升維護性

what’s wrong with this test? (測試發生了甚麼事?)
舉例下面的測試,並且慢慢修正

void Test1() {
    vector<ScoredDocument> docs;
    docs.resize(5);
    docs[0].url = "http://example.com";
    docs[0].score = -5.0;
    docs[1].url = "http://example.com";
    docs[1].score = 1;
...

 SortAndFilterDocs(&docs);
    assert(docs.size() == 3);
    assert(docs[0].score == 4);
    assert(docs[1].score == 3.0);
    assert(docs[2].score == 1);
}



making this test more readable (讓測試更具備可讀性)
文章建議將docs[0].property=xxx,包裹成function,命名為類似addDoc()比較有可讀性,更進一步或許可以建立起string to docs的function,方便產生資料

making error messages readable (使錯誤訊息更具備可讀性)
建議assert錯誤的時候可以輸出更有用的訊息

choosing good test inputs (選擇一個恰當的輸入)
建議簡化以及多種的輸入,做到良好的測試

naming test functions (為測試函數命名)
Test1沒有意義,測試命名要有意義

what was wrong with that test? (測試發生了甚麼事?)
回顧前面的問題

test-friendly development (友善的測試開發)
作者同意TDD (Test Driven Development)的基本想法,並且列出了許多有問題的特徵

going too far (想太多)
作者列出三點可能想太多的盲點

  • Sacrificing the readability of your real code, for the sake of enabling tests.
  • Being obsessive about 100% test coverage.
  • Letting testing get in the way of product development.


designing and implementing a “minute/hour counter” (實作 分鐘/小時 計數器)
the problem
defining the class interface
作者做一總結回顧,舉例以及技巧應用

================我的心得================
可以比較依些類似unit test的構想,以及M$談論測試的書籍,會發現測試真的是一個專門的事情,好比光100% coverage,在M$上是"必須"達到的事情,此外agile process有些建議則是不應該花太多精神在"照顧"test,test應該保持直觀、快速開發,比方說將docs[0].property=xxx包裝成為函數,可能就不大受歡迎,因為可能引入更多的bug,又要花更多時間去維護test,頗有得不償失的味道

The art of readable code摘要 -- 重整程式碼


extracting unrelated subproblems (分解問題)
aggressive是一個重要精神,讓functions可以組裝
introductory example: findClosestLocation() (範例)
將某些codes blocks依照他們工作的屬性獨立出來,並且賦予一個高階的function name,可以增加可讀性(refactoring: extra method)

pure utility code (純粹工具程式)
將一些常用的程式整合成一常見的function

other general-purpose code
同上,作者舉例javascript上一個format ajax response的function

create a lot of general-purpose code
project-specific functionality
開發工具functions

simplifying an existing interface (簡化已經存在的介面)
符合agile的open/close精神,減少不必要的接觸,就可以減少犯錯

reshaping an interface to your needs (從新修整介面成為你需要的模式)
refactoring(?)

taking things too far (不要考慮太多)
不要過分分割

one task at a time (一次只處理一件事情)
tasks can be small (分割工作)
將工作分割以及簡化成為一個小步驟

extracting values from an object (從分解數值看問題)
這部份建議看原著,有點類似refactoring
作者舉例javascript一個類似住址的結構變數假設就是addr,裡面有town, city, state, country,然而有一處理函數需要回傳[town|city|state], country,前三者選擇一個,優先權是town>city>state,直覺的寫法就是一串提取addr[town]...然後if ...,作者認為這樣子不大好,最好一次將次個結構提取出來,跟著做一串判斷
另一解法方式是類似如此

if (country === "USA") {
    first_half = town || city || "Middle-of-Nowhere";
    second_half = state || "USA";
} else {
    first_half = town || city || state || "Middle-of-Nowhere";
    second_half = country || "Planet Earth";
}
return first_half + ", " + second_half;
但作者提到這是熟悉javascript的方式

a larger example

turning thoughts into code (精煉你的想法)
describing logic clearly (清楚描述你的logic)
作者舉例之前Simplifying Loops and Logic的應用,顯示if~else需要簡化

knowing your libraries helps (讓你的函式庫有用)
書中描述一jquery範例實作網頁上tooltip顯示,一開始程式碼使用index跟slice()作為解析,更改的程式碼意圖表示用更抽象(藉由jquery)與一致的方式顯示tooltip。加上註解,這樣可以提高程式碼resue的機率

applying this method to larger problems (分割與處理)
作者提供了一個python處理transaction的方式,但是如果以直觀的方式寫code會出現很凌亂,書中把相似的片段加以集合以及問題加以分割

writing less code (精簡程式碼)
don’t bother implementing that feature—you won’t need it (不要實作你不需要的特色)
不要過分design

question and break down your requirements (處理你的需求)
不斷更新、改進需求

keeping your codebase small (保持程式碼的精簡)
愈少的程式碼愈好處理,必要時移除不必要的程式碼

be familiar with the libraries around you (工欲善其事,必先利其器)
花十五分鐘好好讀讀你用到的library

example: using unix tools instead of coding

===============我的感想===============
這裡提到的技巧都很像refactoring以及約耳趣談軟體內提到的,重複改善軟體(約耳並不鼓勵丟棄code),透過種種的精鍊,可以完成更好的code,除非本來的code真的一無可取

code改進好比改寫論文,必須要更加精簡,過程中就不斷地提升設計以用法,讓他們更精簡、更泛用,然則refactoring比較介於design與code之間,這本書則比較介於語法語code之間

此外很多程式設計師的意見並不一致,比方說這裡鼓勵使用人家的library,但是約耳點出,有時library也是要付出代價的,尤其是library往往也有他的bug,還有自己是否能控制library(指能夠理解並且改寫)。事實上library引用有時的確是很麻煩,好比tomcat最常遇到就是升級的時候一堆library需要升級的麻煩,之前專案的一堆程式碼是否需要改寫?此時問題已提升到configure management的範疇

The art of readable code摘要 -- 簡化迴圈與邏輯


making control flow easy to read (使得流程容易理解)
the order of arguments in conditionals

the order of if/else blocks

the ?: conditional expression (a.k.a. “ternary operator”)
建議不要使用,易懂比省空間重要得多

avoid do/while loops (避免使用避免使用do/while)
把condition放在前面比較好

returning early from a function (儘早回傳value)

the infamous goto (惡名昭彰的goto)
在linux kernel內有不少goto,但是他們使用goto通常是跳到"結尾",並不是當作插入流程控制

minimize nesting (減少loop nesting)
可以簡潔流程
是否你新增的程式碼夠清楚明白嗎?不要因為增加簡單處理而增加很多的理解程式是複雜度

can you follow the flow of execution? (你能夠理解流程嗎?)
一些高階的手法,有其必要的代價,如果可以的話,使用簡單的模式,避免使用他們


  • Programming construct=>How high-level program flow gets obscured
  • threading=>It’s unclear what code is executed when.
  • signal/interrupt=>handlers Certain code might be executed at any time.
  • exceptions=>Execution can bubble up through multiple function calls.
  • function pointers & anonymous functions=>It’s hard to know exactly what code is going to run because that isn’t known at compile time.
  • virtual methods=>object.virtualMethod() might invoke code of an unknown subclass.



breaking down giant expressions (分解太長的表示表示式)
explaining variables (示意變數)
if line.split(':')[0].strip() == "root": ...改為username = line.split(':')[0].strip()
讓一個變數來表達他的用意

summary variables (結論變數)
final boolean user_owns_document = (request.user.id == document.owner_id);
如果條件要反覆使用到,使用一個變數取代他

using de morgan’s laws (使用de morgan定理)
使用de morgan's laws化簡條件判斷式

abusing short-circuit logic (避免濫用"短路"邏輯)
避免誤用短路邏輯,因為短路邏輯會造成其他邏輯部分沒有執行,尤其如果邏輯判斷內的每個method/function都期望他們被執行到

example: wrestling with complicated logic
嘗試為複雜的邏輯找到優雅的表示法

breaking down giant statements  (拆解太長的表示式)
試著使用explaining variables跟summary variables來取代跟化簡太長的表示式

another creative way to simplify expressions (另一種簡化表示式的創意方式)
適當的使用MACRO來以更簡潔的方式簡化expression

variables and readability (變數以及可能性)
eliminating variables (減少變數)
如果使用了explaining variables以及summary variables,要注意是否這個變數有存在的必要,如果他們沒有長期存在的意義,或許直接使用expression會比較好

shrink the scope of your variables (縮小變數的範圍)
儘量減少variable的生命週期以及範圍,如果沒有存在的必要,就不要讓它存在

prefer write-once variables (使用常數)
多使用constant,因為愈少操作,出錯機率愈少

a final example
=============我的心得=============
這部分有很多語言相依的實例,但是有些已經開始牽涉到執行以及正確性了,不是單單可讀性的問題

比方說summary/explaining variables的使用,曾經有人建議不要使用,理由是,每一次使用summary/explaining variables等同增加犯錯的風險,他們堅持使用function call好過用variable傳遞。其實我覺得使用這些變數的確有這樣的風險,但是同時又可以增加效能以及可讀性,作者自己也在eliminating variables提出幾個類似反例例子

所以上面的技巧使用很端看場合決定,沒有一個絕對好的答案,都是有一些trade-off存在

至於有些部分我沒有節錄是因為感覺書上"技巧"實在有點太過over了

IP Header其實很多平台不一樣


IP header format defined in RFC 791:
 
   0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


IP header data sturcture in Linux:
/*  /usr/include/netinet/ip.h  */


struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;
    u_int16_t tot_len;
    u_int16_t id;
    u_int16_t frag_off;
    u_int8_t ttl;
    u_int8_t protocol;
    u_int16_t check;
    u_int32_t saddr;
    u_int32_t daddr;
    /*The options start here. */
  };


IP header data structure in ns2:
/*    src/ns-<ver>/common/ip.h  */

struct hdr_ip {
    /* common to IPv{4,6} */
    ns_addr_t    src_;
    ns_addr_t    dst_;
    int        ttl_;

    /* Monarch extn */
//     u_int16_t    sport_;
//     u_int16_t    dport_;
   
    /* IPv6 */
    int        fid_;    /* flow id */
    int        prio_;

    static int offset_;
    inline static int& offset() { return offset_; }
    inline static hdr_ip* access(const Packet* p) {
        return (hdr_ip*) p->access(offset_);
    }



IP header data structure in GloMoSim:
typedef struct ip {
    unsigned int ip_v:3,        /* version */
                 ip_hl:5,       /* header length */
                 ip_tos:8,      /* type of service */
                 ip_len:16;     /* total length */
    
    unsigned int ip_id:16,
                 ip_reserved:1,
                 ip_dont_fragment:1,
                 ip_more_fragments:1,
                 ip_fragment_offset:13;
                 
    unsigned char  ip_ttl;      /* time to live */
    unsigned char  ip_p;        /* protocol */
    unsigned short ip_sum;      /* checksum */
    long    ip_src,ip_dst;      /* source and dest address */
    
} IpHeaderType;


IP header data structure in OPNET:

/*  Data structure for fields in the */
/*  ip datagram.     */
typedef struct
    {
 OpT_Packet_Size  orig_len;
 int    ident;
 OpT_Packet_Size  frag_len; /* payload_length for IPv6 */
        /* For IPv6 pkts this does not include the length */
        /* of the fragmentation header.     */
 int    ttl;  /* hop_limit for IPv6  */
 InetT_Address    src_addr;
 InetT_Address    dest_addr;
 int    protocol; /* next_hdr for IPv6  */
 int    tos;  /* traffic_class for IPv6 */
 int    offset;  /* The offset of this fragment */


 /* The following two fields are for ECN - RFC#3168. */
 /* The ECT field is set in the datagram only if the */
 /* transport protocol is capable of handling the */
 /* explicit congestion notification.  The CE bit */
 /* actually contains information about congestion. */ 
 int     CE;
 int     ECT;

 /* The remaining fields are for  */
 /* simulation efficiency, and are  */
 /* not actual fields in a packet. */
 int    frag;
 int    connection_class;
 int    src_internal_addr;
 int    dest_internal_addr;
 IpT_Compression_Method  compression_method;
 OpT_Packet_Size    original_size;
 OpT_uInt8    options_field_set; /* Boolean field, uint8 is being used to conserve memory. */
 OpT_uInt8    tunnel_pkt_at_src; /* Boolean set to true in outer pkt at src of tunnel  */
 double    decompression_delay;
 InetT_Address  next_addr;

 /* The tunnel destination ip address*/
 /* is used to tunnel an ip datagram */
 /* in ip in the case of voluntary   */
 /* tunneling. It is NOT a standard  */
 /* field in ip.                     */
 InetT_Address    tunnel_end_addr;
 InetT_Address    net_addr_tunneled;
 void*    tunnel_ptr;
 double     tunnel_start_time;

 /* VPN Stamp time to hold the time when packet enters a VPN */
 double    vpn_stamp_time;
 
 /* Mobile IP related fields */
 int     icmp_type;
 int     encap_count;

 /* IPv6 extension headers container. */ 
 IpT_Ipv6_Extension_Headers_Data* ipv6_extension_hdr_info_ptr; 

    /* Field to store source stat handle, Number of Hops */
    Stathandle*    src_num_hops_stat_hndl_ptr;

 /* This field tells whether the packet has already been decrypted */
 /* by a HAIPE device and is being "re-recieved" on the same interface */
 Boolean    haipe_processing_performed;
 } IpT_Dgram_Fields;





Linux data structure defines fields in bits while simulators define them in common C data types other than bits. Since Linux, which directly runs on hardware, benefits from bit operation. But simulators cannot. So there's no need to bother fields in bits.

Comparing the structures in ns2 and OPNET, we find OPNET struture much more complex, since it supports many technologies in its IP module, including IP tunneling, MPLS, VPN, etc.

參考資料:
http://my.opera.com/Illidan/blog/ip-header-data-structure

The art of readable code摘要 -- 表面的改善


書本列出了一些撰寫code使得code更有可讀性的例子,其實如果從RUP的角度出發,有些事情會自然發生,就好比學過OOP很容易在設計DB就會自動符合一階正規劃(每個entity需要包含primary key),而不用再正規劃

packing information into names (讓變數名稱包含資訊) 

choose specific words(使用明確的字)
使用有明確的動詞跟名詞,比方使用fetchPage()或者downloadPage()取代getPage()
動詞上可以更明確一些,作者列出了若干個例子

  • send=>deliver, dispatch, announce, distribute, route
  • find=>search, extract, locate, recover
  • start=>launch, create, begin, open
  • make=>create, set up, build, generate, compose, add, new
avoid generic names like tmp and retval(避免太過泛稱的名詞)
避免太泛稱的名詞,比方tmp跟retval。loop裡面常用的i, j, k等等index,如果可以,也建議給予適當的名稱

prefer concrete names over abstract names(避免太過抽象的名稱)
儘量讓變數名稱貼合他使用的場合,比方run_locally表示local test的flag就不太合適,不如使用use_local_database

attaching extra information to a name(增加額外有意義的資訊)
讓名稱包含其他有意義的資訊,比方說string id與string hex_id,後者表明了id由hex組成
又或者包含單位,如int time與int time_seconds,後者表明了時間以秒為單位
又或者html與html_utf8,後者表明了encoding的方式

how long should a name be?(名稱會太長嗎?)
作者認為在有工具的幫助下,打long name不是問題;另外如果有效範圍很短的變數使用簡單的名稱也是可以的,如map m;

use name formatting to convey meaning
格式化變數的清晰程度,利用底線、破折號以及大小寫( _ , - , CapitalsAnd)來區分名稱

names that can’t be misconstrued (正確的建構變數名稱)
example: filter()
filter("year >=200")是include?還是exclude?本身名稱就具備模糊空間,不好

example: clip(text, length)
是剪下length長度的文字呢?還是將文字刪除為最長長度length?如果是後者應該使用Trancate(text,length)比較好

prefer min and max for (inclusive) limits(適當的範圍選擇)
prefer first and last for inclusive ranges
prefer begin and end for inclusive/exclusive ranges
作者建議使用數學上,首尾包含[1,100]表示1~100,儘量不要使用[1,100)表示1~99,然則STL的begin(), end()則是[1,100)

naming booleans(為boolean變數命名)
bool read_passwd不是一個好表示法,因為不知道是否是表示已經讀取或者未讀取,使用如disable_passwd或者use_ssl會是一個比較好的名詞

matching expectations of users(使用者非預期中的成本)
例如getMean()其實隱含了計算成本,可是使用者卻不知道,使用computeMean()或許會更好,或者STL中的size()會隨著容器中元素個數改變,有時會在回圈中引發錯誤
example: evaluating multiple name candidates

aesthetics (美學)
why do aesthetics matter?(為何美學重要?)
readable code的排版是重要的,一個格式很糟糕的code很難讀

rearrange line breaks to be consistent and compact (重新分配換行以及保持註解簡潔跟一致)
直接看圖,上者是好的,下者是不好的



use methods to clean up irregularity (將不規則的不分用方法加以包裹)
將複雜且不規則的方法加以包裹,使動作看起來一致

use column alignment when helpful (行對齊是有幫助的)
直接看圖

pick a meaningful order, and use it consistently (保持有意義的次序)
保持對齊,比方assign屬性的時候

organize declarations into blocks (組織適當宣告)
將相似或者功能相近的宣告集中成一個block

break code into “paragraphs” (將code分段)
將一串code依照功能分段落,並且加上適當的註解

personal style versus consistency (個人風格或者一致)
風格要一致比較重要

knowing what to comment (知道哪些東西該註解)
what not to comment (怎樣的註解不該寫)
不要寫出dummy的資訊,例如這是一個"汽車類別"這樣的註解。也不要直接註解重複註解變數名稱,註解不該用來修飾不好的變數或者方法的名稱,遇到這種狀況,請直接改掉他

recording your thoughts (記錄你的想法)
將思路寫下,也可以記錄你的修改過程,常數(constant)往往也是需要解釋為何這樣設計的

put yourself in the reader’s shoes (多替他人想想)
不要在註解內寫下問句,將可能遇到的問題寫下來。使用高階或者宏觀的方式寫下的註解也有助於新手了結程式碼

final thoughts—getting over writer’s block (克服寫作恐懼)
有些人認為寫好的註解很花時間,對症下藥就是~趕緊寫註解,然後只要避免重複的註解。過程: 想到就寫=>以後重讀(需要改進嗎?) =>改進

making comments precise and compact (保持註解正確跟簡潔) 
keep comments compact (保持註解簡潔)

avoid ambiguous pronouns (避免有疑慮的名詞)

polish sloppy sentences (避免註解過度肥大)

describe function behavior precisely (正確描述function的行為)
例如CountLines(),應該寫明是使用\n當計算單位?還是\n\r當計算單位?還是?

use input/output examples that illustrate corner cases (良好的舉例,顯示輸入輸出該注意的事項)

state the intent of your code (解釋你的意圖)
解釋你的意圖,不是解釋code的運作方式,code的運作應該是code本身顯示的

“named function parameter” comments (定義呼叫參數的意義)
當使用Connect(10,false),並無法得知參數的意義
在python可以寫
Connect(timeout = 10, use_encryption = False)
在C++則可以用
Connect(/* timeout_ms = */ 10, /* use_encryption = */ false);

use information-dense words (使用簡潔且資訊含量豐富的字眼)

=======================個人感想=======================
這個話題可能是永遠的話題,好比說,大家都知道註解很重要,但是絕大多數pogrammer的不寫註解。大多的人知道測試很重要,但是他們寧願相信自己的腦袋跟錯誤處理函數。大多人知道好的readable code風格很重要,但是pogrammer會持續使用自己特有的風格

然則這個沒有絕對的對錯,不過理解那些事情該做,才能夠適當的"客製化",好比現實專案大多不依照軟體工程來執行,但是理解軟體工程絕對對專案執行有幫助。好比如果專案很小,可以將需求使用拍照的方式記錄下來就好,不用在寫複雜的格式。在寫code上面也是一樣,有人推崇的方式,是code應該跟註解一樣,每一個function call本身就應該包含它的意義,不應該再添加太多的註解

此外,很多方法已經被研發出來,但是相對應的工具還不是那麼方便,最有名以及全面的工具應該是version control跟refactoring工具,version control不管是cvs, svn or git都是一個良好的工具,比自行壓縮,然後在檔案名稱上面附加上日期好很多,refactoring則是一種瑣碎的過程,如果IDE沒有配合的工具,我想光rename variable/method這件事情就很容易引入更多的bug

最後要表達的是,生產力以及品質之間有關連,但卻又是分開的。有些人會覺得很詭異,舉例來說,使用OOP或者Design Pattern可以提升生產力嗎?答案是否定的,工程是不會因為OOP或者Design Pattern產生新的功能出來,也就是對於用戶,使用lisp(not OOP)或者C++(OOP)是沒有分別的,但是正確使用兩者則可以提升開發品質,間接的會影響生產力,更容易維護的code是有幫助的,但幫助不是增加新功能

在換句話說,可以分為對內需求以及對外需求,對內(專案團隊),如何專案管理、如何測試、如何提升可維護性是很重要。但是對外(客戶),則是團隊交付了多少功能以及花費還有時間才是他們所期待的。這兩者不直接相關,但是兩者又互相影響

2013年2月14日 星期四

select()的細節

直接先看code

 1: #include <sys/types.h>
 2: #include <sys/select.h>
 3: #include <stdio.h>
 4: #include <stdlib.h>
 5: #include <string.h>
 6: 
 7: #define BUFSIZE (256)
 8: 
 9: int main(void){
10:     fd_set read_fdset;
11:     int maxfd=1;
12:     char buff[BUFSIZE];
13:     while (1) {
14:         FD_ZERO(&read_fdset);
15:         FD_SET(0, &read_fdset);
16:         int result = select(maxfd + 1, &read_fdset, NULL, NULL, NULL);
17:         if (0 > result){
18:             printf("select error\n");
19:             exit(1);
20:         }   
21: 
22:         if (FD_ISSET(0, &read_fdset)){
23:             printf("data is ready\n");
24:             int c=getc(stdin);
25:             printf("%c",c);
26:         }   
27:     }   
28:     return 0;
29: }
結果是?輸入test,按下兩次enter,得到的結果,如下圖
如果可以一眼看出答案,表示對select()有深入了解。容我賣個關子,這裡問題出在於stdio對於buffer以及kernel buffer的認知上面