1、i/o复用
一个进程或者一个线程能够同时对多个文件描述符(socket)提供服务,服务器上的进程或线程如何将多个文件描述符统一监听,当任意一个文件描述符上有事件发生,其都能及时处理。
2、select函数
启动监听:int select(int nfds,fd_set *readfds , fd_set *writefds ,fd_set *excefds ,struct timeval *timeout);
每次启动select调用时都能够重新设置值readfds,writefds。
其中fd_set的结构体为
typedef struct
{
int fds_bits[32];
}fd_set;
nfds:最大文件描述符的值+1,
readfds:用户感兴趣的可读事件的文件描述符的集合,
wretefds:用户感兴趣的可写事件的文件描述符的集合,
execfds:异常事件的文件描述符集合,
timeout:设置超时时间。如果timeout为NULL,select一直阻塞。
当select()函数的返回值
大于0 :返回就绪文件描述符的个数
等于0 : 超时
等于-1 :出错
select() 返回后,如何知道那些文件描述符就绪?
以下宏就是用来识别的:
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fd_set);
int FD_ISSET(int fd, fd_set *fdset);
3、以下是select函数的一个简单应用代码
服务端来连接接收数据的代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
void Init_fds(int *fds,int len)
{
int i=0;
for(;i<100;i++)
{
fds[i]=-1;
}
}
void Insert_fd(int *fds,int fd,int len)
{
int i=0;
for(;i<len;i++)
{
if(fds[i] == -1)
{
fds[i] = fd;
break;
}
}
}
void Delete_fd(int *fds,int fd,int len)
{
int i=0;
for(;i<len;i++)
{
if(fds[i] == fd)
{
fds[i]=-1;
break;
}
}
}
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd != -1);
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res != -1);
listen(sockfd,5);
fd_set readfds;
int fds[100];
Init_fds(fds,100);
Insert_fd(fds,sockfd,100);
while(1)
{
int maxfd = -1;
FD_ZERO(&readfds);
int i=0;
for(;i<100;i++)
{
if(fds[i] != -1)
{
if(fds[i] >maxfd)
{
maxfd =fds[i];
}
FD_SET(fds[i],&readfds);
}
}
int n = select(maxfd+1,&readfds,NULL,NULL,NULL);
if(n<=0)
{
printf("select fail\n");
continue;
}
for(i=0;i<100;i++)
{
if(fds[i] != -1&&FD_ISSET(fds[i],&readfds))
{
if(fds[i] ==sockfd)
{
int len = sizeof(caddr);
int c =accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
printf("one cilent link error\n");
continue;
}
Insert_fd(fds,c,100);
}
else
{
int fd = fds[i];
char buff[128] ={0};
int n=recv(fd,buff,127,0);
if(n<=0)
{
close(fd);
Delete_fd(fds,fd,100);
continue;
}
printf("%d %s\n",fd,buff);
send(fd,"OK",2,0);
}
}
}
}
}
客户端来连接发送数据的代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd != -1);
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family =AF_INET;
saddr.sin_port =htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res != -1);
while(1)
{
char buff[128]={0};
printf("Input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
send(sockfd,buff,strlen(buff),0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
}
close(sockfd);
}