udp打洞,c++实现,Nat

来源:互联网 发布:winscp连接linux被拒绝 编辑:程序博客网 时间:2024/05/16 07:14
#pragma once#include <list>// 定义iMessageType的值#define LOGIN 1#define LOGOUT 2#define P2PTRANS 3#define GETALLUSER  4// 服务器端口#define SERVER_PORT 6060// 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 addressunsigned short Port; };using namespace std;typedef list<stUserListNode *> UserList;

#ifndef NATCLIENT_H#define NATCLIENT_H#pragma comment(lib,"ws2_32.lib")#include "windows.h"#include "..\common\msgproto.h"#include <iostream>#include <string.h>using namespace std;#include <thread>#define COMMANDMAXC 25600#define MAXRETRY    5class Nat_Client{private:USHORT g_nClientPort ;USHORT g_nServerPort  ;char UserName[10];char ServerIP[20];void command_exit();void command_send(char * CommandLine);void command_tell(char * CommandLine);void command_getu();bool SendMessageTo(char *UserName, char *Message);// 接收到P2P的消息static void command_p2pMsg(sockaddr_in &remote,stP2PMessage &recvbuf,int sinlen);// 接收到打洞命令,向指定的IP地址打洞static void command_p2pSomeoneWantToCallYou(sockaddr_in &remote,stP2PMessage &recvbuf,int sinlen);public:static UserList ClientList;static SOCKET PrimaryUDP;static bool RecvedACK;Nat_Client();~Nat_Client();SOCKET mksock(int type);void InitWinSock();stUserListNode GetUser(char *username);void BindSock(SOCKET sock);void ConnectToServer(SOCKET sock,char *username, char *serverip);void OutputUsage();bool SendMessageTo2(char *UserName, char *Message, const char *pIP, USHORT nPort );void ParseCommand(char * CommandLine);static void RecvThreadProc();void Init();};UserList Nat_Client::ClientList ;bool Nat_Client::RecvedACK;SOCKET Nat_Client::PrimaryUDP = 0;Nat_Client::~Nat_Client(){command_exit();Sleep(100);}void Nat_Client::command_send(char * CommandLine){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_s(message,sizeof(message), &(CommandLine[i+1]));if(SendMessageTo(sendname, message))printf("Send OK!\n");else printf("Send Failure!\n");}void Nat_Client::command_tell(char * CommandLine){char sendname[20];char sendto[ 64 ] = {0};char message[COMMANDMAXC];int i;for(i=5;;i++){if(CommandLine[i]!=' ')sendname[i-5]=CommandLine[i];else{sendname[i-5]='\0';break;}}i++;int nStart = i;for(;;i++){if(CommandLine[i]!=' ')sendto[i-nStart]=CommandLine[i];else{sendto[i-nStart]='\0';break;}}strcpy_s(message,sizeof(message), &(CommandLine[i+1]));char szIP[32] = {0};char *p1 = sendto;char *p2 = szIP;while ( *p1 != ':' ){*p2++ = *p1++;}p1++;USHORT nPort = atoi( p1 );if(SendMessageTo2(sendname, message, strcmp( szIP, "255.255.255.255" ) ? szIP : NULL, nPort ))printf("Send OK!\n");else printf("Send Failure!\n");}void Nat_Client::command_getu(){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(g_nServerPort);sendto(Nat_Client::PrimaryUDP,(const char*)&command, sizeof(command), 0, (const sockaddr *)&server, sizeof(server));}// 接收到P2P的消息void Nat_Client::command_p2pMsg(sockaddr_in &remote,stP2PMessage &recvbuf,int sinlen){char *comemessage= new char[recvbuf.iStringLen];int iread1 = recvfrom(PrimaryUDP, comemessage, 256, 0, (sockaddr *)&remote, &sinlen);comemessage[iread1-1] = '\0';if(iread1<=0)printf("Recv Message Error\n");else{printf("Recv a Message, %s:%ld -> %s\n", inet_ntoa( remote.sin_addr), htons(remote.sin_port), comemessage);stP2PMessage sendbuf;sendbuf.iMessageType = P2PMESSAGEACK;sendto(PrimaryUDP, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&remote, sizeof(remote));printf("Send a Message Ack to %s:%ld\n", inet_ntoa( remote.sin_addr), htons(remote.sin_port) );}delete []comemessage;}void Nat_Client::command_p2pSomeoneWantToCallYou(sockaddr_in &remote,stP2PMessage &recvbuf,int sinlen){// 接收到打洞命令,向指定的IP地址打洞printf("Recv p2someonewanttocallyou from %s:%ld\n", inet_ntoa( remote.sin_addr), htons(remote.sin_port) );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 punchingstP2PMessage message;message.iMessageType = P2PTRASH;sendto(PrimaryUDP, (const char *)&message, sizeof(message), 0, (const sockaddr*)&_remote, sizeof(_remote));printf("Send p2ptrash to %s:%ld\n", inet_ntoa( _remote.sin_addr), htons(_remote.sin_port) );}void Nat_Client::Init(){InitWinSock();Nat_Client::PrimaryUDP = mksock(SOCK_DGRAM);BindSock(Nat_Client::PrimaryUDP);std::cout<<"cin user name"<<std::endl;std::cin>>UserName;ConnectToServer(Nat_Client::PrimaryUDP, UserName, ServerIP);}void Nat_Client::RecvThreadProc(){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的消息Nat_Client::command_p2pMsg(remote,recvbuf,sinlen);break;}case P2PSOMEONEWANTTOCALLYOU:{Nat_Client::command_p2pSomeoneWantToCallYou(remote,recvbuf,sinlen);break;}case P2PMESSAGEACK:{// 发送消息的应答RecvedACK = true;printf("Recv message ack from %s:%ld\n", inet_ntoa( remote.sin_addr), htons(remote.sin_port) );break;}case P2PTRASH:{// 对方发送的打洞消息,忽略掉。//do nothing ...printf("Recv p2ptrash data from %s:%ld\n", inet_ntoa( remote.sin_addr), htons(remote.sin_port) );break;}case GETALLUSER:{int usercount;int fromlen = sizeof(remote);int iread = recvfrom(PrimaryUDP, (char *)&usercount, sizeof(int), 0, (sockaddr *)&remote, &fromlen);if(iread<=0)printf("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;}}}}void Nat_Client::ParseCommand(char * CommandLine){if(strlen(CommandLine)<4)return;char Command[10];strncpy_s(Command,sizeof(Command), CommandLine, 4);Command[4]='\0';if(strcmp(Command,"exit")==0){command_exit();}else if(strcmp(Command,"send")==0){command_send(CommandLine);}else if(strcmp(Command,"tell")==0){command_tell(CommandLine);}else if(strcmp(Command,"getu")==0){command_getu();}}void Nat_Client::command_exit(){stMessage sendbuf;sendbuf.iMessageType = LOGOUT;strncpy_s(sendbuf.message.logoutmember.userName,10, UserName, 10);sockaddr_in server;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);server.sin_family = AF_INET;server.sin_port = htons(g_nServerPort);sendto(PrimaryUDP,(const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr *)&server, sizeof(server));shutdown(PrimaryUDP, 2);closesocket(PrimaryUDP);exit(0);}bool Nat_Client::SendMessageTo2(char *UserName, char *Message, const char *pIP, USHORT nPort ){char realmessage[256];unsigned int UserIP = 0L;unsigned short UserPort = 0;if ( pIP != NULL ){UserIP = ntohl( inet_addr( pIP ) );UserPort = nPort;}else{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_s(realmessage,sizeof(realmessage), Message);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;printf( "Send message, %s:%ld -> %s\n", inet_ntoa( remote.sin_addr ), ntohs( remote.sin_port ), realmessage );for(int i=0;i<MAXRETRY;i++){RecvedACK = false;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;elseSleep(300);}}return false;}bool Nat_Client::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_s(realmessage,sizeof(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;elseSleep(300);}// 没有接收到目标主机的回应,认为目标主机的端口映射没有// 打开,那么发送请求信息给服务器,要服务器告诉目标主机// 打开映射端口(UDP打洞)sockaddr_in server;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);server.sin_family = AF_INET;server.sin_port = htons(g_nServerPort);stMessage transMessage;transMessage.iMessageType = P2PTRANS;strcpy_s(transMessage.message.translatemessage.userName,10, UserName);sendto(PrimaryUDP, (const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr*)&server, sizeof(server));Sleep(100);// 等待对方先发送信息。}return false;}void Nat_Client::OutputUsage(){cout<<"You can input you command:\n"<<"Command Type:\"send\",\"tell\", \"exit\",\"getu\"\n"<<"Example : send Username Message\n"<<"Example : tell Username ip:port Message\n"<<"          exit\n"<<"          getu\n"<<endl;}void Nat_Client::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(g_nServerPort);stMessage sendbuf;sendbuf.iMessageType = LOGIN;strcpy_s(sendbuf.message.loginmember.userName,10, username);sendto(sock, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&remote,sizeof(remote));int usercount;int fromlen = sizeof(remote);    for ( ;; )    {        fd_set readfds;        fd_set writefds;        FD_ZERO( &readfds );        FD_ZERO( &writefds );        FD_SET( sock, &readfds );        int maxfd = sock;        timeval to;        to.tv_sec = 2;        to.tv_usec = 0;        int n = select( maxfd + 1, &readfds, &writefds, NULL, &to );        if ( n > 0 )        {            if ( FD_ISSET( sock, &readfds ) ){int iread = recvfrom(sock, (char *)&usercount, sizeof(int), 0, (sockaddr *)&remote, &fromlen);if(iread<=0)printf("Login error\n");break;}        }        else if ( n < 0 )printf("Login error\n");sendto(sock, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&remote,sizeof(remote));    }// 登录到服务端后,接收服务端发来的已经登录的用户的信息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 Nat_Client::BindSock(SOCKET sock){sockaddr_in sin;sin.sin_addr.S_un.S_addr = INADDR_ANY;sin.sin_family = AF_INET;sin.sin_port = htons(g_nClientPort);//if (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)//printf("bind error");try{bind(sock, (struct sockaddr*)&sin, sizeof(sin));}catch(...){printf("bind error");}}stUserListNode Nat_Client::GetUser(char *username){UserList::iterator UserIterator = ClientList.begin();for(;UserIterator!=ClientList.end();++UserIterator){if( strcmp( ((*UserIterator)->userName), username) == 0 )return *(*UserIterator);}return *(*UserIterator);}SOCKET Nat_Client::mksock(int type){SOCKET sock = socket(AF_INET, type, 0);if (sock < 0){        printf("create socket error");}return sock;}Nat_Client::Nat_Client():g_nClientPort(9896),g_nServerPort(SERVER_PORT){strcpy_s(UserName,"client_");strcpy_s(ServerIP,"120.25.2.24");}void Nat_Client::InitWinSock(){WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){printf("Windows sockets 2.2 startup");}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));}}int Client_Run(){std::auto_ptr<Nat_Client> clt(new Nat_Client());try{clt->Init();std::thread t(&Nat_Client::RecvThreadProc);clt->OutputUsage();for(;;){char Command[COMMANDMAXC];gets_s(Command);clt->ParseCommand(Command);}}catch(exception &e){printf("some thing is error:%s",e.what());return 1;}}#endif

#include "NatClient.h"int main(int argc, char* argv[]){Client_Run();return 0;}

1 0