2024-03-05
大学课程
00

目录

1 应用层协议说明
2 代码
3 开发过程中的问题及解决方法
4 v2版本代码
5 追加信息
5.1 VS Code的SSH配置
5.2 WSL的防火墙问题
5.3 Debian换源
6 最终版代码
6.1 服务端代码
6.2 客户端代码
7 最终版又来了 UDP版本
7.1 服务端代码
7.2 客户端代码

实验目标:模拟一个分布式计算系统,发布者Server:仅1个,它用于发布计算任务。承担者Client:多个,用于向发布者请求任务,发布者向其发布计算数据后,进行本地计算,完成后,向发布者提交结果。

1 应用层协议说明

  1. 每次传输的内容规定如下:

    字节位置1234...
    传输内容校验码命令码承担者编号数据部分...
  2. 校验码计算方法:

    目前直接填充24即可,没有其他说明。

  3. 命令码说明

    1. CMD_HELLO,注册,C->S,仅1次,数据交互由此开始。其格式为,首部的编号为0,数据部分组成为:数据长度(1B),C主动发送的信息,此处为主机名称。

    2. CMD_HELLOOK,分配编号,S->C,在收到CMD_HELLO后发送,仅1次,编号部分为S分配的结果,数据部分为空。

    3. CMD_TASK,请求计算任务,C->S,多次,数据部分为空。

    4. CMD_TASKOK,下发计算任务,S->C,多次,在收到CMD_TASK之后,S分配任务,返回数据,数据部分为:计算开始值(4B),计算结束值(4B)。若不存在有效的计算任务,开始值和结束值均为0,否则两者均大于0且结束值>开始值。

    5. CMD_UPLOAD,上报计算结果,C->S,多次,每次完成计算后进行,数据部分为:计算开始值(4B),计算结束值(4B),计算结果(4B)

    6. CMD_UPLOADOK,计算结果上报反馈,S->C,在收到UPLOAD之后发送,格式与CMD_UPLOAD完全相同。

    7. CMD_NOTIFY,使用UDP广播和组播时,S通知所有C,C收到后开始发起CMD_HELLO进行后续通信,仅1次。数据部分为空。

  4. 命令码定义

c
#define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7
  1. 一次完整的计算任务分配过程演示
sequenceDiagram
Server ->> Client: CMD_NOTIFY 广播到所有Client
Client ->> Server: CMD_HELLO 注册
Server -->> Client: CMD_HELLOOK 分配编号
loop 一次计算周期
    Client ->> Server: CMD_TASK 请求计算任务
    Server -->> Client: CMD_TASKOK 下发计算任务
    Client ->> Server: CMD_UPLOAD 上报计算结果
    Server -->> Client: CMD_UPLOADOK 计算结果上报反馈
end

2 代码

  1. 服务端代码
c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 1324 // 服务端绑定端口 #define IPADDR "192.168.152.17" // 服务端绑定IP #define CLI_PORT 1325 // 因为CMD_NOTIFY需要广播 而广播需要指定接收者的端口号 所以client也必须指定端口 #define BROADCAST_IP "192.168.152.255" //广播IP int main(int argc, char *argv[]) { srand(time(NULL)); // 初始化随机数种子 int server_socket = socket(AF_INET, SOCK_DGRAM, 0); if (server_socket == -1) { perror("Create Socket Failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IPADDR); int ret = bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(server_socket); return -1; } struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); ssize_t bytes_send; ssize_t bytes_recv; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); // 清空缓存区 printf("Server is Ready!\n"); // 先广播CMD_NOTIFY int val = 1; if (setsockopt(server_socket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) == -1) { perror("Boardcast Denied: \n"); close(server_socket); return -1; } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(CLI_PORT); client_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); buffer[0] = 24; buffer[1] = CMD_NOTIFY; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_NOTIFY failed: \n"); close(server_socket); return -1; } printf("CMD_NOTIFY is Sent\n"); memset(buffer, 0, sizeof(buffer)); // 清空缓存区 // 进入循环 开始接收消息并且返回 while (1) { // 开始接收CMD_HELLO消息 bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive CMD_HELLO Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_HELLO || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_HELLO \n"); continue; } // 查用户ip 端口和主机名 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int clinet_port = ntohs(client_addr.sin_port); printf("Client ip: %s, port: %d\n", client_ip, clinet_port); printf("Client hostname: %.*s\n", 1021, &buffer[3]); memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 // 开始发送CMD_HELLOOK buffer[0] = 24; buffer[1] = CMD_HELLOOK; buffer[2] = 0; bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_HELLOOK Error: \n"); } else { break; } } while(1) { while(1) { // 开始接收CMD_TASK消息 bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive CMD_TASK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_TASK || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_TASK \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓存区 while(1) { // 开始下发任务 CMD_TASKOK buffer[0] = 24; buffer[1] = CMD_TASKOK; buffer[2] = 0; *((u_int32_t*)(buffer + 3)) = htonl(rand()); // 286331153 测试数字 *((u_int32_t*)(buffer + 7)) = htonl(rand()); // 572662306 bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓存区 while(1) { // 开始接收CMD_UPLOAD消息 bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive CMD_UPLOAD Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_UPLOAD || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_UPLOAD \n"); } else { break; } } u_int32_t num1, num2, num3; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); memcpy(&num3, buffer + 3 + 2 * sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); num3 = ntohl(num3); printf("Calc Finished: %u %u %u\n", num1, num2, num3); while(1) { // 开始回复CMD_UPLOADOK buffer[1] = CMD_UPLOADOK; bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓存区 } close(server_socket); return 0; }
  1. 客户端代码
c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 1325 // 客户端绑定端口 #define IPADDR "192.168.152.17" // 客户端绑定IP int main(int argc, char const *argv[]) { // 创建客户端套接字 int socketfd = socket(AF_INET, SOCK_DGRAM, 0); if (socketfd == -1) { perror("Create Socket failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(socketfd, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(socketfd); return -1; } // 创建服务器端地址 struct sockaddr_in server_addr; socklen_t server_len = sizeof(server_addr); char buffer[1024]; u_int32_t result; printf("Client is Ready!\n"); printf("Waiting for CMD_NOTIFY...\n"); while (1) { // 开始接收CMD_NOTIFY ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_NOTIFY || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_NOTIFY \n"); continue; } else { break; } } // 查服务端ip 端口和主机名 char server_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(server_addr.sin_addr), server_ip, INET_ADDRSTRLEN); int server_port = ntohs(server_addr.sin_port); printf("Server ip: %s, port: %d\n", server_ip, server_port); printf("Server hostname: %.*s\n", 1021, &buffer[3]); memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始发送CMD_HELLO buffer[0] = 24; buffer[1] = CMD_HELLO; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_HELLOOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_HELLOOK || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_HELLOOK \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { while(1) { // 开始发送CMD_TASK buffer[0] = 24; buffer[1] = CMD_TASK; buffer[2] = 0; ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_TASKOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_TASKOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_TASKOK || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_TASKOK \n"); continue; } else { break; } } u_int32_t num1, num2; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); result = num1 + num2; printf("Calc Finished: %u %u %u\n", num1, num2, result); while(1) { // 开始发送CMD_UPLOAD buffer[1] = CMD_UPLOAD; *((u_int32_t*)(buffer + 11)) = htonl(result); ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_UPLOAD failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_UPLOADOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_UPLOADOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_UPLOADOK || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_UPLOADOK \n"); continue; } else { break; } } } // 关闭套接字 close(socketfd); return 0; }
  1. 运行效果

服务端:

image.png

客户端:

image.png

3 开发过程中的问题及解决方法

  1. 仅在一个WSL上测试时,Windows端的WireShark抓不到包

这个实验的代码的运行过程给谁发一定想明白。在WSL自己给自己发的情况下:

  • 第一次的CMD_NOTIFY是广播,是要给同一网段的所有设备发的,所以WSL可以发送到Windows层面,Windows也抓得到广播的包。

  • 之后在明确了客户端IP后,收发对象就全是那一个IP了,这种情况下数据包不会出WSL,只会在Linux的网卡内转。 image.png

  1. WSL如何安装WireShark?
bash
sudo apt install wireshark sudo dpkg-reconfigure wireshark-common sudo chmod +x /usr/bin/dumpcap

第二步是允许非root用户抓包;最后这一步不做的话,WireShark找不到Linux上的网卡。

  1. 筛选数据包?

最上面的过滤器里写表达式,如udp.port==1324ip.dst==192.168.1.11等等。

4 v2版本代码

  1. 服务端代码
c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 1324 #define IPADDR "192.168.152.17" #define CLI_PORT 1325 // 因为CMD_NOTIFY需要广播 而广播需要指定接收者的端口号 所以client也必须指定端口 #define BROADCAST_IP "192.168.152.255" // 存储客户端信息 包括客户端地址和编号 struct ClientInfo { struct sockaddr_in client_addr; int client_id; }; // 保存客户端 int newClient(struct ClientInfo client_info_array[], struct sockaddr_in new_client_addr) { for (int i = 0; i < 10; i++) { if (client_info_array[i].client_id == -1) { client_info_array[i].client_addr = new_client_addr; client_info_array[i].client_id = i; return i; } } return -1; } // 回复CMD_HELLOOK void replyCMD_HELLOOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印客户端信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int clinet_port = ntohs(client_addr.sin_port); printf("Client ip: %s, port: %d\n", client_ip, clinet_port); printf("Client hostname: %.*s\n", 1021, &buffer[3]); printf("Client ID: %d\n", client_id); memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 // 开始发送CMD_HELLOOK buffer[0] = 24; buffer[1] = CMD_HELLOOK; buffer[2] = client_id; while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_HELLOOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 break; } } } // 回复CMD_TASKOK void replyCMD_TASKOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 // 开始下发任务 CMD_TASKOK buffer[0] = 24; buffer[1] = CMD_TASKOK; buffer[2] = client_id; *((u_int32_t*)(buffer + 3)) = htonl(rand()); // 286331153 测试数字 *((u_int32_t*)(buffer + 7)) = htonl(rand()); // 572662306 while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } } // 回复CMD_UPLOADOK void replyCMD_UPLOADOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印计算结果 u_int32_t num1, num2, num3; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); memcpy(&num3, buffer + 3 + 2 * sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); num3 = ntohl(num3); printf("Calc Finished: %u %u %u\n", num1, num2, num3); while(1) { // 开始回复CMD_UPLOADOK buffer[1] = CMD_UPLOADOK; ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } } int main(int argc, char const *argv[]) { struct ClientInfo client_array[10]; // 初始化 for (int i = 0; i < 10; i++) { client_array[i].client_id = -1; } srand(time(NULL)); // 初始化随机数种子 int server_socket = socket(AF_INET, SOCK_DGRAM, 0); if (server_socket == -1) { perror("Create Socket Failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IPADDR); int ret = bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(server_socket); return -1; } struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); ssize_t bytes_send; ssize_t bytes_recv; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); // 清空缓存区 printf("Server is Ready!\n"); // 先广播CMD_NOTIFY int val = 1; if (setsockopt(server_socket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) == -1) { perror("Boardcast Denied: \n"); close(server_socket); return -1; } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(CLI_PORT); client_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); buffer[0] = 24; buffer[1] = CMD_NOTIFY; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_NOTIFY failed: \n"); close(server_socket); return -1; } printf("CMD_NOTIFY is Sent\n"); memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 while(1) { // 开始接收消息 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive Message Error: \n"); continue; } if (buffer[0] != 24) { printf("It's Not Allowed Message \n"); continue; } // 接收CMD_HELLO 注册客户端并且调用函数回复CMD_HELLOOK if (buffer[1] == CMD_HELLO && buffer[2] == 0) { int client_id = newClient(client_array, client_addr); if (client_id == -1) { printf("client_array is Full"); continue; } replyCMD_HELLOOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } // 接收CMD_TASK 并且调用函数回复CMD_TASKOK if (buffer[1] == CMD_TASK) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } replyCMD_TASKOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } // 接收CMD_UPLOAD 并且调用函数回复CMD_UPLOADOK if (buffer[1] == CMD_UPLOAD) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } replyCMD_UPLOADOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } } close(server_socket); return 0; }
  1. 客户端代码
c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 1325 #define IPADDR "192.168.152.17" int main(int argc, char const *argv[]) { // 创建客户端套接字 int socketfd = socket(AF_INET, SOCK_DGRAM, 0); if (socketfd == -1) { perror("Create Socket failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(socketfd, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(socketfd); return -1; } // 创建服务器端地址 struct sockaddr_in server_addr; socklen_t server_len = sizeof(server_addr); char buffer[1024]; u_int32_t result; int client_id; printf("Client is Ready!\n"); printf("Waiting for CMD_NOTIFY...\n"); while (1) { // 开始接收CMD_NOTIFY ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_NOTIFY || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_NOTIFY \n"); continue; } else { break; } } // 查服务端ip 端口和主机名 char server_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(server_addr.sin_addr), server_ip, INET_ADDRSTRLEN); int server_port = ntohs(server_addr.sin_port); printf("Server ip: %s, port: %d\n", server_ip, server_port); printf("Server hostname: %.*s\n", 1021, &buffer[3]); memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始发送CMD_HELLO buffer[0] = 24; buffer[1] = CMD_HELLO; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_HELLOOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_HELLOOK || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_HELLOOK \n"); continue; } else { client_id = buffer[2]; printf("Client ID is %d\n", client_id); break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { while(1) { // 开始发送CMD_TASK buffer[0] = 24; buffer[1] = CMD_TASK; buffer[2] = client_id; ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_TASKOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_TASKOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_TASKOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_TASKOK \n"); continue; } else { break; } } u_int32_t num1, num2; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); result = num1 + num2; printf("Calc Finished: %u %u %u\n", num1, num2, result); sleep(1); printf("Sleep Finished\n"); while(1) { // 开始发送CMD_UPLOAD buffer[1] = CMD_UPLOAD; *((u_int32_t*)(buffer + 11)) = htonl(result); ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_UPLOAD failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_UPLOADOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_UPLOADOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_UPLOADOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_UPLOADOK \n"); continue; } else { break; } } } // 关闭套接字 close(socketfd); return 0; }

5 追加信息

5.1 VS Code的SSH配置

Ctrl + Shift + p快捷键搜索remote或者ssh,就能找到一个打开SSH配置文件的选项:

进去之后按这个格式写配置信息,公钥部分之后会说。

image.png

(主机,主机名,用户名,端口,默认验证方式,身份验证文件,是否代理转发)

回到要开SSH的Linux系统中,编辑/etc/ssh/sshd_config文件(假设ssh已经安装),示例如下:

# This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the # default value. Include /etc/ssh/sshd_config.d/*.conf #Port 22 这里是端口 默认22 #AddressFamily any #ListenAddress 0.0.0.0 监听IP ListenAddress 192.168.234.34 #ListenAddress :: 这是本地地址的另一种写法 #HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key #HostKey /etc/ssh/ssh_host_ed25519_key # Ciphers and keying #RekeyLimit default none # Logging #SyslogFacility AUTH #LogLevel INFO # Authentication: #LoginGraceTime 2m #PermitRootLogin prohibit-password PermitRootLogin yes 允许root用户通过ssh登录 #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 #PubkeyAuthentication yes PubkeyAuthentication yes 允许公钥验证 # Expect .ssh/authorized_keys2 to be disregarded by default in future. AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 验证密钥文件 #AuthorizedPrincipalsFile none #AuthorizedKeysCommand none #AuthorizedKeysCommandUser nobody # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #HostbasedAuthentication no # Change to yes if you don't trust ~/.ssh/known_hosts for # HostbasedAuthentication #IgnoreUserKnownHosts no # Don't read the user's ~/.rhosts and ~/.shosts files #IgnoreRhosts yes # To disable tunneled clear text passwords, change to no here! #PasswordAuthentication yes PasswordAuthentication yes #PermitEmptyPasswords no # Change to yes to enable challenge-response passwords (beware issues with # some PAM modules and threads) KbdInteractiveAuthentication no # Kerberos options #KerberosAuthentication no #KerberosOrLocalPasswd yes #KerberosTicketCleanup yes #KerberosGetAFSToken no # GSSAPI options #GSSAPIAuthentication no #GSSAPICleanupCredentials yes #GSSAPIStrictAcceptorCheck yes #GSSAPIKeyExchange no # Set this to 'yes' to enable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will # be allowed through the KbdInteractiveAuthentication and # PasswordAuthentication. Depending on your PAM configuration, # PAM authentication via KbdInteractiveAuthentication may bypass # the setting of "PermitRootLogin prohibit-password". # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and KbdInteractiveAuthentication to 'no'. UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes #PermitTTY yes PrintMotd no #PrintLastLog yes #TCPKeepAlive yes #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 #UseDNS no #PidFile /run/sshd.pid #MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none #VersionAddendum none # no default banner path #Banner none # Allow client to pass locale environment variables AcceptEnv LANG LC_* # override default of no subsystems Subsystem sftp /usr/lib/openssh/sftp-server # Example of overriding settings on a per-user basis #Match User anoncvs # X11Forwarding no # AllowTcpForwarding no # PermitTTY no # ForceCommand cvs server

然后systemctl restart ssh重新启动(这条命令随着系统不同而可能不同)SSH服务。之后可以在别的地方来测试这个端口了:telnet 192.168.234.34 22

提示

这条命令需要在Windows功能中启用telnet客户端。

image.png

正常的测试结果:

image.png

如果没通的话,就要考虑Linux端的防火墙,有些Linux(比如Kali)会默认将22端口ban掉,这时候就要配置或者关掉防火墙:

以Kali为例 apt install ufw 第一次配置防火墙需要装 ufw disable 关闭防火墙 ufw allow 22/tcp 允许22端口tcp连接

剩下的看图吧。

image.png

这个时候VS Code已经可以通过SSH连接Kali了,不过可以通过RSA密钥的方式免于输入密码。先在Windows的cmd里输入ssh-keygen -t rsa -C "3090309265@qq.com"(后面的邮箱随意),之后一路回车即可。

image.png

之后在Windows用户文件夹下的.ssh文件夹下找到公私钥文件id_rsa和id_rsa.pub:

image.png

将id_rsa.pub重命名成authorized_keys,放到Linux下的/root/.ssh文件夹下:

image.png

Linux这边以防万一再修改下权限:

bash
chmod 700 /root chmod 700 /root/.ssh chmod 644 /root/.ssh/authorized_keys

之后回到VS Code的配置文件里,把身份验证文件这部分写上,文件路径指向id_rsa即可。到这一步就配置完成了,配置好的Linux可以在这里找到:

image.png

5.2 WSL的防火墙问题

如果只是安装了WSL2,没有配置防火墙的话,Hyper-V自带的防火墙会禁掉所有的入站连接,就像这样:

1618d25dcba781481104182ede4c46ff.png

数据包发是发到了,但是没转发给WSL,需要按微软官方的说法执行命令。

image.png

bash
Set-NetFirewallHyperVVMSetting -Name ‘{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}’ -DefaultInboundAction Allow

bash
New-NetFirewallHyperVRule -Name MyWebServer -DisplayName “My Web Server” -Direction Inbound -VMCreatorId “{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}” -Protocol TCP -LocalPorts 80

配置Hyper-V防火墙说明,详情见Microsoft Learn | Hyper-V防火墙

5.3 Debian换源

Debian刚安装好的时候apt update有可能会遇到TLS握手过不去的情况,需要重新安装这个sudo apt install apt-transport-https ca-certificates,但是又会陷入到先安装但过不去TLS握手,先修TLS握手要求先安装的怪圈里。

解决方法是换源的地址别用https,先都用http,比如下面这段配置信息:

deb http://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib deb-src http://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib deb http://mirrors.aliyun.com/debian-security/ bookworm-security main deb-src http://mirrors.aliyun.com/debian-security/ bookworm-security main deb http://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib deb-src http://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib deb http://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib deb-src http://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib

换过源之后再把上面那个安装了,安装好之后再换回https:

deb https://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib deb-src https://mirrors.aliyun.com/debian/ bookworm main non-free non-free-firmware contrib deb https://mirrors.aliyun.com/debian-security/ bookworm-security main deb-src https://mirrors.aliyun.com/debian-security/ bookworm-security main deb https://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib deb-src https://mirrors.aliyun.com/debian/ bookworm-updates main non-free non-free-firmware contrib deb https://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib deb-src https://mirrors.aliyun.com/debian/ bookworm-backports main non-free non-free-firmware contrib

6 最终版代码

6.1 服务端代码

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 19326 // 在这里写服务端的端口 #define IPADDR "192.168.234.17" // 在这里写服务端的ip #define CLI_PORT 19327 // 因为CMD_NOTIFY需要广播 而广播需要指定接收者的端口号 所以客户端也必须指定端口 #define BROADCAST_IP "192.168.234.255" // 在这里写广播地址 // 存储客户端信息 包括客户端地址和编号 struct ClientInfo { struct sockaddr_in client_addr; int client_id; }; // 保存客户端 int newClient(struct ClientInfo client_info_array[], struct sockaddr_in new_client_addr) { for (int i = 0; i < 10; i++) { if (client_info_array[i].client_id == -1) { client_info_array[i].client_addr = new_client_addr; client_info_array[i].client_id = i; return i; } } return -1; } // 回复CMD_HELLOOK void replyCMD_HELLOOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印客户端信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int clinet_port = ntohs(client_addr.sin_port); printf("Client ip: %s, port: %d\n", client_ip, clinet_port); printf("Client hostname: %.*s\n", 1021, &buffer[3]); printf("Client ID: %d\n", client_id); memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 // 开始发送CMD_HELLOOK buffer[0] = 24; buffer[1] = CMD_HELLOOK; buffer[2] = client_id; while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_HELLOOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 break; } } } // 回复CMD_TASKOK void replyCMD_TASKOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 // 开始下发任务 CMD_TASKOK buffer[0] = 24; buffer[1] = CMD_TASKOK; buffer[2] = client_id; *((u_int32_t*)(buffer + 3)) = htonl(rand()); // 286331153 测试数字 *((u_int32_t*)(buffer + 7)) = htonl(rand()); // 572662306 while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } } // 回复CMD_UPLOADOK void replyCMD_UPLOADOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印计算结果 u_int32_t num1, num2, num3; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); memcpy(&num3, buffer + 3 + 2 * sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); num3 = ntohl(num3); char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("%s Calc Finished: %u + %u = %u\n", client_ip, num1, num2, num3); while(1) { // 开始回复CMD_UPLOADOK buffer[1] = CMD_UPLOADOK; ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } } int main(int argc, char const *argv[]) { struct ClientInfo client_array[10]; // 初始化 for (int i = 0; i < 10; i++) { client_array[i].client_id = -1; } srand(time(NULL)); // 初始化随机数种子 int server_socket = socket(AF_INET, SOCK_DGRAM, 0); if (server_socket == -1) { perror("Create Socket Failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IPADDR); int ret = bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(server_socket); return -1; } struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); ssize_t bytes_send; ssize_t bytes_recv; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); // 清空缓存区 printf("Server is Ready!\n"); // 先广播CMD_NOTIFY int val = 1; if (setsockopt(server_socket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) == -1) { perror("Boardcast Denied: \n"); close(server_socket); return -1; } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(CLI_PORT); client_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); buffer[0] = 24; buffer[1] = CMD_NOTIFY; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_NOTIFY failed: \n"); close(server_socket); return -1; } printf("CMD_NOTIFY is Sent\n"); memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 while(1) { // 开始接收消息 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive Message Error: \n"); continue; } if (buffer[0] != 24) { printf("It's Not Allowed Message \n"); continue; } // 接收CMD_HELLO 注册客户端并且调用函数回复CMD_HELLOOK if (buffer[1] == CMD_HELLO && buffer[2] == 0) { int client_id = newClient(client_array, client_addr); if (client_id == -1) { printf("client_array is Full"); continue; } replyCMD_HELLOOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } // 接收CMD_TASK 并且调用函数回复CMD_TASKOK if (buffer[1] == CMD_TASK) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } replyCMD_TASKOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } // 接收CMD_UPLOAD 并且调用函数回复CMD_UPLOADOK if (buffer[1] == CMD_UPLOAD) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } replyCMD_UPLOADOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } } close(server_socket); return 0; }

6.2 客户端代码

所有的sleep都是为了不让输出变得太快,以至于完全无法观察。几个客户端之间的sleep的时间也可以进行调整。

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 19327 // 在这里写客户端的端口 int main(int argc, char const *argv[]) { // 创建客户端套接字 int socketfd = socket(AF_INET, SOCK_DGRAM, 0); if (socketfd == -1) { perror("Create Socket failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(socketfd, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(socketfd); return -1; } // 创建服务器端地址 struct sockaddr_in server_addr; socklen_t server_len = sizeof(server_addr); char buffer[1024]; u_int32_t result; int client_id; // 保存客户端id printf("Client is Ready!\n"); printf("Waiting for CMD_NOTIFY...\n"); while (1) { // 开始接收CMD_NOTIFY ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_NOTIFY || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_NOTIFY \n"); continue; } else { break; } } // 查服务端ip 端口和主机名 char server_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(server_addr.sin_addr), server_ip, INET_ADDRSTRLEN); int server_port = ntohs(server_addr.sin_port); printf("Server ip: %s, port: %d\n", server_ip, server_port); printf("Server hostname: %.*s\n", 1021, &buffer[3]); memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { sleep(5); // 开始发送CMD_HELLO buffer[0] = 24; buffer[1] = CMD_HELLO; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { printf("CMD_HELLO is sent \n"); break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_HELLOOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_HELLOOK) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_HELLOOK \n"); continue; } else { client_id = buffer[2]; printf("Client ID is %d\n", client_id); break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { while(1) { // 开始发送CMD_TASK buffer[0] = 24; buffer[1] = CMD_TASK; buffer[2] = client_id; ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_TASKOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_TASKOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_TASKOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_TASKOK \n"); continue; } else { break; } } u_int32_t num1, num2; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); result = num1 + num2; printf("Calc Finished: %u + %u = %u\n", num1, num2, result); sleep(1); // Linux下的sleep函数 传入秒数单位是秒 Windows下是毫秒 printf("Sleep Finished\n"); while(1) { // 开始发送CMD_UPLOAD buffer[1] = CMD_UPLOAD; *((u_int32_t*)(buffer + 11)) = htonl(result); ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_UPLOAD failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_UPLOADOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_UPLOADOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_UPLOADOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_UPLOADOK \n"); continue; } else { break; } } } // 关闭套接字 close(socketfd); return 0; }

7 最终版又来了 UDP版本

这次更新了老师给的计算素数个数的要求,还没测试,理论上是通的。

7.1 服务端代码

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 19326 #define IPADDR "192.168.156.17" #define CLI_PORT 19327 // 因为CMD_NOTIFY需要广播 而广播需要指定接收者的端口号 所以client也必须指定端口 #define BROADCAST_IP "192.168.156.255" // 存储客户端信息 包括客户端地址和编号 struct ClientInfo { struct sockaddr_in client_addr; int client_id; }; // 保存客户端 int newClient(struct ClientInfo client_info_array[], struct sockaddr_in new_client_addr) { for (int i = 0; i < 10; i++) { if (client_info_array[i].client_id == -1) { client_info_array[i].client_addr = new_client_addr; client_info_array[i].client_id = i; return i; } } return -1; } // 回复CMD_HELLOOK void replyCMD_HELLOOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印客户端信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); int clinet_port = ntohs(client_addr.sin_port); printf("Client ip: %s, port: %d\n", client_ip, clinet_port); printf("Client hostname: %.*s\n", 1021, &buffer[3]); printf("Client ID: %d\n", client_id); memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 // 开始发送CMD_HELLOOK buffer[0] = 24; buffer[1] = CMD_HELLOOK; buffer[2] = client_id; while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_HELLOOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓冲区 break; } } } // 回复CMD_TASKOK void replyCMD_TASKOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id, int start_index) { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 // 开始下发任务 CMD_TASKOK buffer[0] = 24; buffer[1] = CMD_TASKOK; buffer[2] = client_id; if (start_index > 1000000) { *((u_int32_t*)(buffer + 3)) = htonl(0); *((u_int32_t*)(buffer + 7)) = htonl(0); } else { *((u_int32_t*)(buffer + 3)) = htonl(start_index); // 计算素数的起始位置 *((u_int32_t*)(buffer + 7)) = htonl(start_index + 99); // 计算素数的终止位置 } while(1) { ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } } // 回复CMD_UPLOADOK int replyCMD_UPLOADOK(char buffer[], int socket, struct sockaddr_in client_addr, int client_id) { // 打印计算结果 u_int32_t num1, num2, num3; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); memcpy(&num3, buffer + 3 + 2 * sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); num3 = ntohl(num3); char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("%s Calc Finished: %u ~ %u = %u\n", client_ip, num1, num2, num3); while(1) { // 开始回复CMD_UPLOADOK buffer[1] = CMD_UPLOADOK; ssize_t bytes_send = sendto(socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); if (bytes_send == -1) { perror("Sending CMD_TASKOK Error: \n"); } else { memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 break; } } return num3; } int main(int argc, char const *argv[]) { struct ClientInfo client_array[10]; // 初始化 for (int i = 0; i < 10; i++) { client_array[i].client_id = -1; } srand(time(NULL)); // 初始化随机数种子 int server_socket = socket(AF_INET, SOCK_DGRAM, 0); if (server_socket == -1) { perror("Create Socket Failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IPADDR); int ret = bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(server_socket); return -1; } struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); ssize_t bytes_send; ssize_t bytes_recv; char buffer[1024]; int start_index = 1; // 开始计算素数位置 int result = 0; // 计算结果 memset(buffer, 0, sizeof(buffer)); // 清空缓存区 printf("Server is Ready!\n"); // 先广播CMD_NOTIFY int val = 1; if (setsockopt(server_socket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) == -1) { perror("Boardcast Denied: \n"); close(server_socket); return -1; } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(CLI_PORT); client_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); buffer[0] = 24; buffer[1] = CMD_NOTIFY; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } bytes_send = sendto(server_socket, buffer, 1024, 0, (struct sockaddr*)&client_addr, client_len); if (bytes_send == -1) { perror("Sending CMD_NOTIFY failed: \n"); close(server_socket); return -1; } printf("CMD_NOTIFY is Sent\n"); memset(buffer, 0, sizeof(&buffer)); // 清空缓存区 while(1) { // 开始接收消息 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); bytes_recv = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { perror("Receive Message Error: \n"); continue; } if (buffer[0] != 24) { printf("It's Not Allowed Message \n"); continue; } // 接收CMD_HELLO 注册客户端并且调用函数回复CMD_HELLOOK if (buffer[1] == CMD_HELLO && buffer[2] == 0) { int client_id = newClient(client_array, client_addr); if (client_id == -1) { printf("client_array is Full"); continue; } replyCMD_HELLOOK(buffer, server_socket, client_array[client_id].client_addr, client_id); } // 接收CMD_TASK 并且调用函数回复CMD_TASKOK if (buffer[1] == CMD_TASK) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } replyCMD_TASKOK(buffer, server_socket, client_array[client_id].client_addr, client_id, start_index); start_index = start_index + 100; } // 接收CMD_UPLOAD 并且调用函数回复CMD_UPLOADOK if (buffer[1] == CMD_UPLOAD) { int client_id = buffer[2]; if (client_id >= 10) { printf("Client ID is not Allowed\n"); continue; } result = result + replyCMD_UPLOADOK(buffer, server_socket, client_array[client_id].client_addr, client_id); printf("Result Updated!: %d \n", result); } } close(server_socket); return 0; }

7.2 客户端代码

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> #define CMD_NOTIFY 1 #define CMD_HELLO 2 #define CMD_HELLOOK 3 #define CMD_TASK 4 #define CMD_TASKOK 5 #define CMD_UPLOAD 6 #define CMD_UPLOADOK 7 #define PORT 19327 // 判断一个数字是不是素数 是的话返回1 不是的话返回0 int is_prime(int i) { int j = 0; for(j = 2; j < i; j++) { if (i % j == 0) { return 0; } } if(j == i) { return 1; } } int main(int argc, char const *argv[]) { // 创建客户端套接字 int socketfd = socket(AF_INET, SOCK_DGRAM, 0); if (socketfd == -1) { perror("Create Socket failed: \n"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(socketfd, (struct sockaddr*)&addr, sizeof(addr)); if (0 > ret) { perror("Bind Failed: \n"); close(socketfd); return -1; } // 创建服务器端地址 struct sockaddr_in server_addr; socklen_t server_len = sizeof(server_addr); char buffer[1024]; u_int32_t result; int client_id; printf("Client is Ready!\n"); printf("Waiting for CMD_NOTIFY...\n"); while (1) { // 开始接收CMD_NOTIFY ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_NOTIFY || buffer[2] != 0) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_NOTIFY \n"); continue; } else { break; } } // 查服务端ip 端口和主机名 char server_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(server_addr.sin_addr), server_ip, INET_ADDRSTRLEN); int server_port = ntohs(server_addr.sin_port); printf("Server ip: %s, port: %d\n", server_ip, server_port); printf("Server hostname: %.*s\n", 1021, &buffer[3]); memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始发送CMD_HELLO buffer[0] = 24; buffer[1] = CMD_HELLO; buffer[2] = 0; if (gethostname(&buffer[3], 1021) == -1) { strcpy(&buffer[3], "Unknown Host"); } ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_HELLOOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receiving Data Failed: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_HELLOOK) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_HELLOOK \n"); continue; } else { client_id = buffer[2]; printf("Client ID is %d\n", client_id); break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { while(1) { // 开始发送CMD_TASK buffer[0] = 24; buffer[1] = CMD_TASK; buffer[2] = client_id; ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_HELLO failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_TASKOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_TASKOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_TASKOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_TASKOK \n"); continue; } else { break; } } u_int32_t num1, num2; memcpy(&num1, buffer + 3, sizeof(u_int32_t)); memcpy(&num2, buffer + 3 + sizeof(u_int32_t), sizeof(u_int32_t)); num1 = ntohl(num1); num2 = ntohl(num2); if (num1 == 0 && num2 == 0) // 两个数字都是0的话停止计算 { break; } result = 0; for(int i = num1; i <= num2; i++) { if (is_prime(i) == 1) { result = result + 1; } } printf("Calc Finished: %u ~ %u = %u\n", num1, num2, result); sleep(1); // todo 这块可以改 调节自己看得清的频率就行了 printf("Sleep Finished\n"); while(1) { // 开始发送CMD_UPLOAD buffer[1] = CMD_UPLOAD; *((u_int32_t*)(buffer + 11)) = htonl(result); ssize_t bytes_send = sendto(socketfd, buffer, 1024, 0, (struct sockaddr*)&server_addr, server_len); if (bytes_send == -1) { perror("Sending CMD_UPLOAD failed: \n"); continue; } else { break; } } memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 while(1) { // 开始接收CMD_UPLOADOK ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len); if (bytes_recv == -1) { perror("Receive CMD_UPLOADOK Error: \n"); continue; } if (buffer[0] != 24 || buffer[1] != CMD_UPLOADOK || buffer[2] != client_id) { printf("%d %d %d\n", buffer[0], buffer[1], buffer[2]); printf("Is Not Allowed CMD_UPLOADOK \n"); continue; } else { break; } } } // 关闭套接字 close(socketfd); return 0; }

本文作者:御坂19327号

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!