網路程式設計 作業五

Last updated:

此次作業要求你改寫作業三的 TCP Server 程式, 加入 UDP Client 功能,以完成通訊要求。

此程式應先啟動好 TCP Concurrent Server 功能,然後建立一個 UDP Socket,先送一個 UDP Segment 給作業五的 UDP Server (IP 位址為 140.127.220.246, port = SERV_PORT + 2)。 此 Segment 帶有你的學號與 TCP server 的 port number,以空格字元隔開。 UDP Server 收到後,會進行下列步驟:

  1. 設定一個1至10的亂數,令為 n
  2. 將收到的學號,UDP Server 偵測到的你的機器的 IP 位址,連同 n 值以空格字元隔開回傳給你的 UDP Client。
  3. 等待你用 UDP 送回一個狀態代碼(以字串表示的整數)。如狀態代碼為 "13",則繼續進行下列流程。否則放棄後續動作。
  4. 建立 n 個 TCP Client,與你的 TCP Concurrent Server 建立連結。 每個 TCP 連結建立後 TCP Client 會送出一個介於1至1024間的數字,令為 x。 你的 TCP Server 需設法取得此 TCP Clinet 在 Client 主機當地的 port number,並將此 port number 與 x 以空格字元隔開傳回。 如果 port number 和數字都正確,TCP Client 會回傳 "ok" 並結束,如果不正確則會回傳 "nak" 並結束。
  5. 如果每個 TCP Client 都得到正確的結果。UDP Server 最後會傳回 "ok", 如果有任何一個 TCP 連結得到不正確的結果,UDP Server 會傳回 "nak"。
一個正確的範例執行流程 如下圖所示(此例中你執行程式的機器 IPv4 位址為 140.126.220.48)。

假設我們先將作業三的 server 程式 hw3serv.c 複製成為另一個 hw5cli.c。接下來 你可以用 Unix 下的 vi 或 GNOME 桌面下的 gedit 來編輯此程式。 事實上你要做的事情如下:
  1. 建立一個 TCP listening Socket 成為 TCP Server (建議 Port 用 SERV_PORT+3)。
  2. 建立另一個 UDP Socket,將你的學號與 TCP Server 的 port number 以空格字元隔開 送至此作業的 UDP Server。
  3. 從 Server 的回傳得知需 Accept 多少個 Connection 並 Fork 多少個 Child Processes。 每個 Child Process 所要做的動作如上面的說明。(提示:TCP Server 可用 getpeername 函數取得 Client 的 port number)
  4. 最後從 UDP Server 收最後的結果 (ok or nak)。
  5. 為了避免你的 Child Process 變成 Zombie,你應該設定 SIGCHLD 的 signal handler。 這部份程式碼可參考作業四 Server 程式
修改完程式後,請參考此網頁來編譯你的程式

如果編譯沒問題,就可以執行你的這個 client 程式了。目前老師的 server IP 位址為 140.127.220.246。你可以在 client 程式中加適當的 output 訊息以瞭解 連線過程是否順利。如以下的執行範例:


lhyen@m622:~/unpv13e/udpcliserv$ ./hw5cli15 140.127.220.246
sent [to 140.127.220.246, port 9879]: lhyen 9880
recv: id = lhyen, ip address = 140.127.220.246, #connections = 10
ip address match 
sent code 13 to server
[Wed May 27 15:07:45 2015] connection 0 (from 140.127.220.246, port 37409)
connection 0 recv: 587
[Wed May 27 15:07:45 2015] connection 1 (from 140.127.220.246, port 37410)
connection 0 sent: 37409 587
[Wed May 27 15:07:45 2015] connection 2 (from 140.127.220.246, port 37411)
[Wed May 27 15:07:45 2015] connection 3 (from 140.127.220.246, port 37412)
connection 1 recv: 845
connection 1 sent: 37410 845
connection 1 recv: ok
[Wed May 27 15:07:45 2015] connection 4 (from 140.127.220.246, port 37413)
[Wed May 27 15:07:45 2015] connection 5 (from 140.127.220.246, port 37414)
connection 2 recv: 857
[Wed May 27 15:07:45 2015] connection 1 closed
connection 0 recv: ok
connection 2 sent: 37411 857
[Wed May 27 15:07:45 2015] connection 6 (from 140.127.220.246, port 37415)
[Wed May 27 15:07:45 2015] connection 0 closed
connection 2 recv: ok
connection 3 recv: 286
[Wed May 27 15:07:45 2015] connection 2 closed
[Wed May 27 15:07:45 2015] connection 7 (from 140.127.220.246, port 37416)
connection 3 sent: 37412 286
connection 3 recv: ok
[Wed May 27 15:07:45 2015] connection 3 closed
connection 4 recv: 641
connection 4 sent: 37413 641
connection 4 recv: ok
[Wed May 27 15:07:45 2015] connection 4 closed
connection 5 recv: 82
[Wed May 27 15:07:45 2015] connection 9 (from 140.127.220.246, port 37418)
[Wed May 27 15:07:45 2015] connection 8 (from 140.127.220.246, port 37417)
connection 5 sent: 37414 82
connection 5 recv: ok
connection 6 recv: 90
[Wed May 27 15:07:45 2015] connection 5 closed
connection 6 sent: 37415 90
connection 7 recv: 301
connection 6 recv: ok
connection 7 sent: 37416 301
[Wed May 27 15:07:45 2015] connection 6 closed
connection 7 recv: ok
connection 8 recv: 206
[Wed May 27 15:07:45 2015] connection 7 closed
connection 8 sent: 37417 206
connection 8 recv: ok
connection 9 recv: 869
[Wed May 27 15:07:45 2015] connection 8 closed
connection 9 sent: 37418 869
connection 9 recv: ok
[Wed May 27 15:07:45 2015] connection 9 closed
recvfrom [140.127.220.246, port 9879]: ok
lhyen@m622:~/unpv13e/udpcliserv$ 
server 程式會記錄連線的時間, IP 位址, port號碼等訊息,也會記錄收到的資料。為了確認你的 程式沒有問題,你應該下載安裝 server 程式,自行在本地端測試成功之後, 再正式與此作業的 Server 連線。我將以 server 程式的連線記錄檔來作為程式是否撰寫成功與否的依據。

與之前的作業不同,此次你的 UDP Clinet 程式會啟動 TCP Server,因此如果你執行完你的程式後 ,在很短的時間內又再度執行一次,很可能會看到下列訊息:

lhyen@m622:~/unpv13e/udpcliserv$ ./hw5cli15 140.127.220.246
bind error: Address already in use

這是因為 TCP Server 還在 TIME_WAIT 狀態 (參考課本 Fig. 2.4)。你所要做的只是稍微等一下。

提醒各位:在此作業的 Server 程式中,TCP Client 送出來的資料最後均有 '\n' 字元,它也 期待各位的 TCP Server 送給它的資料也是如此。但是我們的 UDP Server 送收資料最後 均沒有 '\n' 字元。

另外,還是要記得 Read 和 Recvfrom 這類函數從 socket 讀資料進來時, 並不會自動在資料最後面加 '\0'。 所以如果要呼叫字串相關函如 strlen, strcmp 對此資料作運算時, 記得要自行在讀進資料最後面加上 '\0' 才會正確。

還有,對於 value-result argument,一定要在呼叫函數前正確設定它的值,否則得不到正確的結果。 在下面的程式碼中,如果拿掉第一列的assignment敘述,就跑不出正確的結果。


clilen = sizeof(cliaddr);
n = Recvfrom(sockfd, recvline, MAXLINE, 0, (SA *) &cliaddr, &clilen);

另外,當 UDP Server 傳送給你學號、你的機器的 IP 位址、以及 n 值時,請先確認學號與 IP 位址與你認知的是否一致。請你自行用 getsockname 取得本身 IPv4 位址, 若此位址與老師的 UDP Server 偵測到的你的機器的 IPv4 位址不同,則表示你的機器可能位於 NAT 內部,此時老師的程式就無法連到你的 TCP server 的 port 了 (無法穿透 NAT)。此時記得用 UDP 回傳 "13" 以外的狀態代碼,如 "21" (但避免傳 "0")。此次上傳作業即算失敗,必須重來。

同學常見的問題是 local 測試 ok 但連上老師伺服器就不行,這就可能是 NAT 造成的問題。解決之道是將你寫好 local 測試 ok 的程式帶到系上電腦教室重新編譯執行(記得教科書中的程式原始檔也要全部安裝設定好 才能編譯你的程式喔)。不過目前系上電腦教室有部份電腦亦是使用私有 IP位址,此種電腦 也不能成功執行,所以要先確認所使用電腦IP位址是140.127.*.*這種的才行喔。

最後,如果你的 UDP 訊息與其他使用者的 UDP 訊息混在一起,UDP Server 會回傳 "conflict"。這也算上傳作業失敗,必須重來一次。

Maintained by Li-Hsing Yen.

Please report any mistakes/typos.