一、引言

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

例程:

[cpp] view plaincopy
  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. #defineSERVPORT3333
  16. #defineBACKLOG10
  17. #defineMAX_CONNECTED_NO10
  18. #defineMAXDATASIZE100
  19. intmain()
  20. {
  21. structsockaddr_inserver_sockaddr,client_sockaddr;
  22. intsin_size,recvbytes,flags;
  23. intsockfd,client_fd;
  24. charbuf[MAXDATASIZE];
  25. /*创建socket*/
  26. if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
  27. perror("socket");
  28. exit(1);
  29. }
  30. printf("socketsuccess!,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,(structsockaddr*)&server_sockaddr,sizeof(structsockaddr))==-1){
  38. perror("bind");
  39. exit(1);
  40. }
  41. printf("bindsuccess!\n");
  42. /*监听*/
  43. if(listen(sockfd,BACKLOG)==-1){
  44. perror("listen");
  45. exit(1);
  46. }
  47. printf("listening....\n");
  48. /*fcntl()函数,处理多路复用I/O*/
  49. if((flags=fcntl(sockfd,F_GETFL,0))<0)
  50. perror("fcntlF_SETFL");
  51. flags|=O_NONBLOCK;
  52. if(fcntl(sockfd,F_SETFL,flags)<0)
  53. perror("fcntl");
  54. while(1){
  55. sin_size=sizeof(structsockaddr_in);
  56. if((client_fd=accept(sockfd,(structsockaddr*)&client_sockaddr,&sin_size))==-1){//服务器接受客户端的请求,返回一个新的文件描述符
  57. perror("accept");
  58. exit(1);
  59. }
  60. if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){
  61. perror("recv");
  62. exit(1);
  63. }
  64. if(read(client_fd,buf,MAXDATASIZE)<0){
  65. perror("read");
  66. exit(1);
  67. }
  68. printf("receivedaconnection:%s",buf);
  69. /*关闭连接*/
  70. close(client_fd);
  71. exit(1);
  72. }/*while*/
  73. }


运行该程序:
[cpp] view plaincopy
  1. [root@localhostnet]#./fcntl
  2. socketsuccess!,sockfd=3
  3. bindsuccess!
  4. listening....
  5. accept:Resourcetemporarilyunavailable
可以看到,当accept的资源不可用时,程序会自动返回。

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

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

三、select

[cpp] view plaincopy
  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. #defineSERVPORT3333
  14. #defineBACKLOG10
  15. #defineMAX_CONNECTED_NO10
  16. #defineMAXDATASIZE100
  17. intmain()
  18. {
  19. structsockaddr_inserver_sockaddr,client_sockaddr;
  20. intsin_size,recvbytes;
  21. fd_setreadfd;
  22. fd_setwritefd;
  23. intsockfd,client_fd;
  24. charbuf[MAXDATASIZE];
  25. /*创建socket*/
  26. if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
  27. perror("socket");
  28. exit(1);
  29. }
  30. printf("socketsuccess!,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,(structsockaddr*)&server_sockaddr,sizeof(structsockaddr))==-1){
  38. perror("bind");
  39. exit(1);
  40. }
  41. printf("bindsuccess!\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(structsockaddr_in);
  53. if(select(MAX_CONNECTED_NO,&readfd,NULL,NULL,(structtimeval*)0)>0){//第一个参数是0和sockfd的最大值加1,第二个参数是读集,第三、四个参数是写集//和异常集
  54. if(FD_ISSET(sockfd,&readfd)>0){//FD_ISSET这个宏判断sockfd是否属于可读的文件描述符。从sockfd中读入,输出到标准输出上去.
  55. if((client_fd=accept(sockfd,(structsockaddr*)&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("receivedaconnection:%s",buf);
  68. }/*if*/
  69. close(client_fd);
  70. }/*select*/
  71. }/*while*/
  72. }
  73. 运行结果如下:
  74. [root@localhostnet]#gccselect1.c-oselect1
  75. [root@localhostnet]#./select1
  76. socketcreatesuccess!
  77. bindsuccess!
  78. listening...

更多相关文章

  1. Linux-C语言函数手册
  2. Linux 下nice 函数用法提高一个进程的友善值
  3. 设置查看linux 造成程序Core dumped 的函数调用层次
  4. 为什么函数在ELF中的其他共享库的长度信息?
  5. Linux内核中的container_of函数简要介绍
  6. Linux环境下注册函数的调用顺序
  7. Linux网络编程-客户端与服务器端通信(Echo)
  8. linux线程函数中代替sleep的方法
  9. 如果后台的SVN服务器IP地址更改了,如何修改客户端的连接url呢?

随机推荐

  1. 《趣说前端 - 000》— 序章
  2. 太扎心了!外包程序员的真实体验
  3. unity GameObject.IsStatic的地雷
  4. Android中 popupWindow 点击外部消失的问
  5. 《趣说前端 - 001》— this 指向谁来定
  6. C的第二天-常量
  7. 最新·前端的工资分布情况 - 你拖后腿了
  8. 学习C的第二天-变量
  9. IDEA 2020年最后一个版本更新了,机器学习
  10. 面试官:高并发下重启服务,接口调用老是超时