这里的“通讯”加上了引号,是因为实际上所有的socket都有通讯的功能,只是在我们的例子中,之前那个socket只负责listen,而这个socket负责接受信息并echo回去。

       我们现看看这个函数:

C++代码
  1. bool TcpServer::isAccept()  
  2. {  
  3.     unsigned int clntAddrLen = sizeof(clntAddr);  
  4.   
  5.     if ( (communicationSock = accept(listenSock, (sockaddr*)&clntAddr, &clntAddrLen)) < 0 ) {  
  6.         return false;  
  7.     } else {  
  8.         std::cout << "Client(IP: " << inet_ntoa(clntAddr.sin_addr) << ") connected.\n";  
  9.         return true;  
  10.     }  
  11. }  

       用accept()创建新的socket

       在我们的例子中,communicationSock实际上是用函数accept()创建的。

C++代码
  1. int accept(int socket, struct sockaddr* clientAddress, unsigned int* addressLength);  

       在Linux中的实现为:

C++代码
  1. /* Await a connection on socket FD. 
  2.    When a connection arrives, open a new socket to communicate with it, 
  3.    set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting 
  4.    peer and *ADDR_LEN to the address's actual length, and return the 
  5.    new socket's descriptor, or -1 for errors. 
  6.  
  7.    This function is a cancellation point and therefore not marked with 
  8.    __THROW.  */  
  9. extern int accept (int __fd, __SOCKADDR_ARG __addr,  
  10.            socklen_t *__restrict __addr_len);  

       这个函数实际上起着构造socket作用的仅仅只有第一个参数(另外还有一个不在这个函数内表现出来的因素,后面会讨论到),后面两个指针都有副作用,在socket创建后,会将客户端sockaddr的数据以及结构体的大小传回。

       当程序调用accept()的时候,程序有可能就停下来等accept()的结果。这就是我们前一小节说到的block(阻塞)。这如同我们调用std::cin的时候系统会等待输入直到回车一样。accept()是一个有可能引起block的函数。请注意我说的是“有可能”,这是因为accept()的block与否实际上决定与第一个参数socket的属性。这个文件描述符如果是block的,accept()就block,否则就不block。默认情况下,socket的属性是“可读可写”,并且,是阻塞的。所以,我们不修改socket属性的时候,accept()是阻塞的。

       accept()的另一面connect()

       accept()只是在server端被动的等待,它所响应的,是client端connect()函数:

C++代码
  1. int connect(int socket, struct sockaddr* foreignAddress, unsigned int addressLength);  

       虽然我们这里不打算详细说明这个client端的函数,但是我们可以看出来,这个函数与之前我们介绍的bind()有几分相似,特别在Linux的实现中:

C++代码
  1. /* Open a connection on socket FD to peer at ADDR (which LEN bytes long). 
  2.    For connectionless socket types, just set the default address to send to 
  3.    and the only address from which to accept transmissions. 
  4.    Return 0 on success, -1 for errors. 
  5.  
  6.    This function is a cancellation point and therefore not marked with 
  7.    __THROW.  */  
  8. extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);  

       connect() 也使用了const的sockaddr,只不过是远程电脑上的而非bind()的本机。

       accept()在server端表面上是通过listen socket创建了新的socket,实际上,这种行为是在接受对方客户机程序中connect()函数的请求后发生的。综合起看,被创建的新socket实际上包含了listen socket的信息以及客户端connect()请求中所包含的信息——客户端的sockaddr地址。

       新socket与sockaddr的关系

       accept()创建的新socket(我们例子中的communicationSock,这里我们简单用newSock来带指)首先包含了listen socket的信息,所以,newSock具有本机sockaddr的信息;其次,因为它响应于client端connect()函数的请求,所以,它还包含了clinet端sockaddr的信息。

       我们说过,stream流形式的TCP协议实际上是建立起一个“可来可去”的通道。用于listen的通道,远程机的目标地址是不确定的;但是newSock却是有指定的本机地址和远程机地址,所以,这个socket,才是我们真正用于TCP“通讯”的socket。

       inet_ntoa()

C++代码
  1. #include <arpa/inet.h>  
  2.   
  3. /* Convert Internet number in IN to ASCII representation.  The return value 
  4.    is a pointer to an internal array containing the string.  */  
  5. extern char *inet_ntoa (struct in_addr __in) __THROW;  

       对于这个函数,我们可以作为一种,将IP地址,由in_addr结构转换为可读的ASCII形式的固定用法。

除非特别注明,鸡啄米文章均为原创
转载请标明本文地址:http://www.jizhuomi.com/software/410.html
2015年7月16日
作者:鸡啄米 分类:软件开发 浏览: 评论:1