# 計算機網路_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代表程式執行成功結束。