Linux為多路復用IO提供了較多的接口,有select(),pselect(),poll()的方式,繼承自BSD和System V 兩大派系。
select模型比較簡單,“輪詢”檢測fd_set的狀態,然後再采取相應的措施。
信號驅動模型有必要仔細研究一下,一般有如下步驟:
On Linux, specifying the O_ASYNC
flag when calling open() has no effect. To enable signal-driven I/O, we must
instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1
具體進下文client例程。
寫了一個聊天程序的demo,把這兩種技術都使用了。服務端采取多路復用的IO方式,代替多進(線)程的模型,客服端采取的是信號驅動,如下:
容易產生bug的地方都寫注釋裡邊了。
serv.c
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
void endServ(int sig)
{
printf("Server ended!\n");
exit(-1);
}
int main()
{
// signal
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = endServ;
act.sa_flags = 0;
sigaction(SIGINT,&act,0);
printf("This server is started,enter CTRL+C to end. \n");
int servfd = socket(AF_INET,SOCK_STREAM,0);
if(servfd == -1) {
printf("something wrong with socket():%m\n");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) {
printf("Some thing wrong with bind():%m\n");
exit(-1);
}
printf("Bind success!\n");
listen(servfd,20);
int numbers=0;//how many clients has accepted
fd_set fs;
FD_ZERO(&fs);
int client_fd[100];
int i;
for(i=0;i<100;++i)
{
client_fd[i] = -1;
}
int maxfd=0;
char buf[1024];
for(;;)
{
maxfd =0;
FD_ZERO(&fs);
FD_SET(servfd,&fs);
maxfd = maxfd>servfd?maxfd:servfd;
for(i=0;i<numbers;++i)
{
if(client_fd[i] != -1) {
FD_SET(client_fd[i],&fs);
maxfd = maxfd>client_fd[i]?maxfd:client_fd[i];
}
}
int res = select(maxfd+1,&fs,0,0,0);
if(res == -1) {
printf("Something wrong with select():%m\n");
exit(-1);
}
if(FD_ISSET(servfd,&fs) && numbers < 100) {
printf("New client!\n");
client_fd[numbers] = accept(servfd,0,0);
numbers++;
}
for(i=0;i<numbers;++i)
{
bzero(buf,sizeof(buf));
//judge if client_fd[i] equal -1 is necessary
//if a client quited,next time the program will
//have a segment default
//also it should be in the front.
if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs))
{
res = recv(client_fd[i],buf,sizeof(buf),0);
if(res == 0) {
printf("A client quit\n");
close(client_fd[i]);
FD_CLR(client_fd[i],&fs);
client_fd[i] = -1;
}
else if(res == -1) {
printf("Something wrong with net.\n");
exit(-1);
}
else {
int j;
for(j=0;j<numbers;++j)
{
if(client_fd[j] != -1)
send(client_fd[j],buf,sizeof(buf),0);
}
}
}
}
}
}
client:
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static int fd;
void show(int sig)
{
char buf[1024] = {0};
int n = read(fd,buf,sizeof(buf));
buf[n] = 0;
write(1,"MSG:",strlen("MSG:"));
write(1,buf,strlen(buf));
}
int main()
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = show;
//This is necessary,in last loop read() counld be interrupt;
act.sa_flags = SA_RESTART;
sigaction(SIGIO,&act,0);
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1) {
printf("Cannot get socketfd!:%m\n");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1)
printf("connect success!\n");
else
exit(-1);
int flag = fcntl(fd,F_GETFL);
flag |= O_ASYNC;
fcntl(fd,F_SETFL,flag);
fcntl(fd,F_SETOWN,getpid());
char buffer[1024]={0};
for(;;)
{
int n = read(0,buffer,sizeof(buffer));
if(n==-1)
break;
send(fd,buffer,n,0);
}
write(1,"Closed.",strlen("Closed."));
}