---
# System prepended metadata

title: 計算機網路_HW3_410921216

---

# 計算機網路_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. 步驟３：你的程式需要送出訊息給 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. 步驟４：你的程式需要從 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代表程式執行成功結束。







