一、select应用举例
下面的代码演示了如何使用select函数进行I/O多路复用,同时处理多个客户端的连接和消息收发:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
int server_socket, client_socket[MAX_CLIENTS], max_fd, activity, i, valread, sd;
struct sockaddr_in server_addr, client_addr;
fd_set readfds;
char buffer[BUFFER_SIZE];
// 创建服务器socket
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置服务器socket地址
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(8888);
// 绑定服务器socket到地址
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听客户端连接
if (listen(server_socket, MAX_CLIENTS) == -1) {
perror("listen failed");
exit(EXIT_FAILURE);
}
// 初始化客户端socket数组
for (i = 0; i < MAX_CLIENTS; i++) {
client_socket[i] = 0;
}
// 输出服务器信息
printf("Server started on port %d\n", ntohs(server_addr.sin_port));
while (1) {
// 清空文件描述符集合
FD_ZERO(&readfds);
// 添加服务器socket到集合中
FD_SET(server_socket, &readfds);
max_fd = server_socket;
// 添加客户端socket到集合中
for (i = 0; i < MAX_CLIENTS; i++) {
sd = client_socket[i];
// 如果socket有效,添加到集合中
if (sd > 0) {
FD_SET(sd, &readfds);
}
// 更新最大文件描述符
if (sd > max_fd) {
max_fd = sd;
}
}
// 等待文件描述符上有活动事件
activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
if ((activity < 0) && (errno != EINTR)) {
perror("select failed");
exit(EXIT_FAILURE);
}
// 如果服务器socket有连接请求,处理连接
if (FD_ISSET(server_socket, &readfds)) {
int new_socket, addrlen = sizeof(client_addr);
if ((new_socket = accept(server_socket, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen)) == -1) {
perror("accept failed");
exit(EXIT_FAILURE);
}
// 输出新客户端信息
printf("New connection, socket fd is %d, ip is : %s, port : %d\n", new_socket, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 添加新客户端socket到数组中
for (i = 0; i < MAX_CLIENTS; i++) {
if (client_socket[i] == 0) {
client_socket[i] = new_socket;
break;
}
}
}
// 处理客户端socket上的消息
for (i = 0; i < MAX_CLIENTS; i++) {
sd = client_socket[i];
if (FD_ISSET(sd, &readfds)) {
if ((valread = read(sd, buffer, BUFFER_SIZE)) == 0) {
// 客户端关闭连接
getpeername(sd, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen);
printf("Client disconnected, ip is : %s, port : %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
close(sd);
client_socket[i] = 0;
} else {
// 处理客户端消息
buffer[valread] = '\0';
printf("Received message from client[%d], message is : %s\n", i, buffer);
}
}
}
}
return 0;
}
```
二、epoll应用举例
下面的代码演示了如何使用epoll函数进行I/O多路复用,同时处理多个客户端的连接和消息收发:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
int server_socket, client_socket, epoll_fd, i, valread, sd;
struct sockaddr_in server_addr, client_addr;
struct epoll_event event, events[MAX_CLIENTS];
char buffer[BUFFER_SIZE];
// 创建服务器socket
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置服务器socket地址
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(8888);
// 绑定服务器socket到地址
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听客户端连接
if (listen(server_socket, MAX_CLIENTS) == -1) {
perror("listen failed");
exit(EXIT_FAILURE);
}
// 创建epoll文件描述符
if ((epoll_fd = epoll_create(MAX_CLIENTS)) == -1) {
perror("epoll_create failed");
exit(EXIT_FAILURE);
}
// 添加服务器socket到epoll事件中
event.events = EPOLLIN;
event.data.fd = server_socket;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1) {
perror("epoll_ctl failed");
exit(EXIT_FAILURE);
}
// 输出服务器信息
printf("Server started on port %d\n", ntohs(server_addr.sin_port));
while (1) {
// 等待epoll事件触发
int num_events = epoll_wait(epoll_fd, events, MAX_CLIENTS, -1);
// 处理所有触发的事件
for (i = 0; i < num_events; i++) {
// 如果是服务器socket有连接请求,处理连接
if (events[i].data.fd == server_socket) {
int addrlen = sizeof(client_addr);
if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen)) == -1) {
perror("accept failed");
exit(EXIT_FAILURE);
}
// 输出新客户端信息
printf("New connection, socket fd is %d, ip is : %s, port : %d\n", client_socket, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 将客户端socket添加到epoll事件中
event.events = EPOLLIN;
event.data.fd = client_socket;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event) == -1) {
perror("epoll_ctl failed");
exit(EXIT_FAILURE);
}
} else {
// 处理客户端socket上的消息
sd = events[i].data.fd;
if ((valread = read(sd, buffer, BUFFER_SIZE)) == 0) {
// 客户端关闭连接
getpeername(sd, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen);
printf("Client disconnected, ip is : %s, port : %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
close(sd);
} else {
// 处理客户端消息
buffer[valread] = '\0';
printf("Received message from client, message is : %s\n", buffer);
}
}
}
}
return 0;
}
```