掌握Select()系统调用与fd_set在文件描述符中的应用

版权申诉
0 下载量 173 浏览量 更新于2024-11-04 收藏 2KB RAR 举报
资源摘要信息: "Select()系统调用及文件描述符集fd_set的应用" 知识点说明: 1. Select()系统调用: Select()系统调用是POSIX标准定义的一个Unix/Linux系统API,主要用于监控一组文件描述符的变化,如读、写或异常事件的发生。它允许程序同时等待多个文件描述符,而不是使用多个线程或者重复的read/write调用。使用Select()可以提升程序的效率,尤其是在处理大量并发连接时,如在服务器程序中。 2. 文件描述符(File Descriptor): 文件描述符是一个非负整数,用于在Unix/Linux系统中表示打开的文件(包括文件、管道、网络套接字等)。系统通过文件描述符来管理所有的输入/输出操作。通常情况下,一个程序启动时会有三个默认的文件描述符:标准输入(stdin,文件描述符为0),标准输出(stdout,文件描述符为1)和标准错误(stderr,文件描述符为2)。 3. fd_set与文件描述符集: fd_set是一个数据类型,在POSIX标准中用于表示一组文件描述符,是select()系统调用的基础。fd_set提供了一组宏用于操作文件描述符集合,包括设置文件描述符、清除文件描述符、检查文件描述符是否设置等操作。 fd_set的主要宏定义如下: - FD_ZERO:初始化fd_set集合,将所有的文件描述符设置为未就绪状态。 - FD_SET:将指定的文件描述符添加到fd_set集合中。 - FD_CLR:从fd_set集合中移除指定的文件描述符。 - FD_ISSET:检查指定的文件描述符是否已经设置在fd_set集合中。 使用fd_set配合select()系统调用,程序可以等待一组文件描述符中的任何一个或多个变为就绪状态(例如,可读、可写或出现异常),从而实现非阻塞或多路复用的I/O操作。 4. select()的使用方法: Select()函数的原型如下: ```c #include <sys/select.h> int 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结构体,用于设置等待的时间。如果设置为NULL,则select()会无限期等待。 Select()函数返回就绪的文件描述符数量,如果没有文件描述符就绪或者发生错误,则返回-1。 5. 应用场景: Select()系统调用广泛应用于网络编程,特别是当服务器需要同时处理多个客户端连接时。通过使用select(),服务器可以在不阻塞的情况下,监控多个套接字的状态,并对任何活跃的套接字作出响应。这种方式可以显著提高应用程序的效率,尤其是在高并发环境下。 总结: Select()系统调用配合文件描述符集fd_set的应用,是Unix/Linux系统编程中实现高效I/O操作的关键技术。它允许程序在单个线程中有效地管理多个网络连接,使得程序能够在需要时被唤醒,处理网络事件。理解这些概念对于进行高性能网络编程和编写可扩展的服务器应用程序至关重要。

int server_socket_init(){ int server_sockfd; struct sockaddr_in server_address; server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket if(server_sockfd < 0 ) return -1; bzero(&server_address,sizeof(server_address)); server_address.sin_family = AF_INET; //server_address.sin_addr.s_addr = htonl(INADDR_ANY); //本机 server_address.sin_addr.s_addr = inet_addr(SERVER_IP); server_address.sin_port = htons(SERVER_PORT); if(bind(server_sockfd, (struct sockaddr *)&server_address,sizeof(server_address)) < 0 ) { close(server_sockfd); return -1; } if(listen(server_sockfd, 5) < 0) { close(server_sockfd); return -1; } return server_sockfd; } int server_Listening(int server_sockfd) { struct sockaddr_in client_address; int client_sockfd, ret = 0; int select_result,fd,client_len,data_size; struct timeval timeout; fd_set readfds, testfds; FD_ZERO(&readfds); FD_SET(server_sockfd, &readfds); while(1) { //每一轮监听后结构体被清0,每监听完一轮就要对结构体重新赋值,指定监听对象 testfds = readfds; timeout.tv_sec = 2; timeout.tv_usec = 500000; select_result = select(FD_SETSIZE, &testfds,NULL,NULL,NULL); if (select_result < 0) { return -1; } //perr_exit("select error"); for(fd = 0; fd < FD_SETSIZE; fd++) /*扫描所有的socket(文件)描述符*/ { if(FD_ISSET(fd,&testfds))/*找到可以读写相关socket(文件)描述符*/ { if(fd == server_sockfd) //为服务器socket,是则表示为客户请求连接。 { client_len = sizeof(client_address); client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len); if(client_sockfd < 0) return -1; FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中 } else //客户端socket中有数据请求时 { ioctl(fd, FIONREAD, &data_size);//nread得到fd缓冲区的大小,就是当client写入缓冲区,这操作是读取缓冲区的大小 // n=read(fd,buf,sizeof(buf));//n即和nread一致 /*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */ if(data_size == 0) { //test FASTCGI_LOG("\n client_close_remore :%d\n\n\n\n",fd); close(fd); FD_CLR(fd, &readfds); } else if(!PerformServerTransfer(fd)){ return -1; } } } } } }这个是tcp server端有误么

2023-07-12 上传