阅读 124

VC++实现NAT穿透之NAT类型检测

VC++实现NAT穿透之NAT类型检测    


  // NatCheck.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"   
#include

#define N_SuperNode 5
#define N_NatCheckRequestQueue 20
#define PORT_SERVER_NATCHECK 8888
#define PORT_SERVER_NATCHECK_1 8889
#define PORT_CLIENT_NATCHECK 7777
#define PORT_SERVER_CLIENT_INFO 4444
#define N_CheckTime 5 //最多检测次数

SOCKET sClient,sServer;//客户端和服务端的socket号
struct sockaddr_in sn1_addr,sn2_addr,sn_addr,client_addr;
struct sockaddr_in client_addr_deal;
struct in_addr in_addr_tmp,in_addr_tmp1;
FILE *fp; //客户端读取hostcache.txt文件指针
int i=0,j=0;
unsigned long int snIP[N_SuperNode];//读取的SN的IP地址列表
unsigned short int snPort[N_SuperNode];//读取的SN的Port列表
char tmpIP[15],tmpPort[5];
char ch;
char IP_return[15];
/* 本client的网络类型:
* 0 = public net
* 1 = full cone nat
* 2 = symmetric nat
* 3 = restricted cone nat
* 4 = port restricted cone nat
*/
unsigned long int myID=0,friendID=26;//本client和friend的用户ID
char myNetType=-1,friendNetType=-1;
unsigned long int myPubIp,friendPubIp,myPriIp,friendPriIp;
unsigned short int myPubPort,friendPubPort,myPriPort,friendPriPort;
//natCheck请求包结构
struct natCheckPacket{
 char version;
 char dowhat;
 unsigned short int port;
 unsigned long int ip;
 unsigned long int id;
}natCheckPacket;
typedef struct natCheckPacket NCP;
char buff[sizeof(natCheckPacket)];//发包用的缓冲区
//natCheck请求数据结构
struct natCheckRequest{
 NCP ncp;//缓存到来的请求包
 struct natCheckRequest * next;//指向下一个请求数据
 unsigned long int ip;//发送请求的client IP地址
 unsigned short int port;//发送请求的client Port
}natCheckRequest;
typedef struct natCheckRequest NCR;
struct netInfoResponsePacket{
 NCP ncp;
 unsigned long int ip_pub;
 unsigned short int port_pub;
 unsigned short int no;
}netInfoResponsePacket;
typedef struct netInfoResponsePacket NIRP;
//natCheck请求数据循环队列
NCR natCheckRequestQueue[N_NatCheckRequestQueue];
NCR * h_NatCheckRequestQueue,* t_NatCheckRequestQueue;
/*
 * 获得本地主机IP地址
 */
char * getownIP()
{
 //////////////////  
 //   获得主机名.   
 char   hostname[256];  
 int   res   =   gethostname(hostname,   sizeof(hostname));  
 if   (res   !=   0)   {  
  printf("Error:   %u/n",   WSAGetLastError());  
  return "failed";  
 }    
 ////////////////  
 //   根据主机名获取主机信息.  
 hostent*   pHostent   =   gethostbyname(hostname);  
 if   (pHostent==NULL)   {  
  printf("Error:   %u/n",   WSAGetLastError());  
  return   "failed";  
 }  
 //////////////////  
 //   解析返回的hostent信息.   
 hostent&   he   =   *pHostent;  
 sockaddr_in   sa;  
    memcpy   (   &sa.sin_addr.s_addr,   he.h_addr_list[0],he.h_length);  
 return inet_ntoa(sa.sin_addr);  
}
unsigned long int getownIP_uli()
{
 //////////////////  
 //   获得主机名.   
 char   hostname[256];  
 int   res   =   gethostname(hostname,   sizeof(hostname));  
 if   (res   !=   0)   {  
  printf("Error:   %u/n",   WSAGetLastError());  
  return -1;  
 }    
 ////////////////  
 //   根据主机名获取主机信息.  
 hostent*   pHostent   =   gethostbyname(hostname);  
 if   (pHostent==NULL)   {  
  printf("Error:   %u/n",   WSAGetLastError());  
  return   -1;  
 }  
 //////////////////  
 //   解析返回的hostent信息.   
 hostent&   he   =   *pHostent;  
 sockaddr_in   sa;  
    memcpy   (   &sa.sin_addr.s_addr,   he.h_addr_list[0],he.h_length);  
 return sa.sin_addr.S_un.S_addr;
}
/*
 * superNode接收请求线程入口函数
 */
unsigned int _stdcall ThreadFunction_sn_get(void *param)
{
 //创建套接字
 if( (sServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 {
  printf("获取SOCKET号失败!!!/n");
  return 0;
 }
 //绑定套接字和本机地址和端口  
 sn_addr.sin_family=AF_INET;
 sn_addr.sin_port=htons(PORT_SERVER_NATCHECK);
 sn_addr.sin_addr.s_addr =INADDR_ANY ;//inet_addr(getownIP());  //
 if(bind(sServer, (struct sockaddr*)&sn_addr, sizeof(sn_addr)) < 0 )
 {
  printf("不能将服务器地址捆绑到SOCKET号上!!!/n");
  closesocket(sServer);
  return 0;
 }
 //printf("接收..../n");
 int iLen=sizeof(client_addr);
 struct natCheckPacket * ncp=(struct natCheckPacket *)buff;
 int timeout=300; 
 while (1)
 {
  setsockopt(sServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
  if(recvfrom(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr,&iLen)!=-1)
  {
   //判断队列是否满
   if(t_NatCheckRequestQueue->next!=h_NatCheckRequestQueue)
   {
    //先修改t
    NCR * t=t_NatCheckRequestQueue;
    t_NatCheckRequestQueue=t->next;
    //再写入
    t->ncp=*(ncp);
    t->ip=client_addr.sin_addr.S_un.S_addr;
    t->port=client_addr.sin_port;
   }
  }
  Sleep(300);
  //printf("version:%d/tdowhat:%d/tport:%d/tip:%d/n",ncp->version,ncp->dowhat,
  // ncp->port,ncp->ip);
  //printf("Client said: %s/n",buff);
  //strcpy(buff,"I am a server!!/n");
  //sendto(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr,sizeof(client_addr));
 } 
 //closesocket(sServer);
 return 0;
}
/*
 * 构造NCP包发送给SuperNode
 * 接受返回的NCP包
*/
int sendNCPtoSNandRecvNCP(unsigned long int ip_SN,unsigned short int port_SN,
    NCP ncp_send)
{
 int timeout=300; 
 sn_addr.sin_addr.S_un.S_addr=ip_SN;
 sn_addr.sin_family=AF_INET;
 sn_addr.sin_port=htons(port_SN); 
 int iLen=sizeof(sn_addr);
 NCP * ncp=(NCP *)buff;
 ncp->version=ncp_send.version;
 ncp->dowhat=ncp_send.dowhat;
 ncp->port=ncp_send.port;
 ncp->ip=ncp_send.ip; 
 ncp->id=ncp_send.id;
 setsockopt(sClient, SOL_SOCKET , SO_SNDTIMEO, (char *)&timeout,sizeof(timeout));
 sendto(sClient,buff,sizeof(buff),0,(SOCKADDR*)&sn_addr,iLen);
 in_addr_tmp.S_un.S_addr=ip_SN;
 in_addr_tmp1.S_un.S_addr=ncp->ip;
 char * ipaddr=NULL;
 char addr[15];
 in_addr inaddr;
 inaddr. s_addr=ncp->ip;
 ipaddr= inet_ntoa(inaddr);
 strcpy(addr,ipaddr);
 printf("[send][SN(%s)]/n/tversion:%d/tdowhat:%d/tport:%d/tip:%s/n",inet_ntoa(in_addr_tmp),
  ncp->version,ncp->dowhat,ncp->port,addr);
 timeout=1500;
 setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
 if(recvfrom(sClient,buff,sizeof(buff),0,(sockaddr*)&sn_addr,&iLen)<0)
  return 0;
 else
 {
  inaddr. s_addr=sn_addr.sin_addr.S_un.S_addr;
  ipaddr= inet_ntoa(inaddr);
  strcpy(addr,ipaddr); 
  in_addr_tmp.S_un.S_addr=((NCP *)buff)->ip;
  printf("[rece][sn(%s)]/n/tversion:%d/tdowhat:%d/tport:%d/tip:%s/n",addr,((NCP *)buff)->version,
   ((NCP *)buff)->dowhat,ntohs(((NCP *)buff)->port),inet_ntoa(in_addr_tmp));
  return 1;
 }
}
/*
 * 构造NCP包发送给SuperNode
 * 接受返回的NIRP包
*/
int sendNCPtoSNandRecvNIRP(unsigned long int ip_SN,unsigned short int port_SN,
    NCP ncp_send)
{
 FILE * fp_n;
 FILE * fp_c;//for compare
 //打开文件natCheckResult.txt
 if(!(fp_n=fopen("./natCheckResult.txt","a")))
 {
  printf("natCheckResult.txt file not found!/n");
  return 0;
 }
 char one_line[100],one_line_f[100];
 int timeout=300; 
 sn_addr.sin_addr.S_un.S_addr=ip_SN;
 sn_addr.sin_family=AF_INET;
 sn_addr.sin_port=htons(port_SN); 
 int iLen=sizeof(sn_addr);
 char buff_recv[sizeof(NIRP)];
 NCP * ncp=(NCP *)buff;
 NIRP * nirp=(NIRP *)buff_recv;
 ncp->version=ncp_send.version;
 ncp->dowhat=ncp_send.dowhat;
 ncp->port=ncp_send.port;
 ncp->ip=ncp_send.ip; 
 ncp->id=ncp_send.id;
 setsockopt(sClient, SOL_SOCKET , SO_SNDTIMEO, (char *)&timeout,sizeof(timeout));
 sendto(sClient,buff,sizeof(buff),0,(SOCKADDR*)&sn_addr,iLen);
 in_addr_tmp.S_un.S_addr=ip_SN;
 in_addr_tmp1.S_un.S_addr=ncp->ip;
 char * ipaddr=NULL;
 char addr[15];
 in_addr inaddr;
 inaddr. s_addr=ncp->ip;
 ipaddr= inet_ntoa(inaddr);
 strcpy(addr,ipaddr);
 printf("[send][SN(%s)]/n/tversion:%d/tdowhat:%d/tport:%d/tip:%s/n",
  inet_ntoa(in_addr_tmp),ncp->version,ncp->dowhat,ncp->port,addr);
 timeout=1500;
 setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
 int stop=0;
 while(!stop)
 {
  setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
  if(recvfrom(sClient,buff_recv,sizeof(buff_recv),0,(sockaddr*)&sn_addr,&iLen)<0)
   stop=1;
  else
  {
   sprintf(one_line,"%u %u %u %u %u %u",
    ((NIRP *)buff_recv)->ncp.id,
    ((NIRP *)buff_recv)->ncp.dowhat,
    ((NIRP *)buff_recv)->ip_pub,
    ((NIRP *)buff_recv)->port_pub,
    ((NIRP *)buff_recv)->ncp.ip,
    ((NIRP *)buff_recv)->ncp.port);
   printf("%s/n",one_line);
   friendNetType=((NIRP *)buff_recv)->ncp.dowhat;
   friendPubIp=((NIRP *)buff_recv)->ip_pub;
   friendPubPort=((NIRP *)buff_recv)->port_pub;
   friendPriIp=((NIRP *)buff_recv)->ncp.ip;
   friendPriPort=((NIRP *)buff_recv)->ncp.port;
   //打开文件natCheckResult.txt
   if(!(fp_c=fopen("./natCheckResult.txt","r")))
   {
    printf("natCheckResult.txt file not found!/n");
    return 0;
   }
   while(!feof(fp_c))
   {
    i=0;
    while((ch=fgetc(fp_c))!=10 && ch!=-1)
     one_line_f[i++]=ch;
    if(ch==-1)
     break;
    one_line_f[i]='/0';
    if(strcmp(one_line,one_line_f)==0)
    {
     fclose(fp_c);
     fclose(fp_n);
     return 1;
    }
   }
   
   fprintf(fp_n,"%s/n",one_line);
   fflush(fp_n);
  }
 }
 fclose(fp_c);
 fclose(fp_n);
 return 1;
}
void longIP2string(unsigned long int ip,char* addr)
{
 in_addr inaddr;
 char * ipaddr=NULL;
 inaddr. s_addr=sn_addr.sin_addr.S_un.S_addr;
 ipaddr= inet_ntoa(inaddr);
 strcpy(addr,ipaddr);
}
/*
 * 从client端的hostcache.txt文件获取一个SN
 *  写入ip_SN,port_SN
 *  如果文件已经到结尾,或者文件语法错误,则返回0
 */
int getSNfromHostCache(FILE * fp,unsigned long int &ip_SN,unsigned short int &port_SN)
{
 if(feof(fp))
  return 0;
 i=0;
 while((ch=fgetc(fp))!=' ' && ch!=10 && ch!=-1)
  tmpIP[i++]=ch;
 if(ch==10 || ch==-1)
 {
  printf("hostcache.txt file error!/n");
  return 0;
 }
 tmpIP[i]='/0';
 i=0;
 while((ch=fgetc(fp))!=10 && ch!=-1)
  tmpPort[i++]=ch;
 tmpPort[i]='/0';
 ip_SN=inet_addr(tmpIP);
 port_SN=(unsigned short int)atoi(tmpPort);
 return 1;
}
/*
 * client最先知道检测结果
 *  client需要向自己缓存的SN节点汇报自己的网络类型信息
 *  这里依然使用了NCP包
void broadcastNatCheckResult(char version,
        char dowhat,unsigned long int ip,unsigned short int port,
        unsigned long int id)
*/
void broadcastNatCheckResult(NCP ncp_send)
{
 FILE * fp_b=fp;
 fseek(fp_b,0L,0);
 while (!feof(fp_b)) {
  i=0;
  while((ch=fgetc(fp_b))!=' ' && ch!=10 && ch!=-1)
   tmpIP[i++]=ch;
  if(ch==10 || ch==-1)
  {
   printf("hostcache.txt file error!/n");
   return;
  }
  tmpIP[i]='/0';
  i=0;
  while((ch=fgetc(fp_b))!=10 && ch!=-1)
   tmpPort[i++]=ch;
  tmpPort[i]='/0';
  int timeout=300;
  sn_addr.sin_addr.S_un.S_addr=inet_addr(tmpIP);
  sn_addr.sin_family=AF_INET;
  sn_addr.sin_port=htons((unsigned short int)atoi(tmpPort));
  int iLen=sizeof(sn_addr);
  NCP * ncp=(NCP *)buff;
  ncp->version=ncp_send.version;
  ncp->dowhat=ncp_send.dowhat;
  ncp->port=ncp_send.port;
  ncp->ip=ncp_send.ip; 
  ncp->id=ncp_send.id;
  setsockopt(sClient, SOL_SOCKET , SO_SNDTIMEO, (char *)&timeout,sizeof(timeout));
  sendto(sClient,buff,sizeof(buff),0,(SOCKADDR*)&sn_addr,iLen);
 }
 fclose(fp_b);
 return;
}
/*
 * SN收到client汇报的结果,
 * 向NatCheckResult.txt 写入id 网络类型 外网ip port 内网ip port 
 */
int storeNatCheckResult(unsigned long int id,char type,
      unsigned long int pub_ip,unsigned short int pub_port,
      unsigned long int pri_ip,unsigned short int pri_port)
{
 FILE * fp_n;
 char one_line[100],one_line_f[100];
 sprintf(one_line,"%u %u %u %u %u %u",id,type,pub_ip,pub_port,pri_ip,pri_port);
 //打开文件natCheckResult.txt
 if(!(fp_n=fopen("./natCheckResult.txt","rw")))
 {
  printf("natCheckResult.txt file not found!/n");
  return 0;
 }
 while(!feof(fp_n))
 {
  i=0;
  while((ch=fgetc(fp_n))!=10 && ch!=-1)
   one_line_f[i++]=ch;
  if(ch==-1)
  {
   break;
  }
  one_line_f[i]='/0';
  if(strcmp(one_line,one_line_f)==0)
   return 0;
 }
 fclose(fp_n);
 if(!(fp_n=fopen("./natCheckResult.txt","a")))
 {
  printf("natCheckResult.txt file not found!/n");
  return 0;
 } 
 fprintf(fp_n,"%u %u %u %u %u %u/n",id,type,pub_ip,pub_port,pri_ip,pri_port);
 printf("%u %u %u %u %u %u/n",id,type,pub_ip,pub_port,pri_ip,pri_port);
 fflush(fp_n);
 fclose(fp_n);
 return 1;
}
/*
 * SN收到client的查询请求,
 * 从NatCheckResult.txt 中查找id对应的 网络类型 外网ip port 内网ip port 
 */
int searchNatCheckResult(NCP ncp,SOCKET sServer,sockaddr_in addr)
{
 FILE * fp_n;
 char id[32];
 //打开文件natCheckResult.txt
 if(!(fp_n=fopen("./natCheckResult.txt","r")))
 {
  printf("natCheckResult.txt file not found!/n");
  return 0;
 }
 int j=0;
 while(!feof(fp_n))
 {
  i=0;
  while((ch=fgetc(fp_n))!=' ' && ch!=10 && ch!=-1)
   id[i++]=ch;
  if(ch==10)
  {
   printf("natCheckResult.txt file error!/n");
   return 0;
  }
  if(ch==-1)
  {
   break;
  }
  id[i]='/0';
  if(ncp.id!=(unsigned long int)atoi(id))
  {
   while(fgetc(fp_n)!=10);
   continue;
  }
  unsigned long int ip_pub;
  unsigned short int port_pub;
  fscanf(fp_n,"%d %d %d %d %d/n",&(ncp.dowhat),&ip_pub,&port_pub,&(ncp.ip),&(ncp.port));
  char buff_send[sizeof(NIRP)];
  NIRP * nirp = (NIRP *) buff_send;
  nirp->ncp=ncp;
  nirp->ip_pub=ip_pub;
  nirp->port_pub=port_pub;
  nirp->no=j++;  
  sendto(sServer,buff_send,sizeof(buff_send),0,(sockaddr*)&addr,sizeof(addr));
 }
 fclose(fp_n);
 return 1;
}
/*
 * main函数
 */
int main(int argc, char* argv[])
{  
 if(argc < 2)
 {
  printf("usage: %s -c or -s /n", argv[0]);
  printf("-c: for clients/n-s: for superNode/n");
  return 0;
 }
 //////////////////
 //   初始化winsock环境.
 WSADATA wsadata;
 WORD wVersion=MAKEWORD(2,0);  //winsock 2.0
 if(WSAStartup(wVersion,&wsadata)!=0)
 {
  printf("winsocket initalize failed!/n");
  WSACleanup();
  return 0;
 } 
 ///////////////////////////////////////////////////////////////////
 //客户端程序入口
 ////////////////////////////////////////////////////////////////////
 if(strcmp(argv[1],"-c")==0)
 { 
  if(argc>=3)
   myID=atoi(argv[2]);
  NCP ncp_send;
  ncp_send.version=0;
  ncp_send.id=myID;
  //////////////////
  //  读取hostcache文件中的前两个supernode
  if(!(fp=fopen("./hostcache.txt","r")))
  {
   printf("hostcache.txt file not found!/n");
   return 0;
  }
  if( (sClient=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
  {
   printf("获取SOCKET号失败!!!/n");
   return 0;
  } 
  myPriIp=inet_addr(getownIP());
  myPriPort=htons(PORT_CLIENT_NATCHECK);
  client_addr.sin_addr.S_un.S_addr=INADDR_ANY; /* use my IP address */
  client_addr.sin_family=AF_INET;
  client_addr.sin_port=htons(PORT_CLIENT_NATCHECK);
  printf("[info]client ip=%s/tport:%d/n",getownIP(),ntohs(myPriPort));
  //bind(int sockfd, const struct sockaddr *my_addr, int addrlen);
  if(bind(sClient,(struct sockaddr*)&client_addr,sizeof(client_addr))<0)
  {
   printf("不能捆绑到SOCKET号上!!!/n");
   closesocket(sClient);
   return 0;
  }
  /*/////////////////////////////////////////////////////////////////////////////////////
  // NAT检测开始
  // 检测流程:
   1.向超级节点SN1发送0号包,等待一段超时时间,如果没有返回,换下一个超级节点。
     否则go 2,如果hostcache中的节点都失效了,那么开始向中心节点发请求,来得到一批
     新的superNode节点列表;
   2.判断超级节点返回的7号包中携带的(ip,port)和自己发包时用的(ip,port)是否相同,
     如果相同的话,说明本地是公网IP;如果不同的话,说明在NAT后;
   3.向SN1发送1号包,并携带一个自己列表中的另一个superNode,SN2的(ip,port),让SN1
     发8号包给SN2,告诉本client的外网(ip,port),让SN2给本client发6号包;另外SN1还可
     以在自己维护的临近的SN列表中选择3个也给client发送6号包。
   4.如果client在一段超时时间内,没有收到任何包,那么在将3中的过程进行一次,总共最多
     可以进行N_CheckTime(5)次。其中只要有一次收到包,就go 5。
   5.如果收到了SN2、SN3、SN4中的任何一个,检测就停止,结果是full cone Nat。如果没有
     收到任何包,检测继续,go 6
   6.client从自己的hostcache中选择一个SN2,向其发送2号包,等待一段超时时间,如果没有
     收到5号包的话,继续从hostcache中另选择一个SN3,如果hostcache中的所有SN都不给回应
     5号包的话,从中心节点获取新的SN后,继续发2号包。收到第一个5号包,开始比较,如果和
     SN1的返回结果(ip,port)中如果有任何一对不一致,则说明是Symmetric NAT,检测停止。
     否则是Restricted Cone NAT,同时继续检测,go 7
   7.client向SN1发送3号包,让SN1换用另一个端口发4号包,重复若干次,如果client能收到说明
     是Restricted Cone NAT,否则是Port Restricted Cone NAT。检测结束。
  *///////////////////////////////////////////////////////////////////////////////////////
  //读取第一个SN,SN1的(IP,Port),并给其发0号包
  if(getSNfromHostCache(fp,snIP[0],snPort[0])==0)
  {
   printf("[error]there is no SN in HostCache.txt!/n");
   return 0;
  }
  /*
  int sendNCPtoSNandRecvNCP(unsigned long int ip_SN,
   unsigned short int port_SN,
   char version,
   char dowhat,
   unsigned long int ip,
   unsigned short int port)
  */
  ncp_send.dowhat=0;
  ncp_send.ip=0;
  ncp_send.port=0; 
  while(sendNCPtoSNandRecvNCP(snIP[0],snPort[0],ncp_send)==0)
  {
   if(getSNfromHostCache(fp,snIP[0],snPort[0])==0)
   {
    printf("[error]there is no more SN in HostCache.txt!/n");
    //此时应该从中心节点获取更新的SN列表
    //......
    printf("[error]check failed temporarily");
    return 0;
   }
  }
  long ft=ftell(fp);
  in_addr_tmp.S_un.S_addr=snIP[0];
  printf("[info]SN1 ip=%s/tport=%d/n",inet_ntoa(in_addr_tmp),snPort[0]);
  //对比返回的(ip,port) ip_return port_return
  unsigned long int ip_return=((NCP *)buff)->ip;
  unsigned short int port_return=((NCP *)buff)->port;
  if(myPriIp==ip_return && myPriPort==port_return)
  {
   printf("[Reslut]public net/n");
   myNetType=0;
   ncp_send.dowhat=9;
   ncp_send.ip=myPubIp=myPriIp;
   ncp_send.port=myPubPort=myPriPort;
   broadcastNatCheckResult(ncp_send);
   return 0;
  }
  //再读取一个SN,SN2的(IP,Port)
  int checkTime=N_CheckTime;
  while(checkTime>0 && !feof(fp))
  {
   if(getSNfromHostCache(fp,snIP[1],snPort[1])==0)
   {
    printf("[error]there is no more SN in HostCache.txt!/n");
    break;
   } 
   ncp_send.dowhat=1;
   ncp_send.ip=snIP[1];
   ncp_send.port=snPort[1];
   if(sendNCPtoSNandRecvNCP(snIP[0],snPort[0],ncp_send)==1)
   { 
    printf("[Reslut]full cone nat/n");
    myNetType=1;
    ncp_send.dowhat=10;
    ncp_send.ip=myPriIp;
    ncp_send.port=myPriPort;
    myPubIp=ip_return;
    myPubPort=port_return;
    broadcastNatCheckResult(ncp_send);
    return 0;
   }  
   checkTime--;
  }
  printf("[Reslut]not full cone nat, checking is continued./n");
  in_addr_tmp.S_un.S_addr=snIP[1];
  printf("[info]SN2 ip=%s/tport=%d/n",inet_ntoa(in_addr_tmp),snPort[1]);  
  //在hostcache中的SN1下再找一个SN3,给他发送2号包,让他返回client的ip和port
  fseek(fp,ft,0);
  if(getSNfromHostCache(fp,snIP[1],snPort[1])==0)
  {
   printf("[error]there is no SN in HostCache.txt!/n");
   return 0;
  } 
  ncp_send.dowhat=2;
  ncp_send.ip=0;
  ncp_send.port=0; 
  while(sendNCPtoSNandRecvNCP(snIP[1],snPort[1],ncp_send)==0)
  {
   if(getSNfromHostCache(fp,snIP[1],snPort[1])==0)
   {
    printf("[error]there is no more SN in HostCache.txt!/n");
    //此时应该从中心节点获取更新的SN列表
    //......
    printf("[error]check failed temporarily");
    return 0;
   }
  }
  in_addr_tmp.S_un.S_addr=snIP[1];
  printf("[info]SN3 ip=%s/tport=%d/n",inet_ntoa(in_addr_tmp),snPort[1]);
  //对比返回的(ip,port) ip_return port_return
  unsigned long int ip_return1=((NCP *)buff)->ip;
  unsigned short int port_return1=((NCP *)buff)->port;
  if(ip_return1!=ip_return || port_return1!=port_return)
  {
   printf("[Reslut]symmetric nat/n");
   myNetType=2;
   ncp_send.dowhat=11;
   ncp_send.ip=myPriIp;
   ncp_send.port=myPriPort;
   broadcastNatCheckResult(ncp_send);
   return 0;
  } 
  ncp_send.dowhat=3;
  ncp_send.ip=0;
  ncp_send.port=0; 
  if(sendNCPtoSNandRecvNCP(snIP[0],snPort[0],ncp_send)==1)
  {
   printf("[Reslut]Restricted Cone nat/n");
   myNetType=3;
   ncp_send.dowhat=12;
   ncp_send.ip=myPriIp;
   ncp_send.port=myPriPort;
   myPubIp=ip_return;
   myPubPort=port_return;
   broadcastNatCheckResult(ncp_send);
   return 0;
  }
  printf("[Reslut]Port Restricted Cone nat/n");
  myNetType=4;
  ncp_send.dowhat=13;
  ncp_send.ip=myPriIp;
  ncp_send.port=myPriPort;
  myPubIp=ip_return;
  myPubPort=port_return;
  broadcastNatCheckResult(ncp_send);
  //以上检测网络类型结束,下面开始与某好友通讯
  printf("do you want to talk with someone? [Y]yes [N]no/n");
  scanf("%c",&ch);
  if(ch!='Y' && ch!='y')
   return 0;
  printf("which friend would you want to talk with? Input his id:/n");
  scanf("%d",&friendID);
  //从SN1获取friend网络类型列表
  ncp_send.dowhat=14;
  ncp_send.ip=0;
  ncp_send.port=0;
  ncp_send.id=friendID;
  if(sendNCPtoSNandRecvNIRP(snIP[0],snPort[0],ncp_send)==0)
  { 
   printf("[error]查询好友网络信息失败!/n");
   return 0;
  }
  switch(friendNetType)
  {
  case 9:
   break;
  case 10:
   break;
  case 11:
   break;
  case 12:
   break;
  case 13:
   break;
  default:
   break;
  }
 }
 ////////////////////////////////////////////////////////////////////
 //服务端程序入口
 /////////////////////////////////////////////////////////////////////
 else if(strcmp(argv[1],"-s")==0)
 {
  //创建另一个套接字
  SOCKET sServer1;
  if( (sServer1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
  {
   printf("获取SOCKET号失败!!!/n");
   return 0;
  }
  //绑定套接字和本机地址和端口  
  sn1_addr.sin_family=AF_INET;
  sn1_addr.sin_port=htons(PORT_SERVER_NATCHECK_1);
  sn1_addr.sin_addr.s_addr =INADDR_ANY ;//inet_addr(getownIP());  //
  if(bind(sServer1, (struct sockaddr*)&sn1_addr, sizeof(sn1_addr)) < 0 )
  {
   printf("不能将服务器地址捆绑到SOCKET号上!!!/n");
   closesocket(sServer1);
   return 0;
  }
  //////////////////
  //  循环队列natCheckRequestQueue初始化
  for(i=0;i<N_NatCheckRequestQueue-1;i++)
  {
   natCheckRequestQueue[i].next= & natCheckRequestQueue[i+1];
  }
  natCheckRequestQueue[N_NatCheckRequestQueue-1].next
   = & natCheckRequestQueue[0];
  h_NatCheckRequestQueue = & natCheckRequestQueue[0];
  t_NatCheckRequestQueue = & natCheckRequestQueue[0];
  //////////////////
  //  启动接受请求的服务线程
  //  入口函数为:ThreadFunction_sn_get
  //  判断队列是否满,不满的条件下,先修改t,再写进t指向的单元
  //HANDLE thread_sn_get = (HANDLE)_beginthread(ThreadFunction_sn_get,0,NULL);
  HANDLE thread_sn_get = (HANDLE)_beginthreadex(0,0,&ThreadFunction_sn_get,0,0,NULL);
  //////////////////
  //  主服务线程
  //  用来从循环队列natCheckRequestQueue的头指针得到请求数据来处理
  //  判断队列是否空,非空的条件下,先读出h指向的单元,再修改h
  while(1)
  {
   //判断队列是否空
   if(t_NatCheckRequestQueue!=h_NatCheckRequestQueue)
   {
    //先读出
    NCP * ncp_deal=(NCP *)buff;
    *(ncp_deal)=h_NatCheckRequestQueue->ncp;
    unsigned long int ip_deal=h_NatCheckRequestQueue->ip;
    unsigned short int port_deal=h_NatCheckRequestQueue->port;
    //再修改h
    h_NatCheckRequestQueue=h_NatCheckRequestQueue->next;
    //处理这个来自(ip,port)的请求 
    switch(ncp_deal->dowhat)
    {
    /* dowhat字段: 
    *   0 请求用户返回Ip:port   7 单独的响应包
    *   1 客户请求提供其他主机检测  6 其他主机响应包
    *   2 同时请求两主机发回响应  5 针对2的回应包
    *   3 请求使用另一端口发回响应  4 使用另一端口响应
    *   8 SN请求其他主机检测
    -----------------------------------------------------------
    *  9  汇报检测结果给SN public net
    *  10 汇报检测结果给SN full cone nat
    *  11 汇报检测结果给SN symmetric nat
    *  12 汇报检测结果给SN restricted cone nat
    *  13 汇报检测结果给SN port restricted cone nat
    -----------------------------------------------------------
    */
    case 0:
     ncp_deal->dowhat=7;
     ncp_deal->ip=ip_deal;
     ncp_deal->port=port_deal;
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ip_deal;
     client_addr_deal.sin_port=port_deal;
     printf("[deal][0]send [7] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));
     sendto(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr_deal,sizeof(client_addr_deal));
     break;
    case 1:
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ncp_deal->ip;
     client_addr_deal.sin_port=htons(ncp_deal->port);
     ncp_deal->dowhat=8;
     ncp_deal->ip=ip_deal;
     ncp_deal->port=port_deal;
     printf("[deal][1]send [8] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));     
     sendto(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr_deal,sizeof(client_addr_deal));
     break;
    case 2:
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ip_deal;
     client_addr_deal.sin_port=port_deal;
     ncp_deal->dowhat=5;
     ncp_deal->ip=ip_deal;
     ncp_deal->port=port_deal;
     printf("[deal][2]send [5] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));     
     sendto(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr_deal,sizeof(client_addr_deal));
     break;
    case 3:
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ip_deal;
     client_addr_deal.sin_port=port_deal;
     ncp_deal->dowhat=4;
     ncp_deal->ip=ip_deal;
     ncp_deal->port=port_deal;
     printf("[deal][3]send [4] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));     
     sendto(sServer1,buff,sizeof(buff),0,(sockaddr*)&client_addr_deal,sizeof(client_addr_deal));
     break;
    case 8:
     ncp_deal->dowhat=6;
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ncp_deal->ip;
     client_addr_deal.sin_port=htons(ncp_deal->port);
     ncp_deal->ip=getownIP_uli();
     ncp_deal->port=htons(PORT_SERVER_NATCHECK);
     printf("[deal][8] send[6] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));
     sendto(sServer,buff,sizeof(buff),0,(sockaddr*)&client_addr_deal,sizeof(client_addr_deal));
     break;
    case 9:
     //写入id 网络类型 外网ip port 内网ip port 
     storeNatCheckResult(ncp_deal->id,
      ncp_deal->dowhat,ip_deal,port_deal,
      ncp_deal->ip,ncp_deal->port);
     printf("[deal][9] writing file/n");
     break;
    case 10:
     //写入id 网络类型 外网ip port 内网ip port
     storeNatCheckResult(ncp_deal->id,
      ncp_deal->dowhat,ip_deal,port_deal,
      ncp_deal->ip,ncp_deal->port);
     printf("[deal][10] writing file/n");
     break;
    case 11:
     //写入id 网络类型 外网ip port 内网ip port
     storeNatCheckResult(ncp_deal->id,
      ncp_deal->dowhat,ip_deal,port_deal,
      ncp_deal->ip,ncp_deal->port);
     printf("[deal][11] writing file/n");
     break;
    case 12:
     //写入id 网络类型 外网ip port 内网ip port
     storeNatCheckResult(ncp_deal->id,
      ncp_deal->dowhat,ip_deal,port_deal,
      ncp_deal->ip,ncp_deal->port);
     printf("[deal][12] writing file/n");
     break;
    case 13:
     //写入id 网络类型 外网ip port 内网ip port 
     storeNatCheckResult(ncp_deal->id,
      ncp_deal->dowhat,ip_deal,port_deal,
      ncp_deal->ip,ncp_deal->port);
     printf("[deal][13] writing file/n"); 
     break;
    case 14:
     //处理client发来的查询请求
     //查询id对应用户的网络信息 
     client_addr_deal.sin_family=AF_INET;
     client_addr_deal.sin_addr.S_un.S_addr=ip_deal;
     client_addr_deal.sin_port=port_deal;
     printf("[deal][14]send [15] to:%d.%d.%d.%d:%d/n",
      client_addr_deal.sin_addr.S_un.S_un_b.s_b1,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b2,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b3,
      client_addr_deal.sin_addr.S_un.S_un_b.s_b4,
      htons(client_addr_deal.sin_port));
     searchNatCheckResult(*ncp_deal,sServer,client_addr_deal);
     break;
    default:
     break;
    }
   }
   Sleep(300);
  }  
 } 
 //////////////////  
 //   终止   Windows   sockets   API  
 WSACleanup(); 
 return 0;
}



©著作权归作者所有:来自51CTO博客作者yinchengmvp的原创作品,如需转载,请注明出处,否则将追究法律责任


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐