⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 p2p之udp穿透nat的原理与实现.txt

📁 P2P之UDP穿透NAT的原理与实现! p2p与网络的应用方面
💻 TXT
📖 第 1 页 / 共 2 页
字号:
  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;
   }
  }
 }
}


int main(int argc, char* argv[])
{
 try
 {
  InitWinSock();
  
  PrimaryUDP = mksock(SOCK_DGRAM);
  BindSock(PrimaryUDP);

  cout<<"Please input server ip:";
  cin>>ServerIP;

  cout<<"Please input your name:";
  cin>>UserName;

  ConnectToServer(PrimaryUDP, UserName, ServerIP);

  HANDLE threadhandle = CreateThread(NULL, 0, RecvThreadProc, NULL, NULL, NULL);
  CloseHandle(threadhandle);
  OutputUsage();

  for(;;)
  {
   char Command[COMMANDMAXC];
   gets(Command);
   ParseCommand(Command);
  }
 }
 catch(Exception &e)
 {
  printf(e.GetMessage());
  return 1;
 }
 return 0;
}


/* 异常类
 *
 * 文件名:Exception.h
 *
 * 日期:2004.5.5
 *
 * 作者:shootingstars(zhouhuis22@sina.com)
 */

#ifndef __HZH_Exception__
#define __HZH_Exception__

#define EXCEPTION_MESSAGE_MAXLEN 256
#include "string.h"

class Exception
{
private:
 char m_ExceptionMessage[EXCEPTION_MESSAGE_MAXLEN];
public:
 Exception(char *msg)
 {
  strncpy(m_ExceptionMessage, msg, EXCEPTION_MESSAGE_MAXLEN);
 }

 char *GetMessage()
 {
  return m_ExceptionMessage;
 }
};

#endif


/* P2P 程序传输协议
 * 
 * 日期:2004-5-21
 *
 * 作者:shootingstars(zhouhuis22@sina.com)
 *
 */

#pragma once
#include <list>

// 定义iMessageType的值
#define LOGIN 1
#define LOGOUT 2
#define P2PTRANS 3
#define GETALLUSER  4

// 服务器端口
#define SERVER_PORT 2280

// Client登录时向服务器发送的消息
struct stLoginMessage
{
 char userName[10];
 char password[10];
};

// Client注销时发送的消息
struct stLogoutMessage
{
 char userName[10];
};

// Client向服务器请求另外一个Client(userName)向自己方向发送UDP打洞消息
struct stP2PTranslate
{
 char userName[10];
};

// Client向服务器发送的消息格式
struct stMessage
{
 int iMessageType;
 union _message
 {
  stLoginMessage loginmember;
  stLogoutMessage logoutmember;
  stP2PTranslate translatemessage;
 }message;
};

// 客户节点信息
struct stUserListNode
{
 char userName[10];
 unsigned int ip;
 unsigned short port;
};

// Server向Client发送的消息
struct stServerToClient
{
 int iMessageType;
 union _message
 {
  stUserListNode user;
 }message;

};

//======================================
// 下面的协议用于客户端之间的通信
//======================================
#define P2PMESSAGE 100               // 发送消息
#define P2PMESSAGEACK 101            // 收到消息的应答
#define P2PSOMEONEWANTTOCALLYOU 102  // 服务器向客户端发送的消息
                                     // 希望此客户端发送一个UDP打洞包
#define P2PTRASH        103          // 客户端发送的打洞包,接收端应该忽略此消息

// 客户端之间发送消息格式
struct stP2PMessage
{
 int iMessageType;
 int iStringLen;         // or IP address
 unsigned short Port; 
};

using namespace std;
typedef list<stUserListNode *> UserList;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -