# 計算機網路_HW3_410921216 [程式碼連結](https://ideone.com/IPMACq) :::spoiler 程式碼 ``` c #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #define SERVER_IP "134.208.2.60" #define SERVER_PORT 9999 struct Msgbuf { int mtype; char context[20]; }; int main() { int s; struct sockaddr_in server; char server_reply[2000]; struct Msgbuf msg; char student_id[] = "410921216"; char key[10]; int i; // Create socket if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Could not create socket"); return 1; } // Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(SERVER_IP); server.sin_port = htons(SERVER_PORT); // Connect to server if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("Connect failed"); return 1; } // Step 1: Send student ID to server msg.mtype = 101; strcpy(msg.context, student_id); send(s, (char *)&msg, sizeof(msg), 0); // Step 2: Receive key from server if (recv(s, server_reply, sizeof(server_reply), 0) < 0) { perror("Receive failed"); return 1; } msg = *(struct Msgbuf *)server_reply; if (msg.mtype == 201) { printf("Received key: %s\n", msg.context); strcpy(key, msg.context); } else if (msg.mtype == 401) { printf("\nError: student ID not found\n"); close(s); return 1; } // Step 3: Send XOR result to server for (int i = 0; i < 8; i++) { key[i] ^= student_id[i + 1]; } msg.mtype = 300; sprintf(msg.context, "%s", key); send(s, (char *)&msg, sizeof(msg), 0); // Step 4: Receive pass/fail result from server if (recv(s, server_reply, sizeof(server_reply), 0) < 0) { perror("Receive failed"); return 1; } msg = *(struct Msgbuf *)server_reply; if (msg.mtype == 203) { printf("\nPass\n"); } else if (msg.mtype == 403) { printf("\nFail\n"); } // Cleanup close(s); return 0; } ``` ::: ## 講解影片連結: [影片連結](https://youtu.be/G1Ts0nm7II4) ## 直行結果: Received key: 61212901 Pass ## 程式碼詳細解釋 1. 標頭檔 ``` c #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #define SERVER_IP "134.208.2.60" #define SERVER_PORT 9999 ``` <arpa/inet.h>:該標頭檔主要用來進行網路位址與二進位表示法之間的轉換,例如 inet_addr() 函式用來將 IPv4 位址從純文字表示法轉換為整數表示法。 <stdio.h>:該標頭檔包含了一些用於輸入和輸出的函式,例如 printf() 和 puts()。 <string.h>:該標頭檔主要包含了一些字串處理函式,例如 strcpy() 和 strlen()。 <sys/socket.h>:該標頭檔包含了許多網路程式需要使用的函式、資料結構和定義,例如 socket()、connect() 和 struct sockaddr。 <unistd.h>:該標頭檔包含了用於系統呼叫 (system call) 的函式,例如 close()。 接著程式定義了兩個常數: SERVER_IP:代表遠端伺服器的 IPv4 位址。 SERVER_PORT:代表遠端伺服器的通訊埠號 (port number)。 2. 定義結構 ``` c struct Msgbuf { int mtype; char context[20]; }; ``` 這是一個名為Msgbuf的結構體,它包含兩個成員變數。第一個成員變數是一個整數,名為mtype,代表訊息的類型。第二個成員變數是一個長度為20的字元陣列,名為context,代表訊息的內容。這個結構體可以用來封裝和解析訊息。 3. 定義變數 ``` c int main() { int s; struct sockaddr_in server; char server_reply[2000]; struct Msgbuf msg; char student_id[] = "410921216"; char key[10]; int i; ``` **s** 是一個整數型態的變數,用來代表 socket; **server** 是一個 sockaddr_in 結構型態的變數,用來存放伺服器的資訊; **server_reply** 是一個長度為 2000 的字元陣列,用來接收從伺服器回傳的資訊; **msg** 是一個 Msgbuf 結構型態的變數,用來封裝從伺服器傳來的訊息; **student_id** 是一個字元陣列,存放學號; **key** 是一個長度為 10 的字元陣列,用來存放從伺服器取得的 key; **i** 用來迴圈計數。 4. 建立socket ``` c // Create socket if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Could not create socket"); close(s); return 1; } ``` 這一段程式碼是用來建立一個 TCP 連線用的 socket。 **AF_INET** 是代表 IPv4 的網路協定族 (Address Family)。 **SOCK_STREAM** 是代表 TCP 的一種傳輸協定。socket() 系統呼叫會傳回一個文件描述符 (file descriptor),這個描述符將被用來操控後續的 socket 通訊。 如果 socket() 呼叫失敗,會傳回 -1,程式會印出 "Could not create socket" 的錯誤訊息,釋放資源,回傳1結束程式。 5. 準備連線所需資訊 ``` c // Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(SERVER_IP); server.sin_port = htons(SERVER_PORT); ``` 這部分是設定伺服器的IP位址和Port。 **server.sin_family = AF_INET**表示使用IPv4網路協定。 server.sin_addr.s_addr = inet_addr(SERVER_IP) 設定伺服器的IP位址。 server.sin_port = htons(SERVER_PORT) 設定伺服器的Port,將主機字節序轉換成網路字節序。 6. 建立連線 ``` C // Connect to server if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("Connect failed"); close(s); return 1; } ``` 這段程式碼是用來建立一個連接至伺服器的socket。 如果建立socket失敗,會印出一個錯誤訊息並結束。 7. 步驟 1:你的程式需要與 TCP server 連通,且 mtype 為 101,而 context 填入你的學號。 ``` c // Step 1: Send student ID to server msg.mtype = 101; strcpy(msg.context, student_id); send(s, (char *)&msg, sizeof(msg), 0); ``` socket與server建立連線後,進行步驟 1。 將學號資訊寫入自訂的msg資料結構中,設定mtype為101,表示是傳送學號的訊息類型,並透過send函式將此資料結構傳送至server端。 8. 步驟 2:你的程式將會收到回應訊息。當你的學號有在 server 的姿料庫時,收到的訊息 mtype 是 201,context 是鑰匙。否則 mtype 是 401,context 是”-1”。 ``` c // Step 2: Receive key from server if (recv(s, server_reply, sizeof(server_reply), 0) < 0) { perror("Receive failed"); close(s); return 1; } msg = *(struct Msgbuf *)server_reply; if (msg.mtype == 201) { printf("Received key: %s\n", msg.context); strcpy(key, msg.context); } else if (msg.mtype == 401) { printf("\nError: student ID not found\n"); close(s); return 1; } ``` 程式從伺服器接收一個 Msgbuf 結構體, 如果收到的訊息類型是 **201**,則程式會印出接收到的密鑰,並將其存入 key 陣列中; 如果收到的訊息類型是 **401**,代表找不到學生ID,則程式會印出錯誤訊息並結束。 9. 步驟3:你的程式需要送出訊息給 server,且 mtype 為 300, context 填入鑰匙與你的學號後八個數字的 XOR 結果。 ``` c // Step 3: Send XOR result to server for (int i = 0; i < 8; i++) { key[i] ^= student_id[i + 1]; } msg.mtype = 300; sprintf(msg.context, "%s", key); send(s, (char *)&msg, sizeof(msg), 0); ``` 進行 XOR 運算,將學號後八碼(10921216)和從伺服器收到的 key (61212901)進行 XOR 運算,得到加密後的結果,然後將結果以 Msgbuf 的格式存到 msg 變數中的 context 欄位。 接著將 mtype 設為 **300**,代表要傳送 XOR 結果給伺服器。最後使用 send 函數將 msg 傳送到伺服器。 10. 步驟4:你的程式需要從 server 接收訊息,這訊息的 mtype 為 203 表示你通過此次測驗,否則為 403且表示失敗。 ``` c // Step 4: Receive pass/fail result from server if (recv(s, server_reply, sizeof(server_reply), 0) < 0) { perror("Receive failed"); close(s); return 1; } msg = *(struct Msgbuf *)server_reply; if (msg.mtype == 203) { printf("\nPass\n"); } else if (msg.mtype == 403) { printf("\nFail\n"); } ``` 使用 recv() 函數從伺服器接收資料,接收到的資料存在 server_reply 陣列中,接著將接收到的資料轉換成 Msgbuf 格式,再從該格式中讀取 mtype 值。 如果 mtype 的值為 **203**,則表示通過測驗,印出 "Pass"; 如果 mtype 的值為 **403**,則表示測驗失敗,印出 "Fail"。 11. 結束 ``` c // Cleanup close(s); return 0; } ``` 最後關閉socket並釋放資源。 close(s)會關閉已連接的socket,並且將該socket指派的資源釋放。 回傳0代表程式執行成功結束。