在实际情况中,人们往往遇到多个客户端连接服务器端的情况。由于之前介绍的函数如connect,recv,send等都是阻塞性函数,若资源没有充分准备好,则调用该函数的进程将进入睡眠状态,这样就无法处理i/o多路复用的情况了。
本文给出两种i/o多路复用的方法:fcntl(),select()。可以看到,由于linux中把socket当作一种特殊的文件描述符,这给用户的处理带来很大方便。
二、fcntl
fcntl()函数有如下特性:
1)非阻塞i/o: 可将cmd 设为f_setfl,将lock设为o_nonblock
2)信号驱动i/o:可将cmd设为f_setfl,将lock设为o_async.
例程:
#include <sys/types.h>#include <sys/socket.h>#include <sys/wait.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <sys/un.h>#include <sys/time.h>#include <sys/ioctl.h>#include <unistd.h>#include <netinet/in.h>#include <fcntl.h>#include <unistd.h>#define servport 3333#define backlog 10#define max_connected_no 10#define maxdatasize 100int main(){ struct sockaddr_in server_sockaddr,client_sockaddr; int sin_size,recvbytes,flags; int sockfd,client_fd; char buf[maxdatasize];/*创建socket*/ if((sockfd = socket(af_inet,sock_stream,0))==-1){ perror(socket); exit(1); } printf(socket success!,sockfd=%d\n,sockfd);/*设置sockaddr结构*/ server_sockaddr.sin_family=af_inet; server_sockaddr.sin_port=htons(servport); server_sockaddr.sin_addr.s_addr=inaddr_any; bzero(&(server_sockaddr.sin_zero),8);/*将本地ip地址绑定端口号*/ if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){ perror(bind); exit(1); } printf(bind success!\n);/*监听*/ if(listen(sockfd,backlog)==-1){ perror(listen); exit(1); } printf(listening....\n);/*fcntl()函数,处理多路复用i/o*/ if((flags=fcntl( sockfd, f_setfl, 0))<0) perror(fcntl f_setfl); flags |= o_nonblock; if(fcntl( sockfd, f_setfl,flags)<0) perror(fcntl); while(1){ sin_size=sizeof(struct sockaddr_in); if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))==-1){ //服务器接受客户端的请求,返回一个新的文件描述符 perror(accept); exit(1); } if((recvbytes=recv(client_fd,buf,maxdatasize,0))==-1){ perror(recv); exit(1); } if(read(client_fd,buf,maxdatasize)<0){ perror(read); exit(1); } printf(received a connection :%s,buf);/*关闭连接*/ close(client_fd); exit(1); }/*while*/}
运行该程序:
[root@localhost net]# ./fcntlsocket success!,sockfd=3bind success!listening....accept: resource temporarily unavailable
可以看到,当accept的资源不可用时,程序会自动返回。
若将红色加粗代码替换为:
if((flags=fcntl( sockfd, f_setfl, 0))<0) perror(fcntl f_setfl); flags |= o_async; if(fcntl( sockfd, f_setfl,flags)0){ // fd_isset 这个宏判断 sockfd 是否属于可读的文件描述符。从 sockfd 中读入, 输出到标准输出上去. if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){ //client_sockaddr:客户端地址 perror(accept); exit(1); } if((recvbytes=recv(client_fd,buf,maxdatasize,0))==-1){ perror(recv); exit(1); } if(read(client_fd,buf,maxdatasize)<0){ perror(read); exit(1); } printf(received a connection :%s,buf); }/*if*/ close(client_fd); }/*select*/ }/*while*/}运行结果如下:[root@localhost net]# gcc select1.c -o select1[root@localhost net]# ./select1socket create success!bind success!listening...
以上就是linux socket怎么实现多个客户端连接服务器端的详细内容。
