Chapter 1
伺服器端
1: #include "unp.h"
2: #include <time.h>
3:
4: int
5: main(int argc, char **argv)
6: {
7: int listenfd, connfd;
8: struct sockaddr_in servaddr;
9: char buff[MAXLINE];
10: time_t ticks;
11:
12: listenfd = Socket(AF_INET, SOCK_STREAM, 0);
13:
14: bzero(&servaddr, sizeof(servaddr));
15: servaddr.sin_family = AF_INET;
16: servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
17: servaddr.sin_port = htons(13); /* daytime server */
18:
19: Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
20:
21: Listen(listenfd, LISTENQ);
22:
23: for ( ; ; ) {
24: connfd = Accept(listenfd, (SA *) NULL, NULL);
25:
26: ticks = time(NULL);
27: snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
28: Write(connfd, buff, strlen(buff));
29:
30: Close(connfd);
31: }
32: }
主要流程填寫sockaddr_in結構,socket()=>bind()=>listen()=>accept()=>read()/write()=>close()
客戶端
1: #include "unp.h"
2:
3: int
4: main(int argc, char **argv)
5: {
6: int sockfd, n;
7: char recvline[MAXLINE + 1];
8: struct sockaddr_in servaddr;
9:
10: if (argc != 2)
11: err_quit("usage: a.out <IPaddress>");
12:
13: if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
14: err_sys("socket error");
15:
16: bzero(&servaddr, sizeof(servaddr));
17: servaddr.sin_family = AF_INET;
18: servaddr.sin_port = htons(13); /* daytime server */
19: if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
20: err_quit("inet_pton error for %s", argv[1]);
21:
22: if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
23: err_sys("connect error");
24:
25: while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
26: recvline[n] = 0; /* null terminate */
27: if (fputs(recvline, stdout) == EOF)
28: err_sys("fputs error");
29: }
30: if (n < 0)
31: err_sys("read error");
32:
33: exit(0);
34: }
流程為填寫sockaddr_in結構,socket()=>connect()=>read()/write()=>close()Chapter 2
重要的為TCP以及UDP的特色,以及richard stevens書中那張state diagram,中間牽涉到如何透3-way handshake建立連線,以及何時會斷線,為何TIME_WAIT狀態要等2MSL
- 個人認為重要的觀念是TCP/UDP為全雙工,也就是系統如何處理這樣的概念,從這裏面衍生出了不少問題
- TCP使用socket pair的概念來辨識連線,也就是Server IP+Server Port+Client IP+Client Port,因為UDP沒有連線概念的約束(也可以自行實作,但是TCP天生幫programmer處理這些)
- 同時開始考慮,一台機器可能不只一個網路介面,或者說不只一張網路卡,一個service(如FTP)可以同時在所有卡啟動,只要指定socket結構內為INADDR_ANY即可,如果要綁定數張,就只能一個一個來
- 另外書中提到kernel緩衝區的可以用SO_SNDBUF/SO_RCVBUF(setsockopt())來調整,TCP因為有window size的關係,照理不應該有buffer用盡的問題,但是UDP卻會,又加上UDP是unreliable,buffer一旦滿了就只會drop,連通知都不會有
Chapter 3
- 因為unix/linux設計socket不只可以用在ethernet還可以用在domain socket上面(一種IPC),所以網路上用的是sockaddr_in結構但是在connect()/bind()/accpet()...參數用的卻是sockaddr結構(一種所謂generic socket結構),所以在呼叫這些api往往形態要轉型
- bind()、connect()、sendto()是將資料由user space傳送到kernel space
- accept()、recvfrom、getsockname()、getpeername()則是由kernel space回傳資料到user space
- byte order的問題,網路上使用的是big-edian,但是intel cpu是little-edian,所以有這問題,通常有四個函數來處理htons()、htonl()、ntohs()、ntohl(),h表示host、n表示network、s表示16bits、l表示32bits
- 新的由IP字串取得address的function為intet_pton()以及反向函數inet_ntop()其中p表示presentation
- 作者時做了好一些輔助函數,很有參考價值
Chapter 4
重點之一,流程圖,書中逐一解說各個function用途
- socketaddr_in要轉型的原因如前一章,另外要注意,因為可以合乎各種family,所以在connect()/bind()/accept()除了轉型之外,還需要傳入結構大小的指標
- 這裡衍生出一個重要的觀念,TCP server一定要bind()嗎?答案是不一定,可以由系統指派,之後在用getsockname()取回sockaddr得知,而client的sockaddr資訊則可以用getsockpeername()取回
- fork()解決一小部分 socket為了全雙工所面臨的問題,因為程式不能因為系統呼叫而block住
chapter 5
- 書中提到了系統的問題,也就是signal會干擾正常流程,以及如何避免child process變成zombie的問題,使用waitpid()來解決,wait()因為kernel不queue signal可能還是會產生zombie
- SIGPIPE有可能是因為網路上遇到RST訊號,這屬於網路問題,但是unix反應到process上變成signal,必須妥善處理才能避免連線的問題
chapter 6
- 解說了blocking/nonblocking IO跟IO multiplexing、還有signal IO、asynchronous IO
- select()函數示範了IO multiplexing,這個函數可以簡單的處理網路的IO以及一般的輸入,但是中間呼叫其他system call還是有可能會block整個process,所以要小心
- select() -- read ready
- 正常狀況,資料高於低水位,可以透過setsockopt()的SO_RCVLOWAT調整低水位
- 對方已經關閉連線,會回傳EOF
- 在完成connected的queue中有可用的socket
- 有sock error存在,read()回傳-1,利用getsockopt()取得SO_ERROR資料
- select() -- write ready
- TCP在socket已經連線的時候可以寫入,或者UDP/TCP寫入資料多於低水位的時候,使用setsockopt()的SO_SNDLOWAT調整
- 連線端已經關閉寫入,會產生SIGPIPE
- 網路有sock error存在,write()回傳是-1,利用getsockopt()取得SO_ERROR資料
- shutdown()函數以及setsockopt()的SO_LINGER可以調整關閉socket時候的動作,必須考慮到網路的封包。
- close()只是關閉write/read fd並沒有關閉網路連線(或者TCP沒有送出FIN)
介紹常見的socket options,分成幾個層次,一般socket options、IPv4 options、IPv6 options、ICMP options、TCP options。一般socket options指的是大多與protocol獨立無關,由kernel透過與protocol無關的code設定。
我摘要了幾個前面常常碰見的options,有些options大多集中在書本前面,且偏向一般socket options
- SO_BROADCAST : 廣播
- SO_ERROR : 網路出錯
- SO_KEEPALIVE : 兩個小時透過封包確定TCP連線還存活著
- SO_LINGER : FIN訊號的處理
- SO_RCVBUF/SO_SNDBUF : 設定TCP/UDP的buffer,TCP不用特意調整,他有window size機制
- SO_RCVLOWAT/SO_SNDLOWAT : 資料的低水位控制,有關效率以及select()
- SO_REUSEPORT :
- SO_RESUEADDR : 只要IP不同,特別不用等待2MSL就可以重新bind()
- TCP_KEEPALIVE : 要開啟這個選項就要同時開啟SO_KEEPALIVE
- TCP_NODELAY : nagle演算法實作
Chapter 8
UDP介紹,如同TCP一樣,有張流程圖,不過不是一定的
- 不是一定的理由是,用戶端可以呼叫bind()跟connect(),當然兩者也可以都忽略
- 書中探討到了UDP失去了TCP特色的問題,比方UDP無法得知連線IP/Name,UDP沒有流量控制,UDP沒有重傳機制
- UDP因為不做3-way handshake,所以無法確定client/server是透過哪個IP或者介面卡傳輸,兩邊都可以從自己主機上的任一IP或者介面卡回應另一方
- UDP可以透過IP比對,或者DNS比對(多個IP對應到單一domain name),來驗證資料來源
- UDP可以透過前一點機制與Timeout設定來確定封包有沒有送達
- UDP可以透過SO_RCVBUF改善流量問題
- UDP的好處之一,可以透過一個client同時對多個server提出要求,但是會引發一個問題是ICMP的訊息將是非同步產生
- 其他可以參考我之前寫的socket FAQ
沒有留言:
張貼留言