linux socket网络编程:fcntl select(多个客户端连接服务器端情形)

一、引言

    在实际情况中,人们往往遇到多个客户端连接服务器端的情况。由于之前介绍的函数如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.

例程:

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <sys/wait.h>  
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <errno.h>  
  7. #include <string.h>  
  8. #include <sys/un.h>  
  9. #include <sys/time.h>  
  10. #include <sys/ioctl.h>  
  11. #include <unistd.h>  
  12. #include <netinet/in.h>  
  13. #include <fcntl.h>  
  14. #include <unistd.h>  
  15.   
  16. #define SERVPORT 3333  
  17. #define BACKLOG 10  
  18. #define MAX_CONNECTED_NO 10  
  19. #define MAXDATASIZE 100  
  20.   
  21. int main()  
  22. {  
  23.     struct sockaddr_in server_sockaddr,client_sockaddr;  
  24.     int sin_size,recvbytes,flags;  
  25.     int sockfd,client_fd;  
  26.     char buf[MAXDATASIZE];  
  27. /*创建socket*/  
  28.     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){  
  29.         perror("socket");  
  30.         exit(1);  
  31.     }  
  32.     printf("socket success!,sockfd=%d\n",sockfd);  
  33.   
  34. /*设置sockaddr结构*/  
  35.     server_sockaddr.sin_family=AF_INET;  
  36.     server_sockaddr.sin_port=htons(SERVPORT);  
  37.     server_sockaddr.sin_addr.s_addr=INADDR_ANY;  
  38.     bzero(&(server_sockaddr.sin_zero),8);  
  39.   
  40. /*将本地ip地址绑定端口号*/  
  41.     if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){  
  42.         perror("bind");  
  43.         exit(1);  
  44.     }  
  45.     printf("bind success!\n");  
  46.   
  47. /*监听*/  
  48.     if(listen(sockfd,BACKLOG)==-1){  
  49.         perror("listen");  
  50.         exit(1);  
  51.     }  
  52.     printf("listening....\n");  
  53.   
  54. /*fcntl()函数,处理多路复用I/O*/  
  55.     if((flags=fcntl( sockfd, F_SETFL, 0))<0)  
  56.             perror("fcntl F_SETFL");  
  57.         flags |= O_NONBLOCK;  
  58.         if(fcntl( sockfd, F_SETFL,flags)<0)  
  59.             perror("fcntl");  
  60.     while(1){  
  61.         sin_size=sizeof(struct sockaddr_in);  
  62.         if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))==-1){  //服务器接受客户端的请求,返回一个新的文件描述符  
  63.             perror("accept");  
  64.             exit(1);  
  65.         }  
  66.         if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){  
  67.             perror("recv");  
  68.             exit(1);  
  69.         }  
  70.         if(read(client_fd,buf,MAXDATASIZE)<0){  
  71.             perror("read");  
  72.             exit(1);  
  73.         }  
  74.         printf("received a connection :%s",buf);  
  75.   
  76. /*关闭连接*/  
  77.     close(client_fd);  
  78.     exit(1);  
  79.     }/*while*/  
  80. }  


运行该程序:
  1. [root@localhost net]# ./fcntl  
  2. socket success!,sockfd=3  
  3. bind success!  
  4. listening....  
  5. accept: Resource temporarily unavailable  
可以看到,当accept的资源不可用时,程序会自动返回。

若将红色加粗代码替换为:

  1. <span style="color:#FF6666;"if((flags=fcntl( sockfd, F_SETFL, 0))<0)  
  2.             perror("fcntl F_SETFL");  
  3.         <strong>flags |= O_ASYNC;</strong>  
  4.         if(fcntl( sockfd, F_SETFL,flags)<0)  
  5.             perror("fcntl");</span>  
运行结果如下:
  1. [root@localhost net]# ./fcntl1  
  2. socket success!,sockfd = 3  
  3. bind success!  
  4. listening...  
可以看到,进程一直处于等待中,直到另一相关信号驱动它为止。

三、select

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <sys/wait.h>  
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <errno.h>  
  7. #include <string.h>  
  8. #include <sys/un.h>  
  9. #include <sys/time.h>  
  10. #include <sys/ioctl.h>  
  11. #include <unistd.h>  
  12. #include <netinet/in.h>  
  13. #define SERVPORT 3333  
  14. #define BACKLOG 10  
  15. #define MAX_CONNECTED_NO 10  
  16. #define MAXDATASIZE 100  
  17. int main()  
  18. {  
  19.     struct sockaddr_in server_sockaddr,client_sockaddr;  
  20.     int sin_size,recvbytes;  
  21.     fd_set readfd;  
  22.     fd_set writefd;  
  23.     int sockfd,client_fd;  
  24.     char buf[MAXDATASIZE];  
  25. /*创建socket*/  
  26.     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){  
  27.         perror("socket");  
  28.         exit(1);  
  29.     }  
  30.     printf("socket success!,sockfd=%d\n",sockfd);  
  31. /*设置sockaddr结构*/  
  32.     server_sockaddr.sin_family=AF_INET;  
  33.     server_sockaddr.sin_port=htons(SERVPORT);  
  34.     server_sockaddr.sin_addr.s_addr=INADDR_ANY;  
  35.     bzero(&(server_sockaddr.sin_zero),8);  
  36. /*将本地ip地址绑定端口号*/  
  37.     if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){  
  38.         perror("bind");  
  39.         exit(1);  
  40.     }  
  41.     printf("bind success!\n");  
  42. /*监听*/  
  43.     if(listen(sockfd,BACKLOG)==-1){  
  44.         perror("listen");  
  45.         exit(1);  
  46.     }  
  47.     printf("listening....\n");  
  48. /*select*/  
  49.     FD_ZERO(&readfd);              // 将readfd 清空   
  50. FD_SET(sockfd,&readfd);         //将sockfd加入到readfd集合中  
  51.     while(1){  
  52.     sin_size=sizeof(struct sockaddr_in);  
  53.     if(select(MAX_CONNECTED_NO,&readfd,NULL,NULL,(struct timeval *)0)>0){  //第一个参数是0和sockfd的最大值加1,第二个参数是读集,第三、四个参数是写集                                                                                //和异常集  
  54.         if(FD_ISSET(sockfd,&readfd)>0){         // FD_ISSET 这个宏判断 sockfd 是否属于可读的文件描述符。从 sockfd 中读入, 输出到标准输出上去.  
  55.             if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){   //client_sockaddr:客户端地址  
  56.                 perror("accept");  
  57.                 exit(1);  
  58.             }  
  59.             if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){  
  60.                 perror("recv");  
  61.                 exit(1);  
  62.             }  
  63.             if(read(client_fd,buf,MAXDATASIZE)<0){  
  64.                 perror("read");  
  65.                 exit(1);  
  66.             }  
  67.             printf("received a connection :%s",buf);  
  68.         }/*if*/  
  69.         close(client_fd);  
  70.         }/*select*/  
  71.     }/*while*/  
  72. }  
  73. 运行结果如下:  
  74. [root@localhost net]#  gcc select1.c -o select1  
  75. [root@localhost net]# ./select1  
  76. socket create success!  
  77. bind success!  
  78. listening...  

四、思考


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值