一:多路復用之——select
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout)
1、參數: (1)nfds:需要監視的文件描述符數目;
(2)readfds、writefds、ecceptfds:對應於需要檢測的可讀文件描述符集合、可寫文件描述符集合、異常文件描述符集合;
(3)timeout:NULL:沒有timeout,一直阻塞,直到某個文件描述符發生事件
0:僅檢測文件描述符的狀態,然後立即返回
特定值:等待timeout時間,如果事件沒有發生,超時返回
2、宏:提供處理這三種描述符詞組的方式
(1):FD_CLR(int fd,fd_set* set)清除文件描述符集中相關fd的位
(2):FD_ISSET(int fd,fd_set* set)測試文件描述符集中與fd相關的事件是否發生
(3):FD_SET(int fd,,fd_set* set)設置文件描述符集中相關fd的位
(4):FD_ZERO(int fd,fd_set* set)清除文件描述符集中的全部位
3、timeout:
struct timeout{
long tv_sec 秒
long tv_usec 微妙
}
4、返回值:
(1)成功:返回文件描述符集中狀態改變的文件描述符個數
(2)0:超過timeout時間
(3)-1:失敗
5、理解select模型:
取fd_set長度為1字節,fd_set中的每一bit位對應一個文件描述符fd,則1字節長的fd_set最多可以對應8個fd
(1)執行fd_set set,DF_ZERO(&set),則set用位表示為0000 0000
(2)若fd=5,執行fd_set(fd,&set),後set變為0001 0000
(3)若再加入fd=1,fd=2,則set變為0001 0011
(4)執行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2都發生,則select返回,set變為0000 0011 注意:沒有事件發生的fd=5被清空
6、select模型的特點:
(1)可監控的文件描述符個數取決於sizeof(fd_set)的值;
(2)將fd加入select監控集的同時,還要再使用一個數據結構array保存放到select監控集中的fd。一是用於在select返回後,array作為源數據和fd_set進行FD_ISSET判斷;二是select返回後會把以前加入的但並無事件發生的fd清空,則每次開始select前都要重新從array取得fd逐一加入(先FD_ZERO),掃描array的同時取得fd最大值maxfd,用於select第一個參數;
(3)select模型必須在select前循環array(加fd,取maxfd),select返回後循環array(FD_ISSET判斷是否有事件發生)。
7、select模型的缺點:
(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,當fd很多時,開銷會很大;
(2)在每次調用select時,都要遍歷fd,開銷大;
(3)select支持的文件描述符數目太少,默認是1024,不能處理海量數據。
my_select.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/select.h>
#define _PORT_ 8080
#define _BACK_LOG_ 5
#define _MAX_FD_NUM_ 32
int array_fd[_MAX_FD_NUM_];
int startup()
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_PORT_);
local.sin_addr.s_addr=htonl(INADDR_ANY);
socklen_t len=sizeof(local);
if(bind(sock,(struct sockaddr*)&local,len)<0){
perror("bind");
exit(2);
}
if(listen(sock,_BACK_LOG_)<0){
perror("listen");
exit(3);
}
return sock;
}
int main()
{
int listen_sock=startup();
struct sockaddr_in client;
socklen_t len=sizeof(client);
fd_set read_set;
int i=0;
int max_fd=listen_sock;
array_fd[0]=listen_sock;
for(i=1;i<_MAX_FD_NUM_;i++){
array_fd[i]=-1;
}
while(1){
FD_ZERO(&read_set);
for(i=0;i<_MAX_FD_NUM_;i++){
if(array_fd[i]>0){
FD_SET(array_fd[i],&read_set);
if(max_fd<array_fd[i]){
max_fd=array_fd[i];
}
}
}
struct timeval time_out={3,0};
switch(select(max_fd+1,&read_set,NULL,NULL,&time_out)){
case 0://timeout
printf("timeout...\n");
break;
case -1://error
perror("select");
break;
default:
{
for(i=0;i<_MAX_FD_NUM_;i++){
if(array_fd[i]<0){
continue;
}else if(array_fd[i]==listen_sock && FD_ISSET(array_fd[i],&read_set)){
int new_sock=accept(array_fd[i],(struct sockaddr*)&client,&len);
if(new_sock<0){
continue;
}
printf("get a new connect...\n");
for(i=0;i<_MAX_FD_NUM_;i++){
if(array_fd[i]==-1){
array_fd[i]=new_sock;
break;
}
}
if(i==_MAX_FD_NUM_){
printf("array_fd is full\n");
close(new_sock);
}
}else{
for(i=1;i<_MAX_FD_NUM_;i++){
if(array_fd[i]>0 && FD_ISSET(array_fd[i],&read_set)){
char buf[1024];
memset(buf,'\0',sizeof(buf)-1);
ssize_t _size=read(array_fd[i],buf,sizeof(buf)-1);
if(_size==0){
printf("client close...\n");
close(array_fd[i]);
array_fd[i]=-1;
}else if(_size<0){
}else{
printf("client: %s\n",buf);
}
}
}
}
}
}
break;
}
}
close(listen_sock);
return 0;
}client.c#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#define _PORT_ 8080
int main()
{
int read_fd=0;
int write_fd=1;
fd_set read_set;
fd_set write_set;
int max_fd=0;
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(1);
}
struct sockaddr_in remote;
remote.sin_family=AF_INET;
remote.sin_port=htons(_PORT_);
remote.sin_addr.s_addr=inet_addr("192.168.0.146");
if(connect(sock,(struct sockaddr*)&remote,sizeof(remote))<0){
perror("connect");
exit(2);
}
if(sock>read_fd){
max_fd=sock;
}else{
max_fd=read_fd;
}
while(1){
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(read_fd,&read_set);
FD_SET(sock,&write_set);
switch(select(max_fd+1,&read_set,&write_set,NULL,NULL)){
case 0:
printf("timeout...\n");
break;
case -1:
perror("select");
break;
default:
{
if(FD_ISSET(read_fd,&read_set)){
char buf[1024];
memset(buf,'\0',sizeof(buf));
ssize_t _size=read(read_fd,buf,sizeof(buf)-1);
if(_size>0){
buf[_size]='\0';
printf("echo: %s\n",buf);
}
if(FD_ISSET(sock,&write_set)){
send(sock,buf,strlen(buf),0);
}
}
}
break;
}
}
return 0;
}Makefile.PHONY:all all:my_select client my_select:my_select.c gcc -o $@ $^ client:client.c gcc -o $@ $^ .PHONY:clean clean: rm -rf my_select client