select简介:
Linux系统提供了C语言函数select来进行各种事件的监听。在网络数据的多路复用中,select有着广泛的应用。
更多传送门:
Linux系统中IO多路复用select函数的使用,及其在Android系统中的运用
select函数说明:
函数声明:
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
功能:
select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set); 用来清除描述词组set的全部位
参数:
timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
struct timeval
{
time_t tv_sec;
time_t tv_usec;
};
返回值:
如果参数timeout设为NULL则表示select()没有timeout。
错误代码
执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数n 为负值。
ENOMEM 核心内存不足
select举例:
一个server监听多个client的请求:
server端代码(server3.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAX_SOCKET_CNT 10
main()
{
int ret = -1;
//执行connect后的返回值
int conn_fd = -1;
int fd;
//ip端口,必须和client的一样,才能正常建立链接
int server_ip_port = 885;
int is_connected[MAX_SOCKET_CNT];
char buffer[256];
char msg[] = "Hello from server!";
fd_set readfds;
//初始化sockaddr_in结构体
struct sockaddr_in t_sockaddr;
memset(&t_sockaddr, 0, sizeof(t_sockaddr));
t_sockaddr.sin_family = AF_INET;
t_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
t_sockaddr.sin_port = htons(server_ip_port);
int sock_addr_len = sizeof(struct sockaddr_in);
//创建server端的socket套接字
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
}
//绑定
ret = bind(listen_fd,(struct sockaddr *) &t_sockaddr,sizeof(t_sockaddr));
if (ret < 0) {
fprintf(stderr, "bind socket error %s errno: %d\n", strerror(errno), errno);
}
//监听
ret = listen(listen_fd, 100);
if (ret < 0) {
fprintf(stderr, "listen error %s errno: %d\n", strerror(errno), errno);
}
for ( fd = 0; fd < MAX_SOCKET_CNT; fd++ ){
is_connected[fd] = 0;
}
//loop
for ( ;; ) {
FD_ZERO( &readfds );
FD_SET( listen_fd, &readfds );
for ( fd = 0; fd < MAX_SOCKET_CNT; fd++ )
//初始化fd
if ( is_connected[fd] )
FD_SET( fd, &readfds );
//多路复用select监听
if ( !select( MAX_SOCKET_CNT, &readfds, NULL, NULL, NULL ) )
continue;
for ( fd = 0; fd < MAX_SOCKET_CNT; fd++ ) {
if ( FD_ISSET( fd, &readfds ) )
{
if ( listen_fd == fd )
{
if ( (conn_fd = accept( listen_fd, &t_sockaddr, &sock_addr_len ) ) < 0 )
perror( "accept" );
write( conn_fd, msg, sizeof(msg) );
is_connected[conn_fd] = 1;
printf( "cnnect from %s\n", inet_ntoa( t_sockaddr.sin_addr ) );
}else{
bzero( buffer, sizeof(buffer) );
if ( read( fd, buffer, sizeof(buffer) ) <= 0 )
{
printf( "connect closed.\n" );
is_connected[fd] = 0;
close( fd );
}else
printf( "%s", buffer );
}
}
}
}
}
client端代码(client3.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
char *server_ip_addr = "127.0.0.1";
int server_ip_port = 885;
char buffer[256];
//创建client端的socket套接口
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 0) {
//fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
perror( "create socket error" );
}
//初始化sockaddr_in结构体
struct sockaddr_in t_sockaddr;
memset(&t_sockaddr, 0, sizeof(struct sockaddr_in));
t_sockaddr.sin_family = AF_INET;
t_sockaddr.sin_port = htons(server_ip_port);
inet_pton(AF_INET, server_ip_addr, &t_sockaddr.sin_addr);
//连接
if((connect(socket_fd, (struct sockaddr*)&t_sockaddr, sizeof(struct sockaddr))) < 0 ) {
//fprintf(stderr, "connect error %s errno: %d\n", strerror(errno), errno);
perror( "connect error" );
return 0;
}
//接收由server端传来的信息
recv( socket_fd, buffer, sizeof(buffer), 0 );
printf( "%s\n", buffer );
for(;;) {
bzero( buffer, sizeof(buffer) );
//将标准输入的结果放在buffer数组中
read( STDIN_FILENO, buffer, sizeof(buffer) );
//将buffer中的字符串发送到server端
if ( send( socket_fd, buffer, sizeof(buffer), 0 ) < 0 )
{
perror( "send" );
exit( 1 );
}
}
}
编译:
gcc server3.c -o server3
gcc client3.c -o client3
server端运行结果:
% ./server3
cnnect from 127.0.0.1
abc
ddd
cnnect from 127.0.0.1
123
456
client端运行结果:
//终端1
% ./client3
Hello from server!
abc
ddd
//终端2
% ./client3
Hello from server!
123
456
实现了一个sever端,可以接收多个client的数据请求。
例子程序中,设置了最大连接数为:
#define MAX_SOCKET_CNT 10