在另外一边的客户端,我们分析一下TCPClientSock的建立过程。

C++代码
  1. class TCPClientSock: public BaseSock{  
  2. private:  
  3.     sockaddr_in serverSockAddr;  
  4. protected:  
  5.     char* preBuffer;  
  6.     int preBufferSize;  
  7.     mutable int preReceivedLength;  
  8. public:  
  9.     TCPClientSock(  
  10.         const char* server_IP,  
  11.         unsigned short server_port,  
  12.         int pre_buffer_size = 32);  
  13.     virtual ~TCPClientSock();  
  14.     int TCPReceive() const;  
  15.     int TCPSend(const char* send_data,  
  16.             const int& data_length) const;  
  17. };  

       我们看到TCPClientSock的类与TCPServerSock很类似,构造函数的差别是,TCPClientSock需要提供server端的IP地址和端口号。

C++代码
  1. TCPClientSock::TCPClientSock(  
  2.                     const char *server_IP,  
  3.                     unsigned short server_port,  
  4.                     int pre_buffer_size):  
  5. preBufferSize(pre_buffer_size),  
  6. preReceivedLength(0)  
  7. {  
  8.     preBuffer = new char[preBufferSize];  
  9.   
  10.     sockFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  
  11.     if (sockFD < 0) {  
  12.         sockClass::error_info("sock() failed.");  
  13.     }  
  14.   
  15.     memset(&serverSockAddr, 0, sizeof(serverSockAddr));  
  16.     serverSockAddr.sin_family = AF_INET;  
  17.     serverSockAddr.sin_addr.s_addr = inet_addr(server_IP);  
  18.     serverSockAddr.sin_port = htons(server_port);  
  19.   
  20.     if (connect(sockFD,  
  21.                 (struct sockaddr*)&serverSockAddr,  
  22.                 sizeof(serverSockAddr)) < 0 ) {  
  23.         sockClass::error_info("connect() failed.");  
  24.     }  
  25. }  
  26.   
  27. TCPClientSock::~TCPClientSock()  
  28. {  
  29.     delete [] preBuffer;  
  30.     close(sockFD);  
  31. }  

       TCPClientSock通过socket()建立起sockFD,然后指定服务器的serverSockAddr,然后通过connect()向serverSockAddr指定的服务器发出握手请求。需要说明的是,调用connect()的时候,系统会检查TCPClientSock的sockFD是否已经绑定了本机的SockAddr,事实上我们也可以通过bind()将本机的IP和指定的端口号绑定在这个sockFD上,但是我们并不关心这个IP地址和端口号(况且很多主机并没有公网IP,特别在中国),所以通常我们不自己去绑定,这样系统就会帮我们完成绑定工作,分配一个空闲的端口号作为本机地址的端口号。

       这样TCPClientSock具有来向(本机地址,通常由系统自动完成绑定,也可以指定)和去向(指定的server端地址)的地址信息,所以可以收发信息。于是,TCPClientSock发出的第一个数据报是发给server监听socket的握手请求数据报,TCPListenSock接收这个数据报后,将相关信息传递给TCPServerSock建立新的sockFD,我们上一节讲到,这个新的sockFD建立起来之后马上就向client端返回一个数据报:一方面表示接受第一次握手请求,另外一方面发出第二次握手请求。

       收到第二次握手请求后,connect()才会返回,不然就会阻塞,非常“尽力”的去连接server。这个“尽力”的程度跟系统有关,在我的试验中,windows下很快,就几秒;而Debian则接近6分钟!

       connect()返回的同时,向server发出了第三次握手的信息,这个信息是对第二次握手请求的认可。所以,第一次和第二次握手包含着连接的请求;而第二次和第三次握手则包含着对握手请求的认可,他们都是在告诉对方:我知道并同意你连接上我了。

       至此,TCP三次握手的概念在socket中完整的实现,建立起数据流的TCP通信通道。

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