P2P UDP penetration NAT principle and implementation (attachment code)

xiaoxiao2021-03-06  18

On the forum, there is often discussions on P2P principles, but discussing, rarely having substantial things (source code). Oh, here I use the source code you implement to illustrate the principle of UDP crossing NAT.

First introduce some basic concepts: NAT (Network Address Translators), Network Address Conversion: Network Address Transformation is increasing in the case where IP addresses are increasing, and its main purpose is to be able to be reused. NAT is divided into two categories, basic NAT and Napt (Network Address / Port Translators). The first NAT is a functional module running on the router. The first proposed is the basic NAT, which produces only the following fact: only a few nodes in a private network (domain) need to connect to the external network (Oh, this is proposed in the mid-1990s) . Then there is only a few nodes in this subnet that require a world's unique IP address, and the IP address of other nodes should be reused. Therefore, the basic NAT implementation is simple, and a reserved IP subnet section is used in the subnet, which is invisible to these IPs. Only a few IP addresses in the subnet can correspond to the real world unique IP address. If these nodes need to access the external network, the basic NAT is responsible for transforming the subnet IP of this node into a world unique IP and then sending out. (Basic NAT will change the original IP address in the IP package, but will not change the port in the IP package) About basic NAT can see the RFC 1631 another NAT called Napt, from the name we can also see, NAPT Not only will the IP address of the IP datagram passed through this NAT device, but also change the TCP / UDP port of the IP datagram. Basic NAT's equipment may have no more (huh, I have not seen it), Napt is the protagonist of our truly discussed. Look at the picture below: Server S1 18.181.0.31:1235 | ^ session 1 (a-s1) ^ | | 18.181.0.31:1235 | | v 155.99.25.11:62000 V | | NAT 155.99.25.11 | ^ session 1 (A- S1) ^ | | 18.181.0.31:1235 | V 10.0.0.1:0.1234 V | | Client A 10.0.0.1:1234 There is a private network 10. *. *. *, Client A is one of the computers, this The external network IP of the network's gateway (a NAT device) is 155.99.25.11 (there should be an intranet IP address, such as 10.0.0.10).

If a process in Client A (this process creates a UDP socket, this socket binding 1234 port) wants to access the 1235 port of the external network host 18.181.0.31, then what happens when the data package is passed by NAT? First, NAT will change the original IP address of this packet, change to 155.99.25.11. Then NAT will create a session for this transmission (Session is an abstract concept, if it is TCP, maybe the session starts by a SYN package, ending with a FIN package. And the first one of this IP UDP starts, end, huh, maybe it's a few minutes, maybe it's a few hours, this depends on the specific implementation) and assigns a port to this session, such as 62000, then change the source port of this packet 62000. So the data (10.0.0.1: 1234->18.181.0.0.31:1235) packaged to the Internet (155.99.25.11:62000-18.181.0.31:1235). Once NAT creates a session, NAT will remember that the 62000 port corresponds to the 1234 port of 10.0.0.1, and the data sent from 18.181.0.31 to 62000 port will be forwarded to 10.0.0.1. (Note: This is a data that is sent to 62000 port 18.181.0.31 will be forwarded, and the data sent to this port will be abandoned by NAT) so that Client A is established with Server S1 to connect.

Oh, the basics above may know many people, then the following is a key part. Take a look at the situation below: Server S1 Server S2 18.181.0.31:1235 138.76.29.7:735 | | | | ---------------------- - -------------------- | ^ session 1 (A-S1) ^ | ^ session 2 (A-S2) ^ | 18.181.0.31:1235 | | | 138.76.99.7:1235 | V 155.99.25.11:62000 V | v 155.99.25.11:62000 V | CONE NAT 155.99.25.11 | SESSION 1 (A-S1) ^ | ^ session 2 (A-S2) ^ | 18.181. 0.31: 1235 | | | 138.76.0.0.1:1234 V | v 10.0.0.1:1234 V | Client A 10.0.0.1:1234 Connecting the above example, if Client A is the original socket (binding The UDP socket of the 1234 port is then followed by another S ERVER S2 sends a UDP package, then how will this UDP package when passing Nat? There are two cases that may occur at this time, one is NAT to create a session again, and allocate a port number for this session (such as: 62001). The other is NAT to create a session again, but will not be allocated a port number, but the port number 62000 allocated. The previous NAT is called Symmetric Nat, and the latter is called CONE NAT. We expect our NAT to be the second, huh, if your NAT is just the first, then there will be a lot of P2P software failure. (It is fortunate that the vast majority of NAT belongs to the latter, namely, CONE NAT is ok, we see, the computer in the NAT and subnet is easy (NAT is equivalent to transparent, The computer of the network and the external network don't have to know the case of NAT). But if the external computer wants to access the computer in the subnet (this is also required for P2P).

So if we want to send a data to the intranet from the outside to the intra computer? First, we must play a "hole" on the NAT of the intranet (that is, we said in the NAT to establish a session). This hole cannot be hit by the outside, can only be hit by the host in the intranet. And this hole is a direction, such as a UDP package from an internal host (such as: 192.168.0.10) to the outside, such as: 219.237.60.1), then play on the NAT device of this intranet "Cave" in a direction of 219.237.60.1 (this is called UDP HOLE PUNCHING) After 219.237.60.1, it can be contacted by 192.168.0.10 through this hole. (However, other IPs cannot take advantage of this hole). Oh, now the round to our topic P2P. With the theory of the above, the host communication of the two intangnet is the last step: That is the problem of eggs or egg growth chops, no connection requests on both sides, no one knows the public network address , How can we play this hole? We need a middleman to contact these two intranet hosts. Now let's take a look at a process of P2P software, the following figure is:

Server S (219.237.60.1) | | --------------------------------------- ----- | | NAT A (Outer Network IP: 202.187.45.3) NAT B (Outer Network IP: 187.34.1.56) | (Net IP: 192.168.0.1) | (Inline IP: 192.168.0.1) | | Client A (192.168.0.20:4000) Client B (192.168.0.10:40000)

First, the Client A logs in the server, NAT A is assigned a port 60000 for this session, then the address of the Client A received by Server S is 202.187.45.3:60000, which is the outer network address of Client a. Similarly, Client B logs in Server S, NAT B is 40000 assigned to this session, then the address received by Server S is 187.34.1.56:40000. At this point, Client A and Client B can communicate with Server S. If Client A wants to send information directly to Client B, then he can get B's public network address 187.34.1.56:40000 from Server S, is it CLIENT A to send information to this address CLIENT B? Can you receive it? The answer is not, because if this is sent, NAT B will discard this information (because such information is not please, most NAT will perform discarding action). Now we need to play a hole in Nat B to 202.187.45.3 (ie the outer network address of Client a), then the Client A is sent to the information of 187.34.1.56:40000, and Client B can be received. Who is from anyone command, huh, Of course, Server S. Summarize this process: If Client A wants to send information to the Client B, the Client A sends a command to Server S, request the Server S command Client B to hoist in the direction of the client. Oh, is it very winding, but it doesn't matter, I want to think very clearly. Let's also have source code. (Hou teacher said: There is no secret 8 in front of the source code), then Client A can pass Client B The external network address is communicated with the Client B. Note: The above process is only suitable for CONE NAT. If it is Symmetric Nat, then when Client B has a hole in the CLIENT A hole has been redistributed, Client B will not know this port (if the port of Symmetric Nat is sequentially assigned Then we may guess this port number, but because there are too many factors that may lead to failure, we do not recommend this way to guess ports). Below is a source code for analog P2P chat, the process is simple, P2PServer runs on a computer with public IP, and P2PClient is running after two different NATs (note that if the two clients run after a NAT This program is likely to be normal, depending on whether your NAT supports loopback translation, see http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt, of course, this problem It can be solved by both sides to try to connect the other party's intranet IP, but this code is just to verify the principle, there is no processing of these issues), and the login computer can get the username of the first login computer, and the computer is logged over by Send UserName Message. Format to send a message. If the send is successful, you have achieved successful connection directly to the other party.

The program now supports three commands: Send, Getu, EXIT Send format: send username message feature: Send information to username getu format: Getu function: Get the current server user list EXIT format: EXIT function: Logout and server connection (server is not Automatic monitoring customer is a hanging line) code is very short, I believe it is easy to understand, if there is any problem, I can send me an email zhouiis22@sina.com or send a short message on 9CBS. At the same time, welcome this article, but hope to retain author copyright 8-). Finally, I would like to thank 9CBS users PiggyXP and SEILFER test help p2pserver.c

/ * P2P program server * * File name: P2PServer.c * * Date: 2004-5-21 * * Author: shootingstars (zhouhuis22@sina.com) * * / # pragma comment (lib, "ws2_32.lib")

#include "#include" ../proto.h"#include "../exception.h"

UserList ClientList;

VoidinSock () {wsadata wsadata;

IF (WsaStartup (MakeWord (2, 2), & WSADATA! = 0) {Printf ("Windows Sockets 2.2 Startup); throw exception (" ");} else {printf (" Using% s) / N ", wsadata.szdescription, wsadata.szsystemStatus); Printf (" With API VersionS% D.% D TO% D.% D / N / N ", Lobyte (Wsadata.wvelSION), Hibyte (Wsadata.WVersion) Lobyte (wsadata.whighveions), hibyte (wsadata.whighveions);}}

Socket MKSock (INT TYPE) {Socket Sock = Socket (AF_INET, TYPE, 0); if (SOCK <0) {Printf ("Create Socket Error"); Throw Exception ("");} Return SOCK;

StuserListNode GetUser (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; Stimentage Recvbuf; Memset (& Recvbuf, 0, Sizeof (stmessage);

// Start the primary loop. // Main loop is responsible for the following things: // 1: Read the client login and logout message, record customer list // 2: Forward customers P2P request 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 messagepe = recvbuf.IMessageType; switch (message) {copy login: {// Record this user's information to 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);

// Send a customer information that has been logged in INT nodecount = (int) clientlist.size (); sendto (primaryudp, (const char *) & nodecount, sizeof (int), 0, (const socddr *) & 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: {// Remove this customer information PRINTF ("HAS a User Logout:% S / N", Recvbuf.Message.Logoutmember.userName) UserList :: item = null; for (userlist :: item.begin (); useriterator! = Clientlist.eeriterator! = Clientlist.end (); useriterator) {ix ((* useriterator) -> username , Recvbuf.Message.logoutmember.username) == 0) {transoveiterator = Useriterator; Break;}}} (REMOVEITERATOR! = NULL) ClientList.Remove (* removeiterator); Break;} Case P2PTRANS: {// A customer wants the service to send a hole message Printf ("% s Wants to P2P% S / N", INET_NTOA (SENDER.SIN_ADDR), Recvbuf.Message.Translate.usename) ; 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), 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, Sizeof (transote); Break; Int command = getAlluser; Sendto (Primaryudp, (const char *) & command, sizeof (int), 0, (const socddr * ) & sender, sizeof (sender);

INT NodeCount = (int) ClientList.size (); sendto (primaryudp, (const char *) & nodecount, sizeof (int), 0, (const socddr *) & 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;}

/ * P2P client programs * * File name: P2PClient.c * * Date: 2004-5-21 * * Author: shootingstars (zhouhuis22@sina.com) * * /

#pragma comment (Lib, "WS2_32.LIB")

#include "windows.h" #include "../proto.h"#include" ../exception.h"#include using namespace std;

UserList ClientList;

#define commandmaxc 256 # Define MaxRetry 5

Socket primaryudp; char username [10]; char Serverip [20];

Bool recvedack;

VoidinSock () {wsadata wsadata;

IF (WsaStartup (MakeWord (2, 2), & WSADATA! = 0) {Printf ("Windows Sockets 2.2 Startup); throw exception (" ");} else {printf (" Using% s) / N ", wsadata.szdescription, wsadata.szsystemStatus); Printf (" With API VersionS% D.% D TO% D.% D / N / N ", Lobyte (Wsadata.WVersion), Lobyte (wsadata.whighversion);}}} Socket MKSock (INT TYPE) {socket Sock = Socket (AF_INET, TYPE, 0); if (SOCK <0) {Printf ("Create Socket Error" ); "");} Returni

StuserListNode GetUser (USERLIST :: Iterator Useriterator = ClientList.begin (); useriterator! = clientList.end (); useriterator) {if (Strcmp ((* useriterator) -> username), Username) == 0) Return * (* useriterator);} throw exception ("not find this user");}

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, SOCK, SIZEOF (Sendbuf), 0, (Const SockAddr *) & Remote, Sizeof (Remote));

INT usercount; int.comlen = sizeof (trans.); INT IREAD = Recvfrom (SOCK, (CHAR *) & UserCount, Sizeof (int), 0, (sockaddr *) & remote, & fromlen; if (iRead <= 0) {throw Exception ("Login error / n");} // After logging in to the server, the information COUT << "" "<< usercount <<" << "<< UserCount <<" sent by the received server is received. " ; for (int i = 0; i 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;}

/ * This is the main function: Send a message to a user (c) * process: send a message directly to the external network IP of a user, if this is not connected * So this message will not be sent, the sender waits for timeout . * Timeout, the sender will send a request information to the server. , 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 ((* useriterator) -> username) == 0) {userip = (* useriterator) -> ip; userport = true;}} IF (! finduser) Return False;

STRCPY (RealMessage, Message); For (int i = 0; i

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.Instrunglen, 0, (const socdr *) & remote, sizeof (transote)); // Waiting for the receiving thread to modify this tag for (int J = 0; J <10; J ) {if (recvEDack) Return True; Else Sleep (300);

// No response to the target host, think that the port mapping of the target host is not //, then send request information to the server, to tell the server to tell the target host // open mapping port (UDP hole) 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), (const socddr *) & Server, sizeof (server)); Sleep (100); // Waiting for the other party first send information. } Return False;

// Resolution command, temporarily only exit and send command // new Getu command, get all user void parseCommand (strn (commandline <4) {if (strncpy) {if (strncpy) {ix; (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 (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));}}

Accept message thread // DWORD WINAPI RecvThreadProc (LPVOID lpParameter) {sockaddr_in remote; int sinlen = sizeof (remote); stP2PMessage recvbuf; for (;;) {int iread = recvfrom (PrimaryUDP, (char *) & recvbuf, sizeof (recvbuf) , 0, & SINLEN); if (iRead <= 0) {Printf ("Recv Error / N"); Continue;} Switch (Recvbuf.IMessageType) {copy p2pMessage: {// receive p2p message 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 suckaddr *) & remote, sizeof (transote));} delete [] commessage;

} Case P2PSOMEONEWANTTOCALLYOU: {// command receiving holes, holes printf ( "Recv p2someonewanttocallyou data / n") to the specified IP address; 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 : {// Recons for sending messages Recvedack = true; break;} case p2ptrash: {// The hole message sent by the other party is ignored. // do Nothing ... Printf ("Recv p2ptrash / n"); Break;} case getAllUser: {Int Usercount; int.comlen = sizeof (remote); int = recvfrom (primaryudp, (char *) & usercount, sizeof (int), 0, (SockAddr *) & remote, & fromlease; f (iRead <= 0) {throw exception ("login error / n");} clientlist.clear (); cout << "have" << Usercount << "Users logined server:" << endl; for (int i = 0; i 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;}}}}}}}

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 (E.GETMESSAGE ()); Return 1;} return 0;}

/ * * * Exception class file name: Exception.h * * Date: 2004.5.5 * * Author: shootingstars (zhouhuis22@sina.com) * /

#ifndef __hzh_exception __ # define __hzh_exception__

#define exception_message_maxlen 256 # include "string.h"

Class Exception {private: char m_exceptionMaxlen [eXception_message_maxlen]; public: exception (char * msg) {strncpy (m_exceptionMessage, msg, eXception_message_maxlen);

Char * getMessage () {return m_exceptionMessage;}};

#ENDIF

/ * P2P program Transfer Protocol * * Date: 2004-5-21 * * Author: shootingstars (zhouhuis22@sina.com) * * /

#pragma overce # incrude

// Define the value of iMessageType #define login 1 # Define logout 2 # define p2ptrans 3 # Define getAllUser 4

// Server port #define server_port 2280

// Client When logging in to the message Struct StloginMessage {Char UserName [10]; CHAR Password [10];

Signing Struct StlogoutMessage {Char UserName [10];};

// Client requests another client (username) to send UDP hole messages in the direction Struct stp2ptranslate {char username [10];

// Client Strus; union _Message; stlogoutmessage logoutmensage;} message;

// Customer Node Information Struct StuserListNode {Char Username [10]; UNSIGNED IP; UNSIGNED Short Port;

// Server sent to the client Struct Stservlient {Int iMessageType; Union_Message {StuserListNode User;} message;

}

/ / ================================================================================================= // The following protocol is used for the client Communication // ====================================== # define p2pMessage 100 // Send Message #define p2pMessageack 101 // Receive a message from the message #define p2psomeOnEwantTocallyou 102 // The server sent to the client // hope this client sends a UDP tales #define p2ptrash 103 // client sending holes The receiving end should ignore this message // client to send message format struct stp2pMessage {Int iMessageType; int istringlen; // or ip address unsigned short port;

Using namespace std; typedef list userlist;

转载请注明原文地址:https://www.9cbs.com/read-44707.html

New Post(0)