C语言详解select函数的使用
什么是select函数?
select函数是Linux系统中的多路复用函数,它通过检查一组文件描述符(socket、文件、管道等)的状态来实现同时监视多个文件描述符的读写状态,并在其中的一个文件描述符可读写时进行相应的处理。可以说,select函数是实现I/O多路复用的重要工具之一。
select函数的语法
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- nfds:指最大的文件描述符加1,即文件描述符集合中所有文件描述符的范围,nfds参数常常被设置为需要检查的文件描述符集合中的最大文件描述符+1。
- readfds:可读文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符可读了,则相应元素会被置为1,否则为0。
- writefds:可写文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符可写了,则相应元素会被置为1,否则为0。
- exceptfds:异常文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符出现异常情况了,例如收到外带数据,该元素会被置为1,否则为0。
- timeout:select超时时间,分两种情况:当它的值为NULL时,表示无限等待;如果在超时时间内仍没有事件到达,则返回0。当它的值不为NULL时,最长等待时间为timeout。如果时间耗尽还没有I/O事件发生,则该函数返回0。
select函数使用示例
示例一:使用select函数实现简单的回显服务器
下面的示例演示如何使用select函数实现简单的回显服务器。该服务器首先监听来客户端的连接,当有客户端连接到服务器后,便会以子进程的方式处理客户端请求,并进行回显处理。具体实现过程如下所示:
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#define MAXLINE 1024
#define SERV_PORT 7000
void str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
while (1)
{
memset(buf, 0, MAXLINE);
if ((n = read(sockfd, buf, MAXLINE)) == 0)
return;
write(sockfd, buf, n);
}
}
int main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket error");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
perror("bind error");
exit(1);
}
if (listen(listenfd, SOMAXCONN) == -1)
{
perror("listen error");
exit(1);
}
fd_set rset, allset;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
int maxfd = listenfd;
int nready, i, sockfd;
while (1)
{
rset = allset;
if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) == -1)
{
perror("select error");
exit(1);
}
if (FD_ISSET(listenfd, &rset))
{
clilen = sizeof(cliaddr);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) == -1)
{
perror("accept error");
exit(1);
}
if ((childpid = fork()) == 0)
{
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
exit(0);
}
示例二:使用select函数实现简单的客户端
下面的示例演示如何使用select函数实现简单的客户端。该客户端首先创建一个socket套接字,然后向指定的服务器发起一次连接,最后可以向服务器发送数据,并等待来自服务器的回应。具体实现过程如下所示:
#include <string.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#define MAXLINE 1024
#define SERV_PORT 7000
int main(int argc, char **argv)
{
int sockfd, n;
char recvline[1024], sendline[1024];
struct sockaddr_in servaddr;
if (argc != 2)
{
printf("usage: ./client <IPaddress>\n");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket error");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
{
printf("inet_pton error for %s\n", argv[1]);
exit(1);
}
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect error");
exit(1);
}
fd_set rset;
FD_ZERO(&rset);
while (1)
{
bzero(sendline, 1024);
bzero(recvline, 1024);
FD_SET(fileno(stdin), &rset);
FD_SET(sockfd, &rset);
int maxfd = (fileno(stdin) > sockfd ? fileno(stdin) : sockfd) + 1;
if (select(maxfd, &rset, NULL, NULL, NULL) == -1)
{
perror("select");
exit(1);
}
if (FD_ISSET(fileno(stdin), &rset))
{
fgets(sendline, 1024, stdin);
if (send(sockfd, sendline, strlen(sendline), 0) < 0)
{
perror("send error");
exit(1);
}
}
if (FD_ISSET(sockfd, &rset))
{
if ((n = recv(sockfd, recvline, MAXLINE, 0)) == -1)
{
if (errno == ECONNRESET)
{
printf("server close my socket\n");
close(sockfd);
exit(EXIT_FAILURE);
}
perror("recv error");
exit(1);
}
if (n == 0)
{
printf("server close my socket\n");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("recv the echoed msg:%s\n", recvline);
}
}
exit(0);
}
总结
本文详细介绍了select函数的用法及其相关示例,包括使用select函数实现简单的回显服务器和客户端。使用select函数能够实现I/O多路复用,提高程序的性能和效率,是Linux网络编程中不可或缺的一部分。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言详解select函数的使用 - Python技术站