云计算百科
云计算领域专业知识百科平台

多客户端 - 服务器结构-实操

实现2个客户端之间互相聊天
要求:
1、服务器使用 select 模型实现接受多个客户端连接,以及转发消息
2、客户端要求:使用 poll 模型解决 技能够 read 读取服务器发来的消息,又能够scanf读取键盘输入的信息
3、客户端服务器不允许开启额外线程和进程

服务器代码 (select模型)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>

#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <port>\\n", argv[0]);
return 1;
}

int server_fd, new_socket, client_sockets[MAX_CLIENTS];
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};

// 初始化客户端socket数组
for (int i = 0; i < MAX_CLIENTS; i++) {
client_sockets[i] = 0;
}

// 创建socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 设置socket选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(atoi(argv[1]));

// 绑定socket
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}

printf("Server started on port %d\\n", atoi(argv[1]));

fd_set readfds;
int max_sd, activity;

while (1) {
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);
max_sd = server_fd;

// 添加客户端socket到集合
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_sockets[i] > 0) {
FD_SET(client_sockets[i], &readfds);
}
if (client_sockets[i] > max_sd) {
max_sd = client_sockets[i];
}
}

// 使用select等待活动
activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
if ((activity < 0) && (errno != EINTR)) {
perror("select error");
}

// 检查新连接
if (FD_ISSET(server_fd, &readfds)) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}

printf("New connection, socket fd: %d, ip: %s, port: %d\\n",
new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));

// 添加新socket到数组
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_sockets[i] == 0) {
client_sockets[i] = new_socket;
printf("Adding to list of sockets as %d\\n", i);
break;
}
}
}

// 检查客户端数据
for (int i = 0; i < MAX_CLIENTS; i++) {
int sd = client_sockets[i];
if (FD_ISSET(sd, &readfds)) {
int valread = read(sd, buffer, BUFFER_SIZE);
if (valread == 0) {
// 客户端断开连接
getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
printf("Host disconnected, ip: %s, port: %d\\n",
inet_ntoa(address.sin_addr), ntohs(address.sin_port));
close(sd);
client_sockets[i] = 0;
} else {
// 转发消息给所有客户端
buffer[valread] = '\\0';
printf("Forwarding message: %s\\n", buffer);
for (int j = 0; j < MAX_CLIENTS; j++) {
if (client_sockets[j] > 0 && client_sockets[j] != sd) {
send(client_sockets[j], buffer, strlen(buffer), 0);
}
}
}
}
}
}

return 0;
}

客户端代码 (poll模型)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>

#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s <ip> <port>\\n", argv[0]);
return 1;
}

int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
struct pollfd fds[2];

// 创建socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation failed");
return -1;
}

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));

// 转换IP地址
if (inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0) {
perror("invalid address");
return -1;
}

// 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connection failed");
return -1;
}

printf("Connected to server\\n");

// 设置poll结构
fds[0].fd = STDIN_FILENO; // 标准输入
fds[0].events = POLLIN;
fds[1].fd = sock; // socket
fds[1].events = POLLIN;

while (1) {
int ret = poll(fds, 2, -1); // 无限等待

if (ret == -1) {
perror("poll error");
break;
}

// 检查键盘输入
if (fds[0].revents & POLLIN) {
memset(buffer, 0, BUFFER_SIZE);
if (fgets(buffer, BUFFER_SIZE, stdin) == NULL) {
break;
}
// 发送消息到服务器
send(sock, buffer, strlen(buffer), 0);
}

// 检查服务器消息
if (fds[1].revents & POLLIN) {
memset(buffer, 0, BUFFER_SIZE);
int len = recv(sock, buffer, BUFFER_SIZE, 0);
if (len <= 0) {
printf("Server disconnected\\n");
break;
}
printf("Received: %s", buffer);
}
}

close(sock);
return 0;
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » 多客户端 - 服务器结构-实操
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!