Iperf 源代码分析(四)

Socket 类
    Socket的定义和实现分别在文件Socket.hpp和 Socket.cpp中。它的主要功能是封装了socket文件描述符、此socket对应的端口号,以及socket接口中的listen, accept, connect和close等函数,为用户提供了一个简单易用而又统一的接口。同时作为其他派生类的基类。
    Socket类的定义如下:

  1.  * -------------------------------------------------------------------  
  2.  * A parent class to hold socket information. Has wrappers around  
  3.  * the common listen, accept, connect, and close functions.  
  4.  * ------------------------------------------------------------------- */  
  5.   
  6. #ifndef SOCKET_H  
  7. #define SOCKET_H  
  8.   
  9. #include "headers.h"  
  10. #include "SocketAddr.hpp"  
  11.   
  12. /* ------------------------------------------------------------------- */  
  13. class Socket {  
  14. public:  
  15.     // stores server port and TCP/UDP mode  
  16.     Socket( unsigned short inPort, bool inUDP = false );  
  17.   
  18.     // destructor  
  19.     virtual ~Socket();  
  20.   
  21. protected:  
  22.     // get local address  
  23.     SocketAddr getLocalAddress( void );  
  24.   
  25.     // get remote address  
  26.     SocketAddr getRemoteAddress( void );  
  27.   
  28.     // server bind and listen  
  29.     void Listen( const char *inLocalhost = NULL, bool isIPv6 = false );  
  30.   
  31.     // server accept  
  32.     int Accept( void );  
  33.   
  34.     // client connect  
  35.     void Connect( const char *inHostname, const char *inLocalhost = NULL );  
  36.   
  37.     // close the socket  
  38.     void Close( void );  
  39.   
  40.     // to put setsockopt calls before the listen() and connect() calls  
  41.     virtual void SetSocketOptions( void ) {  
  42.     }  
  43.   
  44.     // join the multicast group  
  45.     void McastJoin( SocketAddr &inAddr );  
  46.   
  47.     // set the multicast ttl  
  48.     void McastSetTTL( int val, SocketAddr &inAddr );  
  49.   
  50.     int   mSock;             // socket file descriptor (sockfd)  
  51.     unsigned short mPort;    // port to listen to  
  52.     bool  mUDP;              // true for UDP, false for TCP  
  53.   
  54. }; // end class Socket  
  55.   
  56. #endif // SOCKET_H  

    Socket类主要提供了四个函数:Listen,Accept,Connect和Close。getLocalAddress和 GetREmoteAddress的作用分别是获得socket本端的地址和对端的地址,两个函数均返回一个SocketAddr实例。 SetSocketOptions的作用是设置socket的属性,它是一个虚函数,因此不同的socket的派生类在实现此函数时会执行不同的操作。下面重点看一下Socket类的几个函数的实现。

Listen 函数

  1. /* ------------------------------------------------------------------- 
  2.  * Setup a socket listening on a port. 
  3.  * For TCP, this calls bind() and listen(). 
  4.  * For UDP, this just calls bind(). 
  5.  * If inLocalhost is not null, bind to that address rather than the 
  6.  * wildcard server address, specifying what incoming interface to 
  7.  * accept connections on. 
  8.  * ------------------------------------------------------------------- */  
  9.   
  10. void Socket::Listen( const char *inLocalhost, bool isIPv6 ) {  
  11.     int rc;  
  12.   
  13.     SocketAddr serverAddr( inLocalhost, mPort, isIPv6 );  
  14.   
  15.     // create an internet TCP socket  
  16.     int type = (mUDP  ?  SOCK_DGRAM  :  SOCK_STREAM);  
  17.     int domain = (serverAddr.isIPv6() ?  
  18. #ifdef IPV6  
  19.                   AF_INET6  
  20. #else  
  21.                   AF_INET  
  22. #endif  
  23.                   : AF_INET);  
  24.   
  25.     mSock = socket( domain, type, 0 );  
  26.     FAIL_errno( mSock == INVALID_SOCKET, "socket" );  
  27.   
  28.     SetSocketOptions();  
  29.   
  30.     // reuse the address, so we can run if a former server was killed off  
  31.     int boolean = 1;  
  32.     Socklen_t len = sizeof(boolean);  
  33.     // this (char*) cast is for old headers that don't use (void*)  
  34.     setsockopt( mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );  
  35.   
  36.     // bind socket to server address  
  37.         rc = bind( mSock, serverAddr.get_sockaddr(), serverAddr.get_sizeof_sockaddr());  
  38.         FAIL_errno( rc == SOCKET_ERROR, "bind" );  
  39.     // listen for connections (TCP only).  
  40.     // default backlog traditionally 5  
  41.     if ( ! mUDP ) {  
  42.         rc = listen( mSock, 5 );  
  43.         FAIL_errno( rc == SOCKET_ERROR, "listen" );  
  44.     }  
  45. // end Listen  

    首先构造一个包含本地服务器地址结构的SocketAddr实例,inLocalhost是本地IP地址(点分十进制字符串或URL,后者在创建 SocketAddr实例是完成地址解析),mPort是Socket构造函数中设置的端口。再通过socket系统调用创建一个socket。 SetSocketOptions方法设置此socket的属性。因为SetSocketOptions是虚函数,在Socket类的实现中是一个空函数,而不同的Socket的派生类在覆盖(overwrite)该函数执行的操作是不同的,这是多态特性的应用。此后设置socket的可重用(reuse)属性,使服务器在重启后可以重用以前的地址和端口。此时该socket还没有绑定到某个网络端点(IP地址、端口对)上,bind系统调用完成此功能。最后,如果该socket用于一个TCP连接,则调用listen函数,一来向系统说明可以接受到socket绑定端口上的连接请求,二来设定请求等待队列的长度为5。
Socket的Listen方法将地址解析(地址结构生成)、socket、bind和listen等系统调用组合为一个函数。在应用时,调用一个Listen方法就可以完成Server端socket初始化的所有工作。

Accept函数

    Accept函数是Server完成socket初始化,等待连接请求时调用的函数。代码如下:

  1. /* ------------------------------------------------------------------- 
  2.  * After Listen() has setup mSock, this will block 
  3.  * until a new connection arrives. Handles interupted accepts. 
  4.  * Returns the newly connected socket. 
  5.  * ------------------------------------------------------------------- */  
  6.   
  7. int Socket::Accept( void ) {  
  8.     iperf_sockaddr clientAddr;  
  9.     Socklen_t addrLen;  
  10.     int connectedSock;  
  11.   
  12.     while ( true ) {  
  13.         // accept a connection  
  14.         addrLen = sizeof( clientAddr );  
  15.         connectedSock = accept( mSock, (struct sockaddr*) &clientAddr, &addrLen );  
  16.   
  17.         // handle accept being interupted  
  18.         if ( connectedSock == INVALID_SOCKET  &&  errno == EINTR ) {  
  19.             continue;  
  20.         }  
  21.   
  22.         return connectedSock;  
  23.     }  
  24.   
  25. // end Accept  

    Accept函数为accept系统调用增添了在中断后自动重启的功能。Server线程在执行accept函数是后被阻塞,直到有请求到达,或是接收到某个信号。若是后面一种情况,accept会返回INVALID_SOCKET并置errno为EINTR。Accept方法检查这种情况,并重新调用accept函数。

Connect函数

Connect函数Client端调用的函数,其作用是连接指定的Server。代码如下:

  1. /* ------------------------------------------------------------------- 
  2.  * Setup a socket connected to a server. 
  3.  * If inLocalhost is not null, bind to that address, specifying 
  4.  * which outgoing interface to use. 
  5.  * ------------------------------------------------------------------- */  
  6.   
  7. void Socket::Connect( const char *inHostname, const char *inLocalhost ) {  
  8.     int rc;  
  9.     SocketAddr serverAddr( inHostname, mPort );  
  10.   
  11.     assert( inHostname != NULL );  
  12.   
  13.     // create an internet socket  
  14.     int type = (mUDP  ?  SOCK_DGRAM : SOCK_STREAM);  
  15.   
  16.     int domain = (serverAddr.isIPv6() ?  
  17. #ifdef IPV6  
  18.                   AF_INET6  
  19. #else  
  20.                   AF_INET  
  21. #endif  
  22.                   : AF_INET);  
  23.   
  24.     mSock = socket( domain, type, 0 );  
  25.     FAIL_errno( mSock == INVALID_SOCKET, "socket" );  
  26.   
  27.     SetSocketOptions();  
  28.   
  29.   
  30.     if ( inLocalhost != NULL ) {  
  31.         SocketAddr localAddr( inLocalhost );  
  32.         // bind socket to local address  
  33.         rc = bind( mSock, localAddr.get_sockaddr(), localAddr.get_sizeof_sockaddr());  
  34.         FAIL_errno( rc == SOCKET_ERROR, "bind" );  
  35.     }  
  36.   
  37.     // connect socket  
  38.     rc = connect( mSock, serverAddr.get_sockaddr(), serverAddr.get_sizeof_sockaddr());  
  39.     FAIL_errno( rc == SOCKET_ERROR, "connect" );  
  40.   
  41. // end Connect  

    首先构造一个SocketAddr实例保存Server端的地址(IP地址,端口对),同时按需完成地址解析。socket系统调用生成 socket接口。虚函数SetSocketOptions利用多态特性使不同的派生类按需要设置socket属性。如果传入的inLocalhost参数不是空指针,说明调用者希望指定某个本地接口作为连接的本地端点,此时通过bind系统调用把该socket绑定到这个接口对应的IP地址上。最后调用 connect函数完成与远端Server的连接。

讨论: TCP和UDP在调用connect函数是的操作有何不同?
    对于TCP连接,调用 connect函数会发起建立TCP连接的三次握手(3-way handshaking)过程。当connect调用返回时,此过程已经完成,连接已经建立。因为TCP连接使用字符流模型,因此在建立好的连接上交换数据时,就好像从一个字符流中读取,向另一个字符流中写入一样。
    而UDP是无连接的协议,使用数据报而不是连接的模型,因此调用connect函数并不发起连接的过程,也没有任何数据向Server发送,而只是通知操作系统,发往该地址和端口的数据报都送到这个socket连接上来,也就是说,把这个(地址、端口)对和该socket关联起来。UDP在IP协议的基础上提供了多路访问(multiplex)的服务,UDP的connect系统调用对这种多路提供了socket接口与对端地址间的对应关系。在UDP连接中,connect提功的这种功能是很有用的。例如,Server可以在接收到一个 Client的数据报后,分配一个线程执行connect函数与该Client绑定,处理与该client的后继交互,其他的线程继续在原来的UDP端口上监听新的请求.因为在Client端和Server端都执行了connect函数,所以一个Server与多个Client间的连接不会发生混乱。在 Iperf对UDP的处理中,就使用了这种技巧。

    以上已经简要讨论了Iperf提供的库中几个比较重要的类的定义与实现。接下来将开始研究Iperf本身的实现细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Iperf 是一个用于测量网络带宽的开源工具,其源代码可在 GitHub 上找到。下面是 Iperf 源代码分析概述: 1. 主要文件和目录结构: - `src/` 目录下包含了 Iperf 的主要源代码文件。 - `include/` 目录下包含了 Iperf 的头文件。 - `configure.ac` 是用于生成 configure 脚本的 Autoconf 文件。 - `Makefile.am` 包含了构建 Iperf 的规则和编译选项。 2. 核心功能: - `iperf.c` 是 Iperf 的主要入口点,包含了程序的主要逻辑和命令行参数解析。 - `Settings.cpp` 定义了与测试相关的设置,如传输协议、端口号、测试时间等。 - `Thread.c` 实现了多线程支持,用于同时处理多个连接。 - `tcp_window_size.c` 提供了 TCP 窗口大小相关的功能。 - `Reporter.c` 负责汇报测试结果,计算带宽和延迟等指标。 3. 协议支持: - Iperf 支持 TCP 和 UDP 两种传输协议,分别由 `TCP.c` 和 `UDP.c` 实现。 - `Socket.c` 封装了底层网络套接字相关的操作,包括创建、绑定和连接等。 4. 平台兼容性: - Iperf 在不同平台上都有相应的适配代码,如 `SocketAddr.c` 用于处理不同平台的网络地址。 - 部分平台相关的代码位于 `src/posix/` 和 `src/windows/` 目录下。 5. 其他功能: - `util.c` 提供了一些辅助函数,如时间戳生成和计算带宽单位转换等。 - `Locale.c` 实现了本地化支持。 - `Timestamps.c` 提供了时间戳相关的功能。 这只是 Iperf 源代码的简要概述,实际分析过程可能需要更深入的了解。如果你对特定部分或功能有更具体的问题,我可以提供更详细的解答。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值