---
# System prepended metadata

title: socket programming c in linux
tags: [Linux]

---

---
tags: Linux
---

# socket programming c in linux

## 目標

![](https://hackmd.io/_uploads/HJqsdzVvn.jpg)

完成 `socket`，在 `client` 端輸入，`server` 端回傳計算後的數學公式。

```bash=
// client端    // server端回傳
add 1 26      27
abs -100      100
mul 33 55     1815
unsupoort!!   Hello
kill
```

## 實作程式碼

### server.c

```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int process_command(char *buffer, char *response) {
    int a, b;
    char command[256];
    sscanf(buffer, "%s %d %d", command, &a, &b); // 解析從緩衝區讀取的命令
    
    // 根據命令生成回應
    if (strcmp(command, "add") == 0) {
        sprintf(response, "%d", a + b);
    } else if (strcmp(command, "abs") == 0) {
        sprintf(response, "%d", abs(a));
    } else if (strcmp(command, "mul") == 0) {
        sprintf(response, "%d", a * b);
    } else if (strcmp(command, "kill") == 0) { // 檢查是否是 "kill" 命令
        return 1; // 如果是 "kill" 命令，返回1，讓主迴圈停止
    } else {
        strcpy(response, "Hello");
    }
    return 0; // 正常情況下返回 0
}

int main() {
    int sockfd, newsockfd, portno = 5001;
    char buffer[256], response[256];
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t clilen;

    sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建一個新的 socket
    bzero((char *) &serv_addr, sizeof(serv_addr)); // 初始化伺服器地址結構
    serv_addr.sin_family = AF_INET; // 設定地址家族為 Internet
    serv_addr.sin_addr.s_addr = INADDR_ANY; // 允許任何來源的連接
    serv_addr.sin_port = htons(portno); // 設定端口號
    bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); // 綁定 socket 到給定的 IP 和端口號
    listen(sockfd, 5); // 開始監聽連接，並設定最大連接數為5
    clilen = sizeof(cli_addr);

    newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); // 接受連接，並獲得新的 socket 檔案描述符

    // 主迴圈
    while (1) {
        bzero(buffer, 256); // 清空緩衝區
        read(newsockfd, buffer, 255); // 從 socket 中讀取數據
        if (strlen(buffer) == 0) {
            break; // 如果讀取到的數據為空，退出迴圈
        }

        // 處理命令，如果處理結果為1，退出迴圈
        if(process_command(buffer, response)) { 
            break;
        }

        write(newsockfd, response, strlen(response)); // 將處理結果寫入 socket
    }

    close(newsockfd); // 關閉新的 socket 檔案描述符
    close(sockfd); // 關閉原始 socket 檔案描述符

    return 0; 
}

```

### client.c

```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>

int main() {
    int sockfd, portno = 5001, n;  // 定義 socket 檔案描述符、端口號和返回值
    struct sockaddr_in serv_addr;  // 定義伺服器地址結構
    struct hostent *server;  // 定義主機信息
    char buffer[256], response[256];  // 定義請求和回應緩衝區

    sockfd = socket(AF_INET, SOCK_STREAM, 0);  // 創建一個新的 socket
    server = gethostbyname("localhost");  // 獲取本地主機名

    bzero((char *) &serv_addr, sizeof(serv_addr));  // 初始化伺服器地址結構
    serv_addr.sin_family = AF_INET;  // 設定地址家族為 Internet
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);  // 拷貝主機地址到伺服器地址結構
    serv_addr.sin_port = htons(portno);  // 設定端口號

    connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));  // 連接到伺服器

    while (fgets(buffer,255,stdin) != NULL) {  // 循環讀取標準輸入
        buffer[strcspn(buffer, "\n")] = '\0';  // 去掉字串末尾的換行符
        if(strcmp(buffer, "") == 0) {  // 如果輸入為空，退出循環
            break;
        }

        write(sockfd, buffer, strlen(buffer));  // 寫請求到 socket

        if(strcmp(buffer, "kill") == 0) {  // 如果命令為 "kill"，退出循環
            break;
        }

        bzero(response, 256);  // 清空回應緩衝區
        read(sockfd, response, 255);  // 從 socket 中讀取回應
        printf("%s\n", response);  // 打印回應
    }

    close(sockfd);  // 關閉 socket

    return 0;
}

```

### Makefile

```makefile=
all: server client

server: server.c
	gcc -o server server.c

client: client.c
	gcc -o client client.c

clean:
	rm -f server client

```

### in.txt

```txt=
add 1 26
abs -100
mul 33 55
unsupoort!!
kill
```

### ans.txt

```txt=
27
100
1815
Hello
```

## 啟動服務

以下命令用於編譯服務器代碼並將編譯後的文件保存為 `server_node`，

```bash=
gcc server.c -o server_node
```

以下命令用於編譯客戶端代碼並將編譯後的文件保存為 `client_node`，

```bash=
gcc client.c -o client_node
```

然後可以在不同的終端運行 `server_node` 和 `client_node` 文件來查看輸出 下面的命令用於運行編譯後的文件

```bash=
./server_node
```

在單獨的終端中運行 `client_node`，並可在 `client` 端測試

```bash=
./client_node
```

## 測試

```bash=
make
./server & ./client < in > out
diff -s out ans
```

跑完測試後，如果一切正確，應該會看到以下消息：`Files out and ans are identical`。