select函数定义
#include
#include
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
返回值:就绪描述符的数目,超时返回0,出错返回-1
参数介绍:
(1)第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2...maxfdp1-1均将被测试,因为文件描述符是从0开始的。
(2)中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
void FD_ZERO(fd_set *fdset); //清空集合
void FD_SET(int fd, fd_set *fdset); //将一个给定的文件描述符加入集合之中
void FD_CLR(int fd, fd_set *fdset); //将一个给定的文件描述符从集合中删除
int FD_ISSET(int fd, fd_set *fdset); // 检查集合中指定的文件描述符是否可以读写
(3)timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。
struct timeval
long tv_sec; //seconds
long tv_usec; //microseconds
};
这个参数有三种可能:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0
select的缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
select服务端程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LISTENQ 1024
#define SERVPORT 7885
#define MAXLINE 4096
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr, cliaddr;
int listenfd, connfd;
fd_set allset, rset;
int result;
socklen_t client_len;
ssize_t n; //read()读取的大小
char buf[MAXLINE];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1)
{
perror("create socket error");
return -1;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVPORT);
inet_pton(AF_INET, "INADDR_ANY", &servaddr.sin_addr);
// 设置套接字选项避免地址使用错误
int on =1;
if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
perror("bind the socket");
return -1;
}
if(listen(listenfd, LISTENQ) == -1)
{
perror("listen the socket");
return -1;
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
while(1)
{
int fd;
int nread;
rset = allset;
FD_ZERO(readfds);
result = select(FD_SETSIZE, &rset, NULL, NULL, NULL);
if(result < 1)
{
perror("server 5");
exit(1);
}
//扫描所有文件描述符
for(fd=0; fd
客户端测试程序:
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 4096
#define SERVPORT 7885
int main(int argc, char *argv[])
{
int client_sockfd;
int len;
struct sockaddr_in address;//服务器端网络地址结构体
int result;
char sendline[MAXLINE],recvline[MAXLINE];
if(argc != 2)
{
printf("usage:tcpcli \n");
return 0;
}
client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket
address.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &address.sin_addr);
address.sin_port = htons(SERVPORT);
len = sizeof(address);
result = connect(client_sockfd, (struct sockaddr *)&address, len);
while(1)
{
if(result == -1)
{
perror("oops: client2");
exit(1);
}
if(fgets(sendline, MAXLINE, stdin) == NULL)
{
return -1;
}
printf("sendline is %s\n", sendline);
write(client_sockfd, sendline, strlen(sendline));
//每次接收前清空recvline
memset(recvline, 0, sizeof(recvline));
read(client_sockfd, recvline, strlen(sendline));
printf("char from server = %s\n", recvline);
}
close(client_sockfd);
return 0;
}