網路程式設計練習題 (Practice - Network Programming)
Related Concepts
- 11-Network-Programming/01-Client-Server-Model-and-Networks
- 11-Network-Programming/02-Global-IP-Internet
- 11-Network-Programming/03-Sockets-Interface
- 11-Network-Programming/04-Web-Servers-and-Tiny
| 關鍵字 | 答案 |
|---|---|
| client-server transaction | 4 步驟:request → server 處理資源 → response → client 處理;client/server 是 process 不是 host |
| hub / bridge / router | 盲目複製 bit / 學習後選擇性轉送 frame / 查 routing table 轉送 packet 並換 frame header |
| network byte order | big-endian;htonl/ntohl(32-bit)、htons/ntohs(16-bit),無 64-bit 版本 |
inet_pton / inet_ntop |
p = presentation(dotted-decimal 字串)、n = network(binary);inet_pton 回傳 1/0/-1 |
| connection 三性質 | point-to-point、full duplex、reliable;由 socket pair 唯一識別 |
| listening vs connected descriptor | 前者建一次、活整個 server 生命期;後者每次 accept 建立;區分為了 concurrent server |
getaddrinfo / getnameinfo |
取代 gethostbyname 等;reentrant、protocol-independent;用畢 freeaddrinfo |
| HTTP 行尾 / request line | CRLF \r\n;method URI version;response line = version status-code status-message |
| status codes | 200 OK / 301 Moved / 400 Bad request / 403 Forbidden / 404 Not found / 501 Not implemented / 505 version |
| CGI | 參數經 QUERY_STRING;child dup2 把 stdout 重導到 connected descriptor;Content-type/length 由 CGI 程式自己產生 |
Question 1 - Client-Server Transaction [recall]
每個網路應用的基本操作單位是 client-server transaction。請依序寫出它固定的四個步驟,並說明:client 與 server 到底是機器還是 process?這種 transaction 具有 database transaction 的 atomicity 嗎?
四步驟:(1) client 送出 request;(2) server 收到後解讀並操作其資源;(3) server 送出 response 並等待下一個 request;(4) client 收到 response 並處理它。
client 與 server 都是 process,不是機器(機器稱 host);一台 host 可同時跑多個 client 與 server,transaction 也可發生在同一台 host 上。
它不具備 atomicity——client-server transaction 不是 database transaction,不共享其任何性質。
Question 2 - Hub vs Bridge vs Router [recall]
LAN 與 internet 中有三種連接元件:hub、bridge、router。請分別說明它們的轉送策略,並指出哪一個會讀取 packet header 中的 internet address、哪一個會改寫 frame header。
Hub:盲目複製每個 port 收到的每個 bit 到所有其他 port,每台 host 都看得到每個 bit。
Bridge:以分散式演算法自動學習「哪些 host 從哪個 port 可達」,只在必要時選擇性轉送 frame(同 segment 的 frame 直接丟棄),節省頻寬。
Router:連接不相容的 LAN/WAN;從 packet header 取出目的 internet address 作為 routing table 索引,剝掉舊 frame header、前置新 frame header 後轉送。
只有 router 理解 internet address,也只有 router 會改寫 frame header。
Question 3 - Byte Order 轉換函式 [recall]
TCP/IP 規定封包標頭中的整數(IP address、port)使用統一的 network byte order。請問這是哪種 endian?寫出 host ↔ network 轉換的四個函式與各自的位元寬度。有 64-bit 版本嗎?
Network byte order 是 big-endian;struct in_addr 中的位址永遠以 network byte order 存放,與 host byte order 無關。
四個函式:htonl / ntohl(32-bit)、htons / ntohs(16-bit);命名中 h=host、n=network、l=long(32-bit)、s=short(16-bit)。
沒有 64-bit 版本。
Question 4 - Connection 與 Socket Pair [recall]
Internet connection 讓兩個 process 以 byte stream 通訊。請寫出 connection 的三個性質、一條 connection 由什麼唯一識別,以及 ephemeral port 與 well-known port 的差別。
三性質:point-to-point(連接一對 process)、full duplex(資料可同時雙向流動)、reliable(byte stream 以相同順序送達)。
一條 connection 由 socket pair (cliaddr:cliport, servaddr:servport) 唯一識別。
client 端 port 由 kernel 自動分配,稱 ephemeral port;server 端用與服務永久關聯的 well-known port(如 http=80、smtp=25),映射記錄在 /etc/services。
Question 5 - Listening vs Connected Descriptor [recall]
server 端的 accept 會回傳一個新的 connected descriptor,而不是直接用 listening descriptor 通訊。請比較兩種 descriptor 的建立時機與存活期間,並說明
listen函式的作用與backlog參數的意義。
Listening descriptor:通常只建立一次,存活於 server 的整個生命週期,是 client 連線請求的端點。Connected descriptor:每次 accept 接受連線請求時建立,只在服務該 client 期間存在。
listen 把 kernel 預設的 active socket 轉為 listening socket,使其可接受連線請求。
backlog 是給 kernel 的提示:開始拒絕請求前應排隊的未完成連線請求數,通常設為大值如 1024。
Question 6 - getaddrinfo 與 getnameinfo [recall]
現代網路程式以
getaddrinfo與getnameinfo做主機/服務轉換。它們各取代了哪些過時函式?相比之下有哪兩大優點?getaddrinfo回傳什麼資料結構,用完必須做什麼?
getaddrinfo 取代 gethostbyname / getservbyname(字串 → socket address);getnameinfo 取代 gethostbyaddr / getservbyport(socket address → 字串)。
兩大優點:reentrant、protocol-independent(不依賴特定 IP 版本)。
getaddrinfo 回傳指向 addrinfo 結構 linked list 的指標,每個節點指向一個 socket address 結構;用畢必須呼叫 freeaddrinfo 釋放,否則 memory leak;錯誤碼用 gai_strerror(不是 strerror)轉字串。
Question 7 - HTTP 格式與 Status Codes [recall]
HTTP 是文字式應用層協定。請寫出:(1) 每個文字行的結尾字元;(2) request line 與 response line 的格式;(3) 以下 status codes 的意義:200、301、403、404、501、505。
(1) 每行以 CRLF(\r\n) 結尾。
(2) request line:method URI version;response line:version status-code status-message。
(3) 200 OK(處理無誤)、301 Moved permanently(內容移到 Location header 所指處)、403 Forbidden(無權限存取檔案)、404 Not found(找不到檔案)、501 Not implemented(不支援該 method)、505 HTTP version not supported。
Question 8 - Host Header 與 static/dynamic content [recall]
哪個 request header 在 HTTP/1.1 為必填、HTTP/1.0 不需要?它的用途是什麼?另外請說明 serving static content 與 serving dynamic content 的差別。
Host header:HTTP/1.1 必填。用途是讓 proxy chain 中間的 proxy cache 判斷自己是否有該內容的本地快取副本。
Static content:server 讀取磁碟檔案並回傳其內容。Dynamic content:server 執行可執行檔並回傳其執行期輸出。
對 client 與 server 而言,內容都是「byte 序列 + MIME type」。
Question 9 - IP Address 進位轉換 [application]
完成下列 dotted-decimal 與 hex 的互轉(hex 值為 network byte order 存放的 32-bit 位址):
(a)128.2.194.242→ 0x? (b)0xFFFFFF00→ ? (c)10.1.1.64→ 0x? (d)0x00000080→ ?
逐 byte 換算(每個十進位數換成一個 hex byte):
(a) 128=0x80、2=0x02、194=0xc2、242=0xf2 → 0x8002c2f2
(b) 0xFF=255、0xFF=255、0xFF=255、0x00=0 → 255.255.255.0
(c) 10=0x0a、1=0x01、1=0x01、64=0x40 → 0x0A010140
(d) 0x00、0x00、0x00、0x80=128 → 0.0.0.128
Question 10 - URL 解析與 Tiny 的判斷 [application]
瀏覽器送出請求
GET /cgi-bin/adder?15000&213 HTTP/1.0給 Tiny server。
(1) URI 中?與&各分隔什麼?若參數含空白該如何編碼?
(2) Tiny 如何判斷這個請求是 static 還是 dynamic?URI 開頭的/是 Linux 根目錄嗎?
(1) ? 分隔檔名與參數、& 分隔各個參數;空白必須編碼為 %20。
(2) Tiny 的 parse_uri 規則:URI 含字串 cgi-bin 即視為 dynamic(此例是),否則 static。
URI 開頭的 / 不是 Linux 根目錄,而是該類內容的 home directory(Tiny 的 static home dir 為 .、dynamic 為 ./cgi-bin);最小 suffix / 會被展開成預設首頁(Tiny 為 ./home.html)。
Question 11 - CGI 參數與輸出路徑 [application]
承上題,Tiny 收到
/cgi-bin/adder?15000&213後 fork 一個 child 執行 CGI 程式 adder:Setenv("QUERY_STRING", cgiargs, 1); Dup2(fd, STDOUT_FILENO); /* fd 是 connected descriptor */ Execve(filename, emptylist, environ);請說明:(1) adder 如何取得參數
15000&213?(2) adder 對printf寫出的內容為什麼會直接到達 client?(3)Content-type與Content-length這兩個 response headers 由誰產生?為什麼?
(1) child 在 execve 前把參數放進環境變數 QUERY_STRING,adder 用 getenv("QUERY_STRING") 讀取後自行以 & 切割。
(2) child 先用 dup2 把 stdout 重導到 connected descriptor 才 execve;CGI 程式繼承 execve 前已開啟的檔案與環境變數,所以寫到 stdout 的輸出不經 parent、直達 client(Practice Problem 11.5 觀念)。
(3) 由 CGI 程式(child)自己產生,包含終止 headers 的空行——因為 parent 不知道 child 產生內容的型別與大小。
Question 12 - 為何區分兩種 Descriptor [analysis]
有同學認為 accept 回傳新的 connected descriptor 是「多餘的複雜化」:server 直接用 listening descriptor 跟 client 通訊不就好了?請分析區分兩種 descriptor 的真正目的,並說明若 server 重啟時 bind 出現 "Address already in use",該用什麼解決、否則會怎樣。
區分的目的是支援 concurrent server:listening descriptor 持續存在、專門接收連線請求;每收到一個請求就得到一個獨立的 connected descriptor,server 可以 fork 新 process 用它與該 client 通訊,同時服務多個 client。若只有一個 descriptor,服務一個 client 期間就無法再接受新連線。
"Address already in use" 用 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, ...) 解決;否則被終止後重啟的 server 預設會拒絕 client 連線約 30 秒,嚴重妨礙除錯。
Question 13 - 已關閉連線的 write 與 EOF [analysis]
使用者在瀏覽器按下 Stop 關閉連線後,Tiny 仍繼續對該 connected descriptor 執行兩次 write。請分析:第一次與第二次 write 各發生什麼事?預設行為對 server 有何致命影響?強健的 server 應如何處理?另外,連線上的 EOF 是一個字元嗎?server 如何偵測?
第一次 write 正常返回;第二次 write 觸發 SIGPIPE,其預設行為是終止 process——iterative server 會就此死掉,無法服務後續 client。
強健作法:捕捉或忽略 SIGPIPE,此時第二次 write 改為回傳 -1 且 errno = EPIPE(Broken pipe),server 檢查並處理該錯誤即可。
EOF 不是字元,是 kernel 偵測的條件:對端 process 關閉其連線端後,本端在讀取 stream 最後一個 byte 之後的資料時,read/rio_readlineb 回傳 0,即偵測到 EOF。
| 題型 | 答題模式 |
|---|---|
| transaction 步驟 | 固定 4 步:request → 處理資源 → response → 處理 response;client/server 是 process;無 atomicity |
| 元件比較 | hub 盲目複製 bit;bridge 學習後選擇性轉送 frame;router 查 routing table、換 frame header |
| byte order | network byte order = big-endian;htonl/ntohl(32)、htons/ntohs(16),無 64-bit 版 |
| IP 進位轉換 | 逐 byte:每個十進位數 ↔ 一個 hex byte(128.2.194.242 = 0x8002c2f2) |
| connection | point-to-point + full duplex + reliable;socket pair 唯一識別;client ephemeral / server well-known port |
| descriptor 生命週期 | listening 建一次活到底;connected 每次 accept 一個;區分為了 concurrent server |
| getaddrinfo 系 | reentrant + protocol-independent;回傳 linked list;freeaddrinfo、gai_strerror |
| HTTP 格式 | 行尾 CRLF;method URI version / version status-code status-message;背熟六個 status codes |
| URL 解析 | ? 分檔名與參數、& 分參數、空白 %20;suffix 開頭 / = home directory 非根目錄 |
| CGI | QUERY_STRING + getenv;dup2 stdout → connected fd;Content-type/length 由 child 自己產生 |
| 錯誤情境分析 | 第二次 write 到已關閉連線 → SIGPIPE(預設終止)/EPIPE;EOF = read 回傳 0 的條件,非字元 |