---
tags: Linux
---
# socket programming c in linux
## 目標

完成 `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`。