网络编程是计算机科学中一个非常重要的领域,它涉及如何使计算机之间能够相互通信。在众多网络编程技术中,select和poll是两个经典的I/O多路复用技术,它们允许单个进程监视多个文件描述符,从而提高程序的性能和效率。本文将为你详细介绍select和poll编程,帮助你轻松掌握网络编程的核心技巧。
了解select和poll
1. select
select函数是UNIX系统中一个用于I/O多路复用的系统调用。它允许一个进程同时监视多个文件描述符,等待其中一个或多个文件描述符变得可读写或出错。这样,单个进程就可以处理多个网络连接,而不需要为每个连接创建一个线程或进程。
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
n:要监视的文件描述符数量。readfds:包含要读取的文件描述符的集合。writefds:包含要写入的文件描述符的集合。exceptfds:包含要检查错误的文件描述符的集合。timeout:等待时间,如果设置为NULL,则无限等待。
2. poll
poll函数是POSIX标准中定义的一个I/O多路复用函数,它提供了与select类似的接口,但在某些方面比select更灵活。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds:包含要监视的文件描述符的数组。nfds:数组中文件描述符的数量。timeout:等待时间,如果设置为-1,则无限等待。
select和poll的用法比较
1. 文件描述符限制
select:文件描述符的数量受限于FD_SETSIZE,通常为1024。poll:没有文件描述符数量的限制。
2. 性能
select:在处理大量文件描述符时,性能较差。poll:在处理大量文件描述符时,性能较好。
3. 线程安全
select:不是线程安全的。poll:是线程安全的。
实例:使用select实现TCP服务器
以下是一个使用select函数实现TCP服务器的简单示例:
#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 <sys/select.h>
#define PORT 8080
#define MAX_CLIENTS 10
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
fd_set master_set, read_fds;
int max_sd;
// 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket creation failed");
return -1;
}
// 设置服务器地址
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(PORT);
// 绑定socket
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
return -1;
}
// 监听socket
if (listen(server_fd, MAX_CLIENTS) < 0) {
perror("listen failed");
return -1;
}
// 初始化文件描述符集合
FD_ZERO(&master_set);
FD_SET(server_fd, &master_set);
max_sd = server_fd;
while (1) {
read_fds = master_set;
if (select(max_sd + 1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select failed");
return -1;
}
// 检查是否有新的连接
if (FD_ISSET(server_fd, &read_fds)) {
client_addr_len = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd == -1) {
perror("accept failed");
continue;
}
printf("new connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 将新的文件描述符添加到主集合中
FD_SET(client_fd, &master_set);
if (client_fd > max_sd) {
max_sd = client_fd;
}
}
// 检查已连接的客户端
for (int i = 0; i <= max_sd; i++) {
if (FD_ISSET(i, &read_fds) && i != server_fd) {
char buffer[1024];
int n = read(i, buffer, sizeof(buffer));
if (n <= 0) {
close(i);
FD_CLR(i, &master_set);
printf("client %d disconnected\n", i);
} else {
write(i, buffer, n);
}
}
}
}
return 0;
}
总结
通过本文的学习,相信你已经对select和poll编程有了初步的了解。在实际应用中,select和poll是网络编程中常用的I/O多路复用技术,掌握它们对于提高程序性能和效率具有重要意义。希望本文能帮助你轻松掌握网络编程的核心技巧。
