Chinaunix首页 | 论坛 | 博客
  • 博客访问: 563544
  • 博文数量: 104
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1559
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-21 00:58
个人简介

锻炼精神,首先要锻炼肉体

文章分类

全部博文(104)

文章存档

2018年(1)

2016年(1)

2015年(101)

2014年(1)

我的朋友

分类: C/C++

2015-03-26 22:10:43

accept 函数常用在服务器端接收从客户端发来的请求信息,服务器程序一旦决定接收
来自客户端的请求
(通常情况下,请求可以是客户端请求读取服务器端的一个文件,或是
请求调用服务器上的一个函数,但无论是哪一种都需要服务器进程在本地为其提供一定的缓存空间
如果是文件,就会从服务器端的硬盘中通过系统调用将文件中的内容读取到内存中(缓存);
如果是远程调用,便会调用服务器端的某个进程,进程的运行时需要创建变量和从系统堆栈上面获取运行空间的)
就需要向系统申请相关的缓冲空间,而在 socket 函数的讲解中我们也讨论过了,系统为
进程划分缓冲空间的方式与系统通过文件描述符来为文件划分空间的方式相似,
所以,一旦服务器 accept 客户端的请求成功,便会返回一个新的 套接字描述符,
而这个套接字描述符所指向的服务器本地系统中的缓冲空间,是用于 client 端与 server 端通信
交换数据的缓冲空间。它与服务器启动的时候通过 socket 函数返回的 套接字描述符 所对应的空间
不同,后者是用来接收并缓存来自各个不同 client端的连接信息的缓冲队列的数据缓冲空间。

下面介绍的是 accept 函数的 API
#include
#include
int accept ( int s , struct sockaddr *addr , socklen_t *addrlen ) ;

参数
 1. s : 是服务器端通过调用正确调用socket -> bind -> listen 函数之后的用于指向存放多个客户端缓冲
        队列缓冲区的套接字描述符
 2. addr : 是用来保存发起连接请求的主机的地址与端口的结构体变量,就是存放服务器接收请求
        的客户端的网络地址与端口的结构体变量
3.  addrlen: 用来传入第二个参数类型长度

返回值:
  如果函数执行正确的话,将会返回新的套接字描述符,用于指向与当前通信的客户端交换数据的缓冲区的
  套接字描述符

在这里不得不说的就是,服务器通过 accept 函数接收来自客户端的连接请求的方式,一共有两种,
1. 阻塞方式  这种方式是通过调用 socket 方法创建出来的 套接字描述符默认的通信方式,
   阻塞方式便是,当没有任何客户端向服务器端发送连接请求,同时服务器端的用于存放客户端连接请求
  缓冲队列为空的情况下,服务器端的代码执行一直卡在 accept 语句上不往下执行,直到接到来自客户端
 的连接请求才往下执行。
  
2. 非阻塞方式 
   这种方式通过 fcntl 函数作用于 socket 函数返回的 套接字描述符,并将该套机字描述符传入到 accept 函数
   的首个参数上来设定的。
   非阻塞的方式是指,当服务器端的请求连接缓冲队列为空,同时没有接到任何来自客户端的连接请求的时候,
   代码执行到 accept 函数调用的时候,会立即返回 -1 告知主程序这种情况而不是一直等待不往下进行。

这两种方式是通过调用 fcntl 这个函数来将 阻塞/非阻塞 信息标识到用于指向存放缓冲队列的套接字描述符
来实现 accept 是以阻塞还是非阻塞的方式接收来自各个客户端的连接请求的。

fcntl 函数的使用方式挺有意思,这种函数的使用方式在 unix 编程中很常见
就像是有三个参数,
第一个参数用作的记录用户信号, 第二个参数是告知用户信号发出什么动作,第三个参数是用作告诉用户信号动作的不同类型

就像是第一个参数是一张纸,这张纸将会作为指示工人的命令,
而 fcntl 函数是一个打印机,把白纸送入打印机
第二个参数用于告知打印机{画画}
第三个参数用于告知画画{画山水画,画油画,画素描}

待到将纸从打印机中取出的时候,纸上写着 (画画,画素描)
那么工人们便会{画画,画素描}

同样第一个参数是一张纸,这张纸将会作为指示工人的命令,
而 fcntl 函数是一个打印机,把白纸送入打印机

第二个参数用于告知打印机{打字}
第三个参数用于告知打字{打毛笔字,打钢笔字,打黑体,打宋体}

待到将纸从打印机中取出的时候,纸上写着 (打字,打毛笔字)
那么工人们便会{打字,打毛笔字}

这几种方式都可以通过不同的宏定义来实现,第二个参数就像是指定了第三个参数的变化阈值一样


所以对于 fcntl 函数来说 传入的 套接字描述符 sock_fd 就相当于是白纸,
int fcntl ( int sock_fd , int cmd , long arg ) ;
cmd 参数就相当于是命令 ,arg 参数就相当于是cmd命令所指定的指令集中的一条指令

在这里,要将 accept 的执行方式设定为非阻塞的便可以这样
fcntl ( 传入套接字描述符, F_SETFL , O_NONBLOCK )
待到传入的套接字描述符以上述的方式调用 fcntl 的时候,在被传入到 accept 函数中之后,
便会指示 accept 以 O_NONBLOCK 的方式来执行了。



实例代码1
阻塞通信方式(默认)

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <string.h> // memset

  3. #include <sys/types.h> // AF_INET, SOCK_STREAM
  4. #include <sys/socket.h> // socket , bind ,listen , accept
  5. #include <netinet/in.h> // struct sockaddr_in
  6. #include <unistd.h> // unix standard libarary
  7. #include <fcntl.h> // fcntl



  8. #define SERVER_PORT 3369
  9. #define MAX_CONNECT_QUEUE_LENGTH 10

  10. int getSocketDone ()
  11. {
  12.   int ret = socket ( AF_INET , SOCK_STREAM , 0 ) ;
  13.   
  14.   if ( ret < 0 )
  15.   {
  16.     perror ("getSocketDone failed") ;
  17.   }
  18.  
  19.   return ret ;
  20. }

  21. int getBindDone ( int sock_fd )
  22. {
  23.   struct sockaddr_in serv_addr ;
  24.   int ret ;

  25.   memset ( &serv_addr , 0 , sizeof( struct sockaddr_in )) ;
  26.   serv_addr.sin_family = AF_INET ;
  27.   serv_addr.sin_port = htons (SERVER_PORT) ;
  28.   serv_addr.sin_addr.s_addr = htonl (INADDR_ANY) ;
  29.   
  30.   ret = bind ( sock_fd , (struct sockaddr *) (&serv_addr) ,
  31.         sizeof(struct sockaddr_in) ) ;
  32.   if ( ret < 0 )
  33.   {
  34.     perror ("getBindDone failed") ;
  35.   }
  36.   
  37.  return ret ;
  38. }

  39. int getListenDone ( int sock_fd )
  40. {
  41.   int ret = listen ( sock_fd , MAX_CONNECT_QUEUE_LENGTH ) ;
  42.  
  43.   if ( ret < 0 )
  44.   {
  45.     perror ("getListenDone failed") ;
  46.   }
  47.  
  48.   return ret ;
  49. }

  50. int setSocketFdNonblock( int *sock_fd ) // not used in this program
  51. {
  52.   // sock_fd will be modified in this method , so pointer will is need 

  53.   int ret = fcntl (*sock_fd , F_SETFL ,O_NONBLOCK) ;
  54.   if ( ret < 0 )
  55.   {
  56.     perror ("setSocketFdNonblock failed ") ;
  57.   }
  58.  
  59.  return ret ;
  60. }

  61. int getAcceptDone ( int sock_fd_queue , struct sockaddr_in *client_addr ,
  62.             int *client_len )
  63. {
  64.   int sock_fd_conn ;
  65.  
  66.   *client_len = sizeof (struct sockaddr_in) ;
  67.   
  68.   sock_fd_conn = accept ( sock_fd_queue , (struct sockaddr *)client_addr ,(socklen_t *) client_len ) ;
  69.  
  70.   if ( sock_fd_conn < 0 )
  71.   {
  72.       perror ("getAcceptDone failed") ;
  73.   }
  74.  
  75.  return sock_fd_conn ;
  76. }


  77. int main ( int argc , char ** argv )
  78. {
  79.   int sock_fd_queue , sock_fd_conn ;
  80.   int ret ;
  81.   struct sockaddr_in client_conn ;
  82.   int client_len ;
  83.  
  84.    
  85.   sock_fd_queue = getSocketDone () ;

  86.   if ( sock_fd_queue > 0 )
  87.   
  88.     printf (" getSocketDone success \n");
  89.   
  90.   else
  91.     goto error ;
  92.    
  93.   if ( !getBindDone ( sock_fd_queue ) )
  94.       printf ( "getBindDone success \n") ;
  95.   
  96.   else
  97.      goto error ;

  98.   if ( !getListenDone ( sock_fd_queue ) )
  99.      printf ("getListenDone success \n") ;
  100.   else
  101.     goto error ;
  102.  
  103.  if ( (sock_fd_conn = getAcceptDone ( sock_fd_queue , &client_conn , &client_len) ) > 0 )
  104.  {
  105.      printf ("getAcceptDone success \n ") ;
  106.      printf (" everything is done %d will used as the connect sock_fd \n " , sock_fd_conn ) ;

  107.  }

  108.  else
  109.    goto error ;

  110.   error :
  111.    printf ("one or more methods have failed \n ") ;
  112.    goto leave ;

  113.   leave:
  114.     return 0 ;
  115. }
代码执行的结果为

 getSocketDone success 
getBindDone success 
getListenDone success 

通过程序执行结果可知,代码执行到 accept 的时候进入阻塞状态,因为此时我们并没有创建请求连接的客户端


实例代码2
非阻塞通信方式(通过调用fcntl 函数设定)
两端代码没有太大区别注意红色代码段即可


点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <string.h> // memset

  3. #include <sys/types.h> // AF_INET, SOCK_STREAM
  4. #include <sys/socket.h> // socket , bind ,listen , accept
  5. #include <netinet/in.h> // struct sockaddr_in
  6. #include <unistd.h> // unix standard libarary
  7. #include <fcntl.h> // fcntl



  8. #define SERVER_PORT 3369
  9. #define MAX_CONNECT_QUEUE_LENGTH 10

  10. int getSocketDone ()
  11. {
  12.   int ret = socket ( AF_INET , SOCK_STREAM , 0 ) ;
  13.   
  14.   if ( ret < 0 )
  15.   {
  16.     perror ("getSocketDone failed") ;
  17.   }
  18.  
  19.   return ret ;
  20. }

  21. int getBindDone ( int sock_fd )
  22. {
  23.   struct sockaddr_in serv_addr ;
  24.   int ret ;

  25.   memset ( &serv_addr , 0 , sizeof( struct sockaddr_in )) ;
  26.   serv_addr.sin_family = AF_INET ;
  27.   serv_addr.sin_port = htons (SERVER_PORT) ;
  28.   serv_addr.sin_addr.s_addr = htonl (INADDR_ANY) ;
  29.   
  30.   ret = bind ( sock_fd , (struct sockaddr *) (&serv_addr) ,
  31.         sizeof(struct sockaddr_in) ) ;
  32.   if ( ret < 0 )
  33.   {
  34.     perror ("getBindDone failed") ;
  35.   }
  36.   
  37.  return ret ;
  38. }

  39. int getListenDone ( int sock_fd )
  40. {
  41.   int ret = listen ( sock_fd , MAX_CONNECT_QUEUE_LENGTH ) ;
  42.  
  43.   if ( ret < 0 )
  44.   {
  45.     perror ("getListenDone failed") ;
  46.   }
  47.  
  48.   return ret ;
  49. }

  50. int setSocketFdNonBlock( int *sock_fd )
  51. {
  52.   int new_sock_fd = fcntl (*sock_fd , F_SETFL ,O_NONBLOCK) ;
  53.   if ( new_sock_fd < 0 )
  54.   {
  55.     perror ("setSocketFdNonblock failed ") ;
  56.   }
  57.  
  58.  return new_sock_fd ;
  59. }

  60. int getAcceptDone ( int sock_fd_queue , struct sockaddr_in *client_addr ,
  61.             int *client_len )
  62. {
  63.   int sock_fd_conn ;
  64.  
  65.   *client_len = sizeof (struct sockaddr_in) ;
  66.   
  67.   sock_fd_conn = accept ( sock_fd_queue , (struct sockaddr *)client_addr ,(socklen_t *) client_len ) ;
  68.  
  69.   if ( sock_fd_conn < 0 )
  70.   {
  71.       perror ("getAcceptDone failed") ;
  72.   }
  73.  
  74.  return sock_fd_conn ;
  75. }


  76. int main ( int argc , char ** argv )
  77. {
  78.   int sock_fd_queue , sock_fd_conn ;
  79.   int ret ;
  80.   struct sockaddr_in client_conn ;
  81.   int client_len ;
  82.  
  83.    
  84.   sock_fd_queue = getSocketDone () ;

  85.   if ( sock_fd_queue > 0 )
  86.   
  87.     printf (" getSocketDone success \n");
  88.   
  89.   else
  90.     goto error ;
  91.    
  92.   if ( !getBindDone ( sock_fd_queue ) )
  93.       printf ( "getBindDone success \n") ;
  94.   
  95.   else
  96.      goto error ;

  97.   if ( !getListenDone ( sock_fd_queue ) )
  98.      printf ("getListenDone success \n") ;
  99.   else
  100.     goto error ;

  101.  // now we try to set accept running in non-blocking mode
  102. // by calling setSocketFdNonBlock

  103.   if ( !setSocketFdNonBlock (&sock_fd_queue) )
  104.      printf ( "setSocketFdNonBlock success , working in non blocking mode \n" ) ;
  105.   else
  106.       goto error ;
  107.  
  108.  if ( (sock_fd_conn = getAcceptDone ( sock_fd_queue , &client_conn , &client_len) ) > 0 )
  109.  {
  110.      printf ("getAcceptDone success \n ") ;
  111.      printf (" everything is done %d will used as the connect sock_fd \n " , sock_fd_conn ) ;

  112.  }

  113.  else
  114.  {
  115.    printf ("non accept working in non blocking mode , must return -1 ") ;
  116.    goto error ;
  117.  }

  118.   error :
  119.    printf ("one or more methods have failed \n ") ;
  120.    goto leave ;

  121.   leave:
  122.     return 0 ;
  123. }

程序运行结果为:

 getSocketDone success 
getBindDone success 
getListenDone success 
setSocketFdNonBlock success , working in non blocking mode 
getAcceptDone failed: Resource temporarily unavailable
non accept working in non blocking mode , must return -1 one or more  methods have failed 

阅读(8454) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~