使用epoll方式实现一个C/S架构的应用; 要求:服务器同时支持多个客户端连接,一个客户端发送消息,服务器转发 到其他客户端并回显消息;
时间: 2024-05-08 14:20:59 浏览: 98
这里提供一个基于 epoll 的 C/S 架构的聊天室示例代码,可以实现服务器同时支持多个客户端连接,一个客户端发送消息,服务器转发到其他客户端并回显消息。
服务器端代码:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define MAX_EVENTS 1024
#define BUF_SIZE 1024
int set_nonblocking(int fd) {
int old_flags = fcntl(fd, F_GETFL, 0);
if (old_flags == -1) {
return -1;
}
return fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
return 1;
}
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
return 1;
}
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
perror("setsockopt");
return 1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(atoi(argv[1]));
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
return 1;
}
if (listen(server_fd, SOMAXCONN) == -1) {
perror("listen");
return 1;
}
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
return 1;
}
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl");
return 1;
}
struct epoll_event events[MAX_EVENTS];
while (1) {
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) {
perror("epoll_wait");
break;
}
for (int i = 0; i < n; i++) {
int fd = events[i].data.fd;
if (fd == server_fd) {
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd == -1) {
perror("accept");
continue;
}
if (set_nonblocking(client_fd) == -1) {
perror("set_nonblocking");
return 1;
}
event.events = EPOLLIN | EPOLLET;
event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
perror("epoll_ctl");
return 1;
}
printf("New client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
} else {
char buf[BUF_SIZE];
int len = recv(fd, buf, BUF_SIZE, 0);
if (len == -1) {
perror("recv");
continue;
}
if (len == 0) {
printf("Client disconnected\n");
close(fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
continue;
}
printf("Received message: %s\n", buf);
for (int i = 0; i < n; i++) {
int send_fd = events[i].data.fd;
if (send_fd != server_fd && send_fd != fd) {
if (send(send_fd, buf, len, 0) == -1) {
perror("send");
continue;
}
}
}
}
}
}
close(epoll_fd);
close(server_fd);
return 0;
}
```
客户端代码:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define MAX_EVENTS 1024
#define BUF_SIZE 1024
int set_nonblocking(int fd) {
int old_flags = fcntl(fd, F_GETFL, 0);
if (old_flags == -1) {
return -1;
}
return fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s <ip> <port>\n", argv[0]);
return 1;
}
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd == -1) {
perror("socket");
return 1;
}
if (set_nonblocking(client_fd) == -1) {
perror("set_nonblocking");
return 1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
if (errno != EINPROGRESS) {
perror("connect");
return 1;
}
}
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
return 1;
}
struct epoll_event event;
event.events = EPOLLIN | EPOLLOUT | EPOLLET;
event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
perror("epoll_ctl");
return 1;
}
struct epoll_event events[MAX_EVENTS];
while (1) {
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) {
perror("epoll_wait");
break;
}
for (int i = 0; i < n; i++) {
int fd = events[i].data.fd;
if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLOUT)) {
char buf[BUF_SIZE];
fgets(buf, BUF_SIZE, stdin);
int len = strlen(buf);
if (send(fd, buf, len, 0) == -1) {
perror("send");
continue;
}
} else if (events[i].events & EPOLLERR) {
printf("Connection error\n");
close(fd);
return 1;
} else if (events[i].events & EPOLLHUP) {
printf("Connection closed\n");
close(fd);
return 1;
}
}
}
close(epoll_fd);
close(client_fd);
return 0;
}
```
使用方法:
在终端1中启动服务器:
```
$ ./server 12345
```
在终端2中启动客户端,并连接到服务器:
```
$ ./client 127.0.0.1 12345
```
可以在客户端输入消息并发送,服务器会将消息转发给其他客户端并回显消息。可以启动多个客户端连接到同一个服务器。
阅读全文