2012-10-29 18 views
2

我有一個使用TCP連接進行通信的服務器 - 客戶端程序。多個客戶端可以同時連接到服務器。我想在這個系統上實現tcp打孔。如何控制對等套接字[TCP Hole Punching]

在客戶端,它調用公共服務器來查找我的服務器的公共ip,端口。然後連接到它。

但是在服務器端它必須打開一個端口才能連接到公共服務器,並且它也必須接受該端口上的客戶端連接請求。

我打算做的是打開一個套接字並綁定到端口X,然後連接到公共服務器,然後將此套接字更改爲偵聽狀態以在一段時間內接受傳入連接,然後開始連接到公共服務器再次,一遍又一遍。

這是正確的做法嗎?

編輯:我有另一個想法。它是打開一個新的端口並連接到公共服務器。主服務器端口像往常一樣監聽傳入連接。當客戶想連接時,公共服務器會通過新的端口告訴我的服務器。它會阻止主端口收聽傳入的連接,而是連接到客戶端進行打孔。然後它連接到公共服務器,該服務器將服務器公共IP地址轉發給客戶端,然後像往常一樣繼續監聽傳入連接。然後客戶端將使用該地址連接到已經打開TCP洞的服務器。

回答

0

最好有兩個套接字,並維護服務器和客戶端之間的連接。

  1. m_nServerTCPSocket-用於連接與服務器聽者插座

  2. m_nPeerPrivateTCPSocket-與對方建立連接(公網地址)

  3. m_nPeerPublicTCPSocket-與對方建立連接(私有地址,如果其他同行是在同一個網絡中)

  4. m_nListeningTCPSocket - 用於在這裏偵聽套接字,您需要接受來自對等方的連接。
  5. m_nConnectedPeerTCPSocket->當您與其他對等體連接後,您將獲得此套接字。

    while(end_client)  
    { 
    
        FD_ZERO(&fdRead); 
        FD_ZERO(&fdWrite); 
        FD_ZERO(&fdExcept);  
    
        if (pControlMgr->GetConnectionMgr()->GetListeningTCPSocket()>0) 
    
        { 
    
         FD_SET (pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(),&fdRead);  
         FD_SET (pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(),&fdExcept); 
        } 
    
        if (pControlMgr->GetConnectionMgr()->GetServerTCPSocket()>0) 
        { 
    
         FD_SET (pControlMgr->GetConnectionMgr()->GetServerTCPSocket(),&fdRead); 
         FD_SET (pControlMgr->GetConnectionMgr()->GetServerTCPSocket(),&fdExcept); 
    } 
        if (pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket()>0) 
    { 
        FD_SET (pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(),&fdRead);  
        FD_SET (pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(),&fdExcept);   
    } 
    
    timeval tv; 
    tv.tv_sec = 2; 
    tv.tv_usec = 0; 
    
    nSelectRetVal = select(NULL,&fdRead,NULL,&fdExcept,&tv); 
    
    
    if (nSelectRetVal>0) 
    { 
        int nRecvRetVal = 0; 
        /* TCP Server Socket handling */ 
        if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetServerTCPSocket(), &fdRead)) 
        {   
         try 
         { 
          pRecvBuffer = new char[TCP_RECV_SIZE]; 
          nRecvRetVal = recv(pControlMgr->GetConnectionMgr()->GetServerTCPSocket(), 
           pRecvBuffer,TCP_RECV_SIZE, 
           0); 
          int n = WSAGetLastError(); 
          if (nRecvRetVal>0) 
          { 
           int nPeerNameRetVal = getpeername(pControlMgr->GetConnectionMgr()->GetServerTCPSocket(),(sockaddr*)&addrRemotePeer,&nSockAddrLen); 
           if (pControlMgr->HandlePacket(pRecvBuffer,addrRemotePeer)== -1)      
           { 
            if (NULL != pRecvBuffer) 
            { 
             delete [] pRecvBuffer; 
             pRecvBuffer = NULL; 
             return 0 ; 
            } 
           } 
          }       
         }   
         catch (...) 
         { 
          if (NULL != pRecvBuffer) 
          { 
           delete [] pRecvBuffer; 
           pRecvBuffer = NULL; 
          } 
         } 
    
         if (NULL != pRecvBuffer) 
         { 
          delete [] pRecvBuffer; 
          pRecvBuffer = NULL; 
         }  
        } /* TCP Server Socket handling */  
    
        int n; 
        /* TCP Exception Server Socket handling */ 
        if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetServerTCPSocket(), &fdExcept)) 
        { 
         /*FD_CLR(pControlMgr->GetConnectionMgr().GetServerTCPSocket(),&fdRead); 
         FD_CLR(pControlMgr->GetConnectionMgr().GetServerTCPSocket(),&fdExcept);*/ 
         n = WSAGetLastError(); 
         //return 0; 
        } 
         if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(),&fdRead)) 
        { 
         sockaddr_in addrConnectedPeer; 
         int nAddrLen =sizeof(addrConnectedPeer) ; 
         int nConnectedSock = accept(pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(), 
          (sockaddr*)&addrConnectedPeer, 
          &nAddrLen); 
         int n1 = WSAGetLastError(); 
         if (nConnectedSock>0) 
         { 
          pControlMgr->GetConnectionMgr()->SetConnectedTCPSocket(nConnectedSock); 
          int n = pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(); 
          continue; 
         } 
        } 
        /* TCP Exception Listening Socket handling */ 
        if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(), &fdExcept)) 
        { 
         FD_CLR(pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(),&fdRead); 
         FD_CLR(pControlMgr->GetConnectionMgr()->GetListeningTCPSocket(),&fdExcept); 
         //return 0; 
        } /* TCP Exception Listening Socket handling */ 
    
        /* Connected Peer TCP Read Socket handling */ 
        if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(), &fdRead)) 
        {   
         try 
         { 
          pRecvBuffer = new char[TCP_RECV_SIZE]; 
          nRecvRetVal = recv (pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(), 
           pRecvBuffer,TCP_RECV_SIZE, 
           0); 
          if (nRecvRetVal>0) 
          { 
           int nPeerNameRetVal = getpeername(pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(),(sockaddr*)&addrRemotePeer,&nSockAddrLen); 
           if (pControlMgr->HandlePacket(pRecvBuffer,addrRemotePeer)== -1)      
           { 
            if (NULL != pRecvBuffer) 
            { 
             delete [] pRecvBuffer; 
             pRecvBuffer = NULL; 
             return 0 ; 
            } 
           } 
          }       
         }   
         catch (...) 
         { 
          if (NULL != pRecvBuffer) 
          { 
           delete [] pRecvBuffer; 
           pRecvBuffer = NULL; 
          } 
         } 
         //FD_CLR(pControlMgr->GetConnectionMgr().GetConnectedTCPSocket(),&fdRead); 
         if (NULL != pRecvBuffer) 
         { 
          delete [] pRecvBuffer; 
          pRecvBuffer = NULL; 
         }  
        } /* Peer TCP Read Socket handling */ 
        /* TCP Exception Connected Socket handling */ 
        if (FD_ISSET(pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(), &fdExcept)) 
        { 
         /*FD_CLR(pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(),&fdRead); 
         FD_CLR(pControlMgr->GetConnectionMgr()->GetConnectedTCPSocket(),&fdExcept); 
         return 0;*/ 
         n = WSAGetLastError(); 
        } 
    

邏輯來創建套接字

 int CConnectionMgr::CreateSocket(const int nSockType) 
     { 
     //TODO: Add code here  
      if (InitWinSock() == -1) 
     { 
     return -1;  
     } 
     SetLocalIPAddress(); 

     m_nListeningTCPSocket = socket(AF_INET, SOCK_STREAM ,nSockType);      
     if (GetListeningTCPSocket() <0) 
     return -1; 
     if (BindSocket(GetListeningTCPSocket())<0) 
     return -1; 
     int nListenRet = listen(GetListeningTCPSocket(),SOMAXCONN); 

     if (nListenRet!=0) 
     { 
     return -1; 
     }  
     m_nPeerPrivateTCPSocket = socket(AF_INET, SOCK_STREAM ,nSockType); 
     if (GetPeerPrivateTCPSocket()<0) 
     return -1; 
     if (BindSocket(GetPeerPrivateTCPSocket())<0) 
     return -1; 

     m_nPeerPublicTCPSocket = socket(AF_INET, SOCK_STREAM ,nSockType); 
     if (GetPeerPublicTCPSocket()<0) 
      return -1; 
      if (BindSocket(GetPeerPublicTCPSocket())<0) 
      return -1; 

      m_nServerTCPSocket = socket(AF_INET, SOCK_STREAM ,nSockType); 
      if (GetServerTCPSocket()<0) 
      return -1; 
      if (BindSocket(GetServerTCPSocket())<0) 
      return -1; 
     return 1; 
     } 
+0

很好的例子,謝謝。 – user1782151

+0

無論如何,我有一些問題,當我通過多個ISP連接到它時,公共服務器返回各種端口號。你有過這種問題嗎?我已經發布在這裏; http://superuser.com/questions/498262/hole-punching-over-multiple-isp – user1782151