求助  一段完整的  反弹端口程序代码...小子想直观学习一下反弹端口...还请我的天使们多多帮忙.

解决方案 »

  1.   

    就是反向连接了.服务器端用connect就可以了.
    还有一个端口复用技术,是用setsockopt函数.
    网上完整的源代码是很难找的.
      

  2.   

    也发给我一份
    [email protected]
    谢谢!
      

  3.   

    下面是一个模拟P2P聊天的过程的源代码,过程很简单,P2PServer运行在一个拥有公网IP的计算机上,P2PClient运行在两个不同的NAT后(注意,如果两个客户端运行在一个NAT后,本程序很可能不能运行正常,这取决于你的NAT是否支持loopback translation,详见http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt,当然,此问题可以通过双方先尝试连接对方的内网IP来解决,但是这个代码只是为了验证原理,并没有处理这些问题),后登录的计算机可以获得先登录计算机的用户名,后登录的计算机通过send username message的格式来发送消息。如果发送成功,说明你已取得了直接与对方连接的成功。
        程序现在支持三个命令:send , getu , exit
        
        send格式:send username message
        功能:发送信息给username
        
        getu格式:getu
        功能:获得当前服务器用户列表
        
        exit格式:exit
        功能:注销与服务器的连接(服务器不会自动监测客户是否吊线)
            
        代码很短,相信很容易懂,如果有什么问题,可以给我发邮件[email protected]  或者在CSDN上发送短消息。同时,欢迎转发此文,但希望保留作者版权8-)。
        
        最后感谢CSDN网友 PiggyXP 和 Seilfer的测试帮助P2PServer.c/* P2P 程序服务端

    * 文件名:P2PServer.c
    *
    * 日期:2004-5-21
    *
    * 作者:shootingstars([email protected])
    *
    */
    #pragma comment(lib, "ws2_32.lib")#include "windows.h"
    #include "..\proto.h"
    #include "..\Exception.h"UserList ClientList;void InitWinSock()
    {
    WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
      printf("Windows sockets 2.2 startup");
      throw Exception("");
    }
    else{
      printf("Using %s (Status: %s)\n",
       wsaData.szDescription, wsaData.szSystemStatus);
      printf("with API versions %d.%d to %d.%d\n\n",
       LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),
       LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));
      
    }
    }SOCKET mksock(int type)
    {
    SOCKET sock = socket(AF_INET, type, 0);
    if (sock < 0)
    {
            printf("create socket error");
      throw Exception("");
    }
    return sock;
    }stUserListNode GetUser(char *username)
    {
    for(UserList::iterator UserIterator=ClientList.begin();
          UserIterator!=ClientList.end();
           ++UserIterator)
    {
      if( strcmp( ((*UserIterator)->userName), username) == 0 )
       return *(*UserIterator);
    }
    throw Exception("not find this user");
    }int main(int argc, char* argv[])
    {
    try{
      InitWinSock();
      
      SOCKET PrimaryUDP;
      PrimaryUDP = mksock(SOCK_DGRAM);  sockaddr_in local;
      local.sin_family=AF_INET;
      local.sin_port= htons(SERVER_PORT); 
      local.sin_addr.s_addr = htonl(INADDR_ANY);
      int nResult=bind(PrimaryUDP,(sockaddr*)&local,sizeof(sockaddr));
      if(nResult==SOCKET_ERROR)
       throw Exception("bind error");  sockaddr_in sender;
      stMessage recvbuf;
      memset(&recvbuf,0,sizeof(stMessage));  // 开始主循环.
      // 主循环负责下面几件事情:
      // 一:读取客户端登陆和登出消息,记录客户列表
      // 二:转发客户p2p请求
      for(;;)
      {
       int dwSender = sizeof(sender);
       int ret = recvfrom(PrimaryUDP, (char *)&recvbuf, sizeof(stMessage), 0, (sockaddr *)&sender, &dwSender);
       if(ret <= 0)
       {
        printf("recv error");
        continue;
       }
       else
       {
        int messageType = recvbuf.iMessageType;
        switch(messageType){
        case LOGIN:
         {
          //  将这个用户的信息记录到用户列表中
          printf("has a user login : %s\n", recvbuf.message.loginmember.userName);
          stUserListNode *currentuser = new stUserListNode();
          strcpy(currentuser->userName, recvbuf.message.loginmember.userName);
          currentuser->ip = ntohl(sender.sin_addr.S_un.S_addr);
          currentuser->port = ntohs(sender.sin_port);
          
          ClientList.push_back(currentuser);      // 发送已经登陆的客户信息
          int nodecount = (int)ClientList.size();
          sendto(PrimaryUDP, (const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));
          for(UserList::iterator UserIterator=ClientList.begin();
            UserIterator!=ClientList.end();
            ++UserIterator)
          {
           sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); 
          }      break;
         }
        case LOGOUT:
         {
          // 将此客户信息删除
          printf("has a user logout : %s\n", recvbuf.message.logoutmember.userName);
          UserList::iterator removeiterator = NULL;
          for(UserList::iterator UserIterator=ClientList.begin();
           UserIterator!=ClientList.end();
           ++UserIterator)
          {
           if( strcmp( ((*UserIterator)->userName), recvbuf.message.logoutmember.userName) == 0 )
           {
            removeiterator = UserIterator;
            break;
           }
          }
          if(removeiterator != NULL)
           ClientList.remove(*removeiterator);
          break;
         }
        case P2PTRANS:
         {
          // 某个客户希望服务端向另外一个客户发送一个打洞消息
          printf("%s wants to p2p %s\n",inet_ntoa(sender.sin_addr),recvbuf.message.translatemessage.userName);
          stUserListNode node = GetUser(recvbuf.message.translatemessage.userName);
          sockaddr_in remote;
          remote.sin_family=AF_INET;
          remote.sin_port= htons(node.port); 
          remote.sin_addr.s_addr = htonl(node.ip);      in_addr tmp;
          tmp.S_un.S_addr = htonl(node.ip);
          printf("the address is %s,and port is %d\n",inet_ntoa(tmp), node.port);      stP2PMessage transMessage;
          transMessage.iMessageType = P2PSOMEONEWANTTOCALLYOU;
          transMessage.iStringLen = ntohl(sender.sin_addr.S_un.S_addr);
          transMessage.Port = ntohs(sender.sin_port);
                            
          sendto(PrimaryUDP,(const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr *)&remote, sizeof(remote));      break;
         }
        
        case GETALLUSER:
         {
          int command = GETALLUSER;
          sendto(PrimaryUDP, (const char*)&command, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));      int nodecount = (int)ClientList.size();
          sendto(PrimaryUDP, (const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));      for(UserList::iterator UserIterator=ClientList.begin();
            UserIterator!=ClientList.end();
            ++UserIterator)
          {
           sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); 
          }
          break;
         }
        }
       }
      }}
    catch(Exception &e)
    {
      printf(e.GetMessage());
      return 1;
    }return 0;
    }
      

  4.   

    void BindSock(SOCKET sock)
    {
    sockaddr_in sin;
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    sin.sin_family = AF_INET;
    sin.sin_port = 0;if (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)
      throw Exception("bind error");
    }void ConnectToServer(SOCKET sock,char *username, char *serverip)
    {
    sockaddr_in remote;
    remote.sin_addr.S_un.S_addr = inet_addr(serverip);
    remote.sin_family = AF_INET;
    remote.sin_port = htons(SERVER_PORT);stMessage sendbuf;
    sendbuf.iMessageType = LOGIN;
    strncpy(sendbuf.message.loginmember.userName, username, 10);sendto(sock, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&remote,sizeof(remote));int usercount;
    int fromlen = sizeof(remote);
    int iread = recvfrom(sock, (char *)&usercount, sizeof(int), 0, (sockaddr *)&remote, &fromlen);
    if(iread<=0)
    {
      throw Exception("Login error\n");
    }// 登录到服务端后,接收服务端发来的已经登录的用户的信息
    cout<<"Have "<<usercount<<" users logined server:"<<endl;
    for(int i = 0;i<usercount;i++)
    {
      stUserListNode *node = new stUserListNode;
      recvfrom(sock, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&remote, &fromlen);
      ClientList.push_back(node);
      cout<<"Username:"<<node->userName<<endl;
      in_addr tmp;
      tmp.S_un.S_addr = htonl(node->ip);
      cout<<"UserIP:"<<inet_ntoa(tmp)<<endl;
      cout<<"UserPort:"<<node->port<<endl;
      cout<<""<<endl;
    }
    }void OutputUsage()
    {
    cout<<"You can input you command:\n"
      <<"Command Type:\"send\",\"exit\",\"getu\"\n"
      <<"Example : send Username Message\n"
      <<"          exit\n"
      <<"          getu\n"
      <<endl;
    }/* 这是主要的函数:发送一个消息给某个用户(C)
    *流程:直接向某个用户的外网IP发送消息,如果此前没有联系过
    *      那么此消息将无法发送,发送端等待超时。
    *      超时后,发送端将发送一个请求信息到服务端,
    *      要求服务端发送给客户C一个请求,请求C给本机发送打洞消息
    *      以上流程将重复MAXRETRY次
    */
    bool SendMessageTo(char *UserName, char *Message)
    {
    char realmessage[256];
    unsigned int UserIP;
    unsigned short UserPort;
    bool FindUser = false;
    for(UserList::iterator UserIterator=ClientList.begin();
          UserIterator!=ClientList.end();
          ++UserIterator)
    {
      if( strcmp( ((*UserIterator)->userName), UserName) == 0 )
      {
       UserIP = (*UserIterator)->ip;
       UserPort = (*UserIterator)->port;
       FindUser = true;
      }
    }if(!FindUser)
      return false;strcpy(realmessage, Message);
    for(int i=0;i<MAXRETRY;i++)
    {
      RecvedACK = false;  sockaddr_in remote;
      remote.sin_addr.S_un.S_addr = htonl(UserIP);
      remote.sin_family = AF_INET;
      remote.sin_port = htons(UserPort);
      stP2PMessage MessageHead;
      MessageHead.iMessageType = P2PMESSAGE;
      MessageHead.iStringLen = (int)strlen(realmessage)+1;
      int isend = sendto(PrimaryUDP, (const char *)&MessageHead, sizeof(MessageHead), 0, (const sockaddr*)&remote, sizeof(remote));
      isend = sendto(PrimaryUDP, (const char *)&realmessage, MessageHead.iStringLen, 0, (const sockaddr*)&remote, sizeof(remote));
      
      // 等待接收线程将此标记修改
      for(int j=0;j<10;j++)
      {
       if(RecvedACK)
        return true;
       else
        Sleep(300);
      }  // 没有接收到目标主机的回应,认为目标主机的端口映射没有
      // 打开,那么发送请求信息给服务器,要服务器告诉目标主机
      // 打开映射端口(UDP打洞)
      sockaddr_in server;
      server.sin_addr.S_un.S_addr = inet_addr(ServerIP);
      server.sin_family = AF_INET;
      server.sin_port = htons(SERVER_PORT);  stMessage transMessage;
      transMessage.iMessageType = P2PTRANS;
      strcpy(transMessage.message.translatemessage.userName, UserName);  sendto(PrimaryUDP, (const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr*)&server, sizeof(server));
      Sleep(100);// 等待对方先发送信息。
    }
    return false;
    }
    // 解析命令,暂时只有exit和send命令
    // 新增getu命令,获取当前服务器的所有用户
    void ParseCommand(char * CommandLine)
    {
    if(strlen(CommandLine)<4)
      return;
    char Command[10];
    strncpy(Command, CommandLine, 4);
    Command[4]='\0';if(strcmp(Command,"exit")==0)
    {
      stMessage sendbuf;
      sendbuf.iMessageType = LOGOUT;
      strncpy(sendbuf.message.logoutmember.userName, UserName, 10);
      sockaddr_in server;
      server.sin_addr.S_un.S_addr = inet_addr(ServerIP);
      server.sin_family = AF_INET;
      server.sin_port = htons(SERVER_PORT);  sendto(PrimaryUDP,(const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr *)&server, sizeof(server));
      shutdown(PrimaryUDP, 2);
      closesocket(PrimaryUDP);
      exit(0);
    }
    else if(strcmp(Command,"send")==0)
    {
      char sendname[20];
      char message[COMMANDMAXC];
      int i;
      for(i=5;;i++)
      {
       if(CommandLine[i]!=' ')
        sendname[i-5]=CommandLine[i];
       else
       {
        sendname[i-5]='\0';
        break;
       }
      }
      strcpy(message, &(CommandLine[i+1]));
      if(SendMessageTo(sendname, message))
       printf("Send OK!\n");
      else 
       printf("Send Failure!\n");
    }
    else if(strcmp(Command,"getu")==0)
    {
      int command = GETALLUSER;
      sockaddr_in server;
      server.sin_addr.S_un.S_addr = inet_addr(ServerIP);
      server.sin_family = AF_INET;
      server.sin_port = htons(SERVER_PORT);  sendto(PrimaryUDP,(const char*)&command, sizeof(command), 0, (const sockaddr *)&server, sizeof(server));
    }
    }// 接受消息线程
    DWORD WINAPI RecvThreadProc(LPVOID lpParameter)
    {
    sockaddr_in remote;
    int sinlen = sizeof(remote);
    stP2PMessage recvbuf;
    for(;;)
    {
      int iread = recvfrom(PrimaryUDP, (char *)&recvbuf, sizeof(recvbuf), 0, (sockaddr *)&remote, &sinlen);
      if(iread<=0)
      {
       printf("recv error\n");
       continue;
      }
      switch(recvbuf.iMessageType)
      {
      case P2PMESSAGE:
       {
        // 接收到P2P的消息
        char *comemessage= new char[recvbuf.iStringLen];
        int iread1 = recvfrom(PrimaryUDP, comemessage, 256, 0, (sockaddr *)&remote, &sinlen);
        comemessage[iread1-1] = '\0';
        if(iread1<=0)
         throw Exception("Recv Message Error\n");
        else
        {
         printf("Recv a Message:%s\n",comemessage);
         
         stP2PMessage sendbuf;
         sendbuf.iMessageType = P2PMESSAGEACK;
         sendto(PrimaryUDP, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&remote, sizeof(remote));
        }    delete []comemessage;
        break;   }
      case P2PSOMEONEWANTTOCALLYOU:
       {
        // 接收到打洞命令,向指定的IP地址打洞
        printf("Recv p2someonewanttocallyou data\n");
        sockaddr_in remote;
        remote.sin_addr.S_un.S_addr = htonl(recvbuf.iStringLen);
        remote.sin_family = AF_INET;
        remote.sin_port = htons(recvbuf.Port);    // UDP hole punching
        stP2PMessage message;
        message.iMessageType = P2PTRASH;
        sendto(PrimaryUDP, (const char *)&message, sizeof(message), 0, (const sockaddr*)&remote, sizeof(remote));
                    
        break;
       }
      case P2PMESSAGEACK:
       {
        // 发送消息的应答
        RecvedACK = true;
        break;
       }
      case P2PTRASH:
       {
        // 对方发送的打洞消息,忽略掉。
        //do nothing ...
        printf("Recv p2ptrash data\n");
        break;
       }
      case GETALLUSER:
       {
        int usercount;
        int fromlen = sizeof(remote);
        int iread = recvfrom(PrimaryUDP, (char *)&usercount, sizeof(int), 0, (sockaddr *)&remote, &fromlen);
        if(iread<=0)
        {
         throw Exception("Login error\n");
        }
        
        ClientList.clear();    cout<<"Have "<<usercount<<" users logined server:"<<endl;
        for(int i = 0;i<usercount;i++)
        {
         stUserListNode *node = new stUserListNode;
         recvfrom(PrimaryUDP, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&remote, &fromlen);
         ClientList.push_back(node);
         cout<<"Username:"<<node->userName<<endl;
         in_addr tmp;
         tmp.S_un.S_addr = htonl(node->ip);
         cout<<"UserIP:"<<inet_ntoa(tmp)<<endl;
         cout<<"UserPort:"<<node->port<<endl;
         cout<<""<<endl;
        }
        break;
       }
      }
    }
    }