写一个服务器和客户端
运行服务器和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和port
struct 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个是标准输入流 0
FD_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 和 port
struct 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 和 port
struct 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;
}