寫一個服務器和客戶端
運行服務器和2個客戶端,實現聊天功能 客戶端1和客戶端2進行聊天,客戶端1將聊天數據發送給服務器,服務器將聊天數據轉發給客戶端2
要求: 服務器使用 select 模型實現 ,客戶端1使用 poll 模型實現, 客戶端2使用多線程實現
服務器:
#include <head.h>
// 將client存入數組arr中的最后一個位置上,存完之后,arr數組的長度記得自增
void insert_client(int arr[], int client, int *len) {arr[*len] = client;(*len)++;
}// 將client從數組arr中移除,移除后記得數組長度-1
void remove_client(int arr[], int client, int *len) {int i;for (i = 0; i < *len; i++) {if (arr[i] == client) {break;}}if (i == *len) {return;}for (; i < (*len - 1); i++) {arr[i] = arr[i + 1];}(*len)--;
}int main(int argc, const char *argv[]) {if (argc < 2) {printf("請輸入端口號\n");return 1;}int port = atoi(argv[1]);// 創建服務器套接字int server = socket(AF_INET, SOCK_STREAM, 0);// 為服務器準備ip和portstruct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("0.0.0.0");if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("bind");return 1;}listen(server, 50);int client_arr[64] = {0}; // 用來存放所有客戶端套接字的數組int arr_len = 0; // 記錄數組的長度fd_set readfds; // 創建一個select的監視列表// 初始化,只有2個描述符可以初始化,1個是server服務器套接字,1個是標準輸入流 0FD_ZERO(&readfds);FD_SET(server, &readfds); // 將服務器套接字放入到監視列表中FD_SET(STDIN_FILENO, &readfds); // 將標準輸入流描述符放入到監視列表中while (1) {fd_set temp = readfds;select(1024, &temp, 0, 0, 0);// select是一個阻塞型函數,一旦接觸阻塞,就說明有任意個描述符激活了,激活的描述符會寫入temp里面// 判斷一下激活列表temp里面的描述符到底是哪些if (FD_ISSET(STDIN_FILENO, &temp)) {char buf[1024] = {0};scanf("%s", buf);printf("鍵盤輸入數據:%s\n", buf);}if (FD_ISSET(server, &temp)) {int client = accept(server, 0, 0);printf("有新客戶端連接\n");// 將新連接的客戶端加入到監視列表 readfds里面去 以及 數組 client_arr里面去FD_SET(client, &readfds);insert_client(client_arr, client, &arr_len);}// 判斷一下各種各樣的客戶端是否被激活,也就是是否有在temp 里面for (int i = 0; i < arr_len; i++) {int client = client_arr[i];if (FD_ISSET(client, &temp)) {char pack[1024] = {0};int size = 0;int res = read(client, &size, 4);if (res == 0) {printf("從客戶端斷開連接\n");// 從監視列表和客戶端數組中移除客戶端套接字FD_CLR(client, &readfds);remove_client(client_arr, client, &arr_len);close(client); // 關閉相關的客戶端break;}read(client, (char *)&pack + 4, size - 4);// 轉發數據給其他客戶端for (int j = 0; j < arr_len; j++) {if (client_arr[j] != client) {write(client_arr[j], &size, 4);write(client_arr[j], pack, size);}}}}}return 0;
}
客戶端1:
#include <head.h>
// 將client存入數組arr中的最后一個位置上,存完之后,arr數組的長度記得自增
void insert_client(struct pollfd *arr, struct pollfd client, int *len) {arr[*len] = client;(*len)++;
}// 將client從數組arr中移除,移除后記得數組長度-1
void remove_client(struct pollfd *arr, int client, int *len) {int i;for (i = 0; i < *len; i++) {if (arr[i].fd == client) {break;}}if (i == *len) {return;}for (; i < (*len - 1); i++) {arr[i] = arr[i + 1];}(*len)--;
}int main(int argc, const char *argv[]) {if (argc < 2) {printf("請輸入端口號\n");return 1;}int port = atoi(argv[2]);// 創建客戶端套接字int client = socket(AF_INET, SOCK_STREAM, 0);// 準備 ip 和 portstruct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("192.168.126.235");if (connect(client, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("connect");return 1;}struct pollfd list[10];int list_len = 0;struct pollfd client_fd = {client, POLLIN, 0};insert_client(list, client_fd, &list_len);struct pollfd stdin_fd = {STDIN_FILENO, POLLIN, 0};insert_client(list, stdin_fd, &list_len);while (1) {int res = poll(list, list_len, -1);if (res == -1) {perror("poll");break;}for (int i = 0; i < list_len; i++) {if (list[i].revents & POLLIN) {if (list[i].fd == STDIN_FILENO) {char buf[1024] = {0};scanf("%s", buf);int size = strlen(buf);write(client, &size, 4);write(client, buf, size);} else if (list[i].fd == client) {int size = 0;read(client, &size, 4);char pack[1024] = {0};read(client, pack, size);printf("收到消息: %s\n", pack);}}}}close(client);return 0;
}
客戶端2:
#include <head.h>
void* receive_message(void* arg) {int client = *(int*)arg;while (1) {int size = 0;int res = read(client, &size, 4);if (res == 0) {printf("與服務器斷開連接\n");break;}char pack[1024] = {0};read(client, pack, size);printf("收到消息: %s\n", pack);}return NULL;
}int main(int argc, const char *argv[]) {if (argc < 2) {printf("請輸入端口號\n");return 1;}int port = atoi(argv[1]);// 創建客戶端套接字int client = socket(AF_INET, SOCK_STREAM, 0);// 準備 ip 和 portstruct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(192.168.126.235);if (connect(client, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("connect");return 1;}pthread_t thread_id;pthread_create(&thread_id, NULL, receive_message, &client);pthread_detach(thread_id);while (1) {char buf[1024] = {0};scanf("%s", buf);int size = strlen(buf);write(client, &size, 4);write(client, buf, size);}close(client);return 0;
}