[Open-source download] c # chat program breeze IM Version 2 adds P2P communication to the LAN,
Happy New Year
Some people have been asking P2P-related questions. Recently, on the basis of breeze IM, we have implemented P2P communication and shared it with everyone. I hope you can criticize and correct it.
Source code download (only source code is included, and no plug-in advertisement is included) The database download database is the same as the first version.
We know that in network communication, if all communications are forwarded through the server, it will increase the burden on the server. If P2P is implemented, the client can communicate directly, for example, when chatting or transferring files, instead of passing through the server, direct communication between clients will effectively reduce the burden on the server and improve the program efficiency.
The related P2P in this section refers to the P2P implemented in the LAN through the TCP protocol. P2P in the Wan is not involved at the moment.
This Demo is based on the open-source communication framework networkComms2.3.1 from the UK.
Working principle-Establish a P2P channel between clients through the server, and then the communication between clients can be detached from the server
The process is as follows:
The internal communication mechanism of the NetworkComms communication framework makes P2P communication very simple.
(1 ):Server start listening
(2 ):Client, start to connect to the server, and then start to listen, actually become a server. During the connection, the system randomly assigns a port to the client to complete communication with the server. After the connection is complete, we get the client IP address and the port for communicating with the server. The client listens on this port. That is to say, each client listens, which has all the characteristics of being a server.
Simulation code:
ConnectionInfo connInfo = new ConnectionInfo ("Server IP", "server port ");
// Connection between the client and the server newTcpConnection = TCPConnection. GetConnection (connInfo );
// After the client successfully connects to the server, it starts to listen to the local port, which is also known as the server TCPConnection. StartListening (connInfo. LocalEndPoint) that can be listened );
(3 ):Each client needs to maintain a "P2P communication connection" table
We use a static class for implementation. For details, refer to the Common class.
// Store the user ID and corresponding connection references in the dictionary
public static Dictionary<string, Connection> UserConnList = new Dictionary<string, Connection>();
Public static void AddUserConn (string userID, Connection conn) {lock (dictLocker) {if (UserConnList. containsKey (userID) {UserConnList. remove (userID);} UserConnList. add (userID, conn) ;}} public static bool ContainsUserConn (string userID) {lock (dictLocker) {if (UserConnList. containsKey (userID) {return true;} else {return false ;}} public static Connection GetUserConn (string userID) {lock (dictLocker) {if (UserConnList. containsKey (userID) {return UserConnList [userID];} else {return null ;}} public static void RemoveUserConn (string userID) {lock (dictLocker) {if (UserConnList. containsKey (userID) {UserConnList. remove (userID );}}}Related Operations
(4 ):After successfully logging on to the client, obtain the local endpoints (IP addresses and ports) of all online client users from the server (that is, the endpoints of the listener on other clients in step 1) and connect them.
Client A connects to other clients one by one. After the connection is successful, client a adds the user ID of the other party and references the connection to the local P2P channel dictionary.
Client A of 2 sends a message with the Message Type "setupP2PMessage" to the other party, so that the other party can add corresponding recordsPeer P2P dictionaryMedium
When Client A is connected to other users, Client A is "client" and other clients are "server". Therefore, in the P2P channel, one end is the "client" and the other end is the "server ".
In combination with the NetworkComms communication framework, this conceptual distinction does not affect P2P channel communication.
When Client A communicates with other clients, either as a "client" or as a "server", you only need to have a TCP persistent connection with the other client.
"4" is a "server" built by communication between clients. It has all the functions of a Real Server and performs "Heartbeat detection" and "connection" maintenance.
The following code: After a client logs in, it obtains and connects all online users. After the connection is complete, it sends a "SetupP2PMessag" message to the other party. Through this process, both parties will establish "P2P connections.
Private void GetP2PInfo (){
// Obtain information about all online users (User ID, corresponding local endpoint) from the server, the listener has started on this endpoint.) IList <UserIDEndPoint> userInfoList = Common. tcpConn. sendReceiveObject <IList <UserIDEndPoint> ("GetP2PInfo", "ResP2pInfo", 5000, "GetP2P"); // traverses all online users foreach (UserIDEndPoint userInfo in userInfoList) {try {if (userInfo. userID! = Common. UserID ){
// Write the log LogInfo in the root directory. logMessage ("prepare to create" + userInfo. userID + ":" + userInfo. IPAddress + ":" + userInfo. port. toString (), "P2PInfo"); // create a connection information class ConnectionInfo connInfo = new ConnectionInfo (userInfo. IPAddress, userInfo. port );
// Treat the client of the other party as the server Connection newTcpConnection = TCPConnection. getConnection (connInfo); Common. addUserConn (userInfo. userID, newTcpConnection); SetUpP2PContract contract = new SetUpP2PContract (); contract. userID = Common. userID; // send a message to the peer user after the P2p channel is connected, so that the Peer user can establish a P2P channel newTcpConnection after receiving the message. sendObject ("SetupP2PMessage", contract); // write logs to the root directory
LogInfo. logMessage ("created" + userInfo. userID + ":" + userInfo. IPAddress + ":" + userInfo. port. toString (), "P2PInfo") ;}} catch {}}}
In the complete code, we create the relevant p2pchannel and insert the p2pinfo.txt file under the program file to facilitate observation of the establishment of the P2P Message channel. And send messages through P2P Channels
(5 ):Send messages through P2P Channels
When the client sends a message, check whether there is a P2P channel with the other party. If a message is sent through a P2P connection, otherwise the message is sent through the server.
For example, when sending a chat message, check whether a p2p channel exists.
Private void chatcontrol1_intosend (string content) {this. chatControl1.ShowMessage (Common. userName, DateTime. now, content, true); // obtain the P2P channel Connection p2pConnection = Common from the client Common. getUserConn (this. friendID); if (p2pConnection! = Null) {ChatContract chatContract = new ChatContract (); chatContract. userID = Common. userID; chatContract. userName = Common. userName; chatContract. destUserID = this. friendID; chatContract. destUserName = this. friendID; chatContract. content = content; chatContract. sendTime = DateTime. now; p2pConnection. sendObject ("ClientChatMessage", chatContract); this. chatControl1.Focus (); LogInfo. logMessage ("send messages through p2p channel, current user ID is" + Common. userID, "P2PINFO");} else {ChatContract chatContract = new ChatContract (); chatContract. userID = Common. userID; chatContract. userName = Common. userName; chatContract. destUserID = this. friendID; chatContract. destUserName = this. friendID; chatContract. content = content; chatContract. sendTime = DateTime. now; Common. tcpConn. sendObject ("ChatMessage", chatContract); this. chatControl1.Focus (); LogInfo. logMessage ("server Forwarding Message", "P2PINFO ");}}
(6)P2P channel logout
When a client is disconnected, We need to cancel it from the P2P channel of another client.
Method:
The server uses heartbeat detection to send messages to all other clients after a connection is lost.
Private void userstatenoworkflow (string userID, bool onLine) {try {// user status contract class UserStateContract userState = new UserStateContract (); userState. userID = userID; userState. onLine = onLine; IList <strong guid> allUserID; lock (syncLocker) {// obtain the user ID allUserID = new List <strong guid> (userManager. values);} // send a user's online status foreach (using guid netID in allUserID) {List <Connection> result = NetworkComms. getExistingConnection (netID, ConnectionType. TCP); if (result. count> 0 & result [0]. connectionInfo. networkIdentifier = netID) {result [0]. sendObject ("userstatenoworkflow", userState) ;}} catch (Exception ex) {LogTools. logException (ex, "MainForm. userstatenoworkflow ");}}Server code to send online or offline messages to users
Client code:
NetworkComms.AppendGlobalIncomingPacketHandler<UserStateContract>("UserStateNotify", IncomingUserStateNotify);
Private void incominguserstatenovel (PacketHeader header, Connection connection, UserStateContract userStateContract) {if (userStateContract. OnLine) {lock (syncLocker ){
// This part handles the launch of the user and has nothing to do with the P2p channel. getDicUser (userStateContract. userID ). state = OnlineState. online ;}} else {lock (syncLocker) {Common. getDicUser (userStateContract. userID ). state = OnlineState. offline; // when a user is Offline, delete the user-related p2p channel Common. removeUserConn (userStateContract. userID );}}}
I hope you will like P2P communication.