网站评估内容 优帮云,网页游戏平台软件,策划营销公司企业介绍,nas网站怎么做网站目录 第一章#xff1a;select基础
1.1 select概述
1.2 select的使用场景
1.3 select的函数原型
1.4 fd_set和timeval结构体
1.5 select的基本流程
1.6 select的局限性
1.7 小结
第二章#xff1a;select的高级使用
2.1 处理select的超时
2.1.1 设置永久等待
2.1.…目录 第一章select基础
1.1 select概述
1.2 select的使用场景
1.3 select的函数原型
1.4 fd_set和timeval结构体
1.5 select的基本流程
1.6 select的局限性
1.7 小结
第二章select的高级使用
2.1 处理select的超时
2.1.1 设置永久等待
2.1.2 设置非阻塞等待
2.2 避免select的局限性
2.2.1 使用poll代替select
2.2.2 使用epoll仅限于Linux
2.3 select的错误处理
2.4 select在多线程环境中的应用
2.5 小结
第三章select的应用实例
3.1 简单的echo服务器
3.1.1 echo服务器的实现
3.2 并发聊天服务器
3.3 小结 第一章select基础
1.1 select概述
select是C语言中用于IO多路复用的一个系统调用它允许程序监视多个文件描述符通常是网络套接字的状态以便在某个文件描述符准备好进行IO操作时获得通知。select的这种能力使得单个线程能够管理多个并发连接从而实现高效的并发处理。
1.2 select的使用场景
select通常用于以下场景
当需要同时处理多个套接字时例如在网络服务器中。当需要同时监听标准输入和其他套接字时。当需要等待多个套接字事件如读就绪、写就绪、异常时。
1.3 select的函数原型
select的函数原型如下
#include sys/select.hint select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);nfds监视的文件描述符集合中最大文件描述符加1。readfds指向fd_set结构的指针用于监视读就绪的文件描述符。writefds指向fd_set结构的指针用于监视写就绪的文件描述符。exceptfds指向fd_set结构的指针用于监视异常的文件描述符。timeout指向timeval结构的指针用于设置select的超时时间。
1.4 fd_set和timeval结构体
fd_set是一个位掩码用于表示文件描述符集合。在select中它用来指定需要监视的文件描述符。timeval结构体用于指定select函数的超时时间。
struct timeval {long tv_sec; // 秒long tv_usec; // 微秒
};1.5 select的基本流程
使用select的基本流程如下
初始化fd_set集合将需要监视的文件描述符加入到相应的集合中。调用select函数等待文件描述符就绪或超时。检查select返回值处理就绪的文件描述符。根据需要重复以上步骤。
1.6 select的局限性
select虽然是一个强大的工具但它也有局限性
fd_set集合的大小受限于系统定义的FD_SETSIZE通常为1024这意味着select不能有效地处理超过1024个并发连接。每次调用select都需要重新设置文件描述符集合这在有大量文件描述符时效率较低。select返回后需要遍历整个文件描述符集合来确定哪些文件描述符就绪这在高并发场景下可能成为性能瓶颈。
1.7 小结
本章介绍了select的基础知识包括select的使用场景、函数原型、基本流程以及局限性。通过本章的学习读者应该对select有了初步的了解为后续深入学习select的高级使用和应用实例打下了基础。在下一章中我们将探讨select的高级使用技巧包括如何处理select的超时、如何避免select的局限性等。 第二章select的高级使用
2.1 处理select的超时
在select中超时参数timeout可以用来指定select函数等待的最大时间。如果在这个时间内没有任何文件描述符就绪select将返回0。正确处理select的超时对于实现高效的网络程序至关重要。
2.1.1 设置永久等待
如果要使select永久等待直到至少一个文件描述符就绪可以将timeout设置为NULL
struct timeval timeout;
timeout.tv_sec 0;
timeout.tv_usec 0;int ret select(nfds, readfds, writefds, exceptfds, NULL);2.1.2 设置非阻塞等待
如果要使select在指定的时间后超时可以设置timeout结构体的tv_sec和tv_usec字段
struct timeval timeout;
timeout.tv_sec 5; // 等待5秒
timeout.tv_usec 0; // 微秒int ret select(nfds, readfds, writefds, exceptfds, timeout);2.2 避免select的局限性
虽然select有局限性但可以通过一些技巧来避免或减轻这些问题。
2.2.1 使用poll代替select
poll是另一个IO多路复用函数它没有select的文件描述符数量限制并且提供了更高效的接口来处理大量的文件描述符。
2.2.2 使用epoll仅限于Linux
epoll是Linux特有的IO多路复用机制它解决了select和poll的一些问题如文件描述符数量限制和效率问题。epoll使用事件驱动的机制可以高效地处理大量的文件描述符。
2.3 select的错误处理
select在调用过程中可能会遇到错误常见的错误包括
EBADF文件描述符集合中有无效的文件描述符。EINTRselect被信号中断。EINVALselect的参数无效如nfds小于0或timeout的值非法。
正确处理这些错误对于确保程序的健壮性非常重要。
2.4 select在多线程环境中的应用
在多线程环境中select可以与线程结合起来使用以提高程序的性能和并发处理能力。例如可以在一个线程中使用select来监听多个套接字然后在其他线程中处理就绪的套接字。
2.5 小结
本章介绍了select的高级使用包括处理select的超时、避免select的局限性、错误处理以及在多线程环境中的应用。通过本章的学习读者应该能够更加熟练地使用select并能够应对一些复杂的网络编程场景。在下一章中我们将通过一些实际的应用实例来进一步巩固对select的理解。 第三章select的应用实例
3.1 简单的echo服务器
echo服务器是一个基本的网络编程实例它接收客户端发送的数据并将其原样返回。使用select可以轻松实现一个多客户端的echo服务器。
3.1.1 echo服务器的实现
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include sys/time.h#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024int main() {int server_fd, client_fd, max_fd;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len;fd_set readfds, temp_readfds;char buffer[BUFFER_SIZE];int client_sockets[MAX_CLIENTS] {0};// 创建套接字server_fd socket(AF_INET, SOCK_STREAM, 0);if (server_fd 0) {perror(socket creation failed);exit(EXIT_FAILURE);}// 绑定地址和端口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(12345);if (bind(server_fd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) {perror(bind failed);exit(EXIT_FAILURE);}// 监听if (listen(server_fd, MAX_CLIENTS) 0) {perror(listen failed);exit(EXIT_FAILURE);}// 初始化fd_setFD_ZERO(readfds);FD_SET(server_fd, readfds);max_fd server_fd;while (1) {temp_readfds readfds;// select等待就绪文件描述符int activity select(max_fd 1, temp_readfds, NULL, NULL, NULL);if (activity 0) {perror(select failed);exit(EXIT_FAILURE);}// 检查服务器套接字if (FD_ISSET(server_fd, temp_readfds)) {client_addr_len sizeof(client_addr);client_fd accept(server_fd, (struct sockaddr *)client_addr, client_addr_len);if (client_fd 0) {perror(accept failed);exit(EXIT_FAILURE);}printf(New client connected, socket fd is %d\n, client_fd);// 添加到客户端套接字数组for (int i 0; i MAX_CLIENTS; i) {if (client_sockets[i] 0) {client_sockets[i] client_fd;break;}}// 更新最大文件描述符if (client_fd max_fd) {max_fd client_fd;}}// 检查客户端套接字for (int i 0; i MAX_CLIENTS; i) {client_fd client_sockets[i];if (client_fd 0 FD_ISSET(client_fd, temp_readfds)) {memset(buffer, 0, BUFFER_SIZE);int bytes_received recv(client_fd, buffer, BUFFER_SIZE, 0);if (bytes_received 0) {// 客户端断开连接close(client_fd);client_sockets[i] 0;} else {// 发送回显数据send(client_fd, buffer, bytes_received, 0);}}}}return 0;
}3.2 并发聊天服务器
聊天服务器是一个稍微复杂的应用实例它允许多个客户端连接并相互发送消息。使用select可以同时处理多个客户端的连接和消息传递。
3.2.1 聊天服务器的实现
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include sys/time.h
#include pthread.h#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024typedef struct {int client_fd;struct sockaddr_in client_addr;
} client_info;client_info clients[MAX_CLIENTS];
int num_clients 0;
pthread_mutex_t lock;void *client_handler(void *arg) {client_info *client (client_info *)arg;char buffer[BUFFER_SIZE];int bytes_received;while (1) {memset(buffer, 0, BUFFER_SIZE);bytes_received recv(client-client_fd, buffer, BUFFER_SIZE, 0); if (bytes_received 0) { // 客户端断开连接 break; }// 广播消息给所有客户端for (int i 0; i MAX_CLIENTS; i) {if (clients[i].client_fd 0) {send(clients[i].client_fd, buffer, bytes_received, 0);}}}// 清理客户端信息pthread_mutex_lock(lock);for (int i 0; i MAX_CLIENTS; i) {if (clients[i].client_fd client-client_fd) {clients[i].client_fd 0;num_clients--;break;}}pthread_mutex_unlock(lock);close(client-client_fd);free(client);return NULL;
}int main() { int server_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_len; fd_set readfds, temp_readfds; int max_fd; pthread_t thread_id;// 初始化互斥锁pthread_mutex_init(lock, NULL);// 创建套接字server_fd socket(AF_INET, SOCK_STREAM, 0);if (server_fd 0) {perror(socket creation failed);exit(EXIT_FAILURE);}// 绑定地址和端口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(12345);if (bind(server_fd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) {perror(bind failed);exit(EXIT_FAILURE);}// 监听if (listen(server_fd, MAX_CLIENTS) 0) {perror(listen failed);exit(EXIT_FAILURE);}// 初始化fd_setFD_ZERO(readfds);FD_SET(server_fd, readfds);max_fd server_fd;while (1) {temp_readfds readfds;// select等待就绪文件描述符int activity select(max_fd 1, temp_readfds, NULL, NULL, NULL);if (activity 0) {perror(select failed);exit(EXIT_FAILURE);}// 检查服务器套接字if (FD_ISSET(server_fd, temp_readfds)) {client_addr_len sizeof(client_addr);client_fd accept(server_fd, (struct sockaddr *)client_addr, client_addr_len);if (client_fd 0) {perror(accept failed);exit(EXIT_FAILURE);}printf(New client connected, socket fd is %d\n, client_fd);// 添加客户端信息pthread_mutex_lock(lock);for (int i 0; i MAX_CLIENTS; i) {if (clients[i].client_fd 0) {clients[i].client_fd client_fd;clients[i].client_addr client_addr;num_clients;break;}}pthread_mutex_unlock(lock);// 创建线程处理新客户端client_info *new_client malloc(sizeof(client_info));*new_client clients[num_clients - 1];pthread_create(thread_id, NULL, client_handler, (void *)new_client);}}// 清理资源close(server_fd);pthread_mutex_destroy(lock);return 0;
}
3.3 小结
本章通过两个应用实例展示了select在实际网络编程中的应用。第一个实例是一个简单的echo服务器它使用select来同时处理多个客户端的连接并将客户端发送的数据原样返回。第二个实例是一个并发聊天服务器它不仅使用select来处理多个客户端的连接还使用多线程来同时处理客户端的消息实现了广播功能。这些实例展示了select在多客户端网络应用中的强大功能并为进一步探索网络编程提供了基础。