C#实现P2P之UDP穿透NAT及其原理讲解1
2009-04-21 20:48 观海看云 阅读(2035) 评论(0) 编辑 收藏 举报 { using System; using System.Net; using System.Net.Sockets; using System.Threading; using P2P.WellKnown; /// <summary> /// AppClass 的摘要说明。 /// </summary> public class AppClass { public static void Main() { Server server = new Server(); try { server.Start(); Console.ReadLine(); server.Stop(); } catch { } } } /// <summary> /// Server 的摘要说明。 /// </summary> public class Server { private UdpClient server; private UserCollection userList; private Thread serverThread; private IPEndPoint remotePoint; public Server() { userList = new UserCollection(); remotePoint = new IPEndPoint(IPAddress.Any, 0); serverThread = new Thread(new ThreadStart(Run)); } public void Start() { try { server = new UdpClient(P2PConsts.SRV_PORT); serverThread.Start(); Console.WriteLine("P2P Server started, waiting client connect..."); } catch(Exception exp) { Console.WriteLine("Start P2P Server error: " + exp.Message); throw exp; } } public void Stop() { Console.WriteLine("P2P Server stopping..."); try { serverThread.Abort(); server.Close(); Console.WriteLine("Stop OK."); } catch(Exception exp) { Console.WriteLine("Stop error: " + exp.Message); throw exp; } } private void Run() { byte[] buffer = null; while (true) { byte[] msgBuffer = server.Receive(ref remotePoint); try { object msgObj = FormatterHelper.Deserialize(msgBuffer); Type msgType = msgObj.GetType(); if (msgType == typeof(P2P.WellKnown.C2S.LoginMessage)) { // 转换接受的消息 P2P.WellKnown.C2S.LoginMessage lginMsg = (P2P.WellKnown.C2S.LoginMessage)msgObj; Console.WriteLine("has an user login: {0}", lginMsg.UserName); // 添加用户到列表 IPEndPoint userEndPoint = new IPEndPoint(remotePoint.Address, remotePoint.Port); User user = new User(lginMsg.UserName, userEndPoint); userList.Add(user); // 发送应答消息 P2P.WellKnown.S2C.GetUsersResponseMessage usersMsg = new P2P.WellKnown.S2C.GetUsersResponseMessage(userList); buffer = FormatterHelper.Serialize(usersMsg); server.Send(buffer, buffer.Length, remotePoint); } else if (msgType == typeof(P2P.WellKnown.C2S.LogoutMessage)) { // 转换接受的消息 P2P.WellKnown.C2S.LogoutMessage lgoutMsg = (P2P.WellKnown.C2S.LogoutMessage)msgObj; Console.WriteLine("has an user logout: {0}", lgoutMsg.UserName); // 从列表中删除用户 User lgoutUser = userList.Find(lgoutMsg.UserName); if (lgoutUser != null) { userList.Remove(lgoutUser); } } else if (msgType == typeof(P2P.WellKnown.C2S.TranslateMessage)) { // 转换接受的消息 P2P.WellKnown.C2S.TranslateMessage transMsg = (P2P.WellKnown.C2S.TranslateMessage)msgObj; Console.WriteLine("{0}(1) wants to p2p {2}", remotePoint.Address.ToString(), transMsg.UserName, transMsg.ToUserName); // 获取目标用户 User toUser = userList.Find(transMsg.ToUserName); // 转发Purch Hole请求消息 if (toUser == null) { Console.WriteLine("Remote host {0} cannot be found at index server", transMsg.ToUserName); } else { P2P.WellKnown.S2C.SomeOneCallYouMessage transMsg2 = new P2P.WellKnown.S2C.SomeOneCallYouMessage(remotePoint); buffer = FormatterHelper.Serialize(transMsg); server.Send(buffer, buffer.Length, toUser.NetPoint); } } else if (msgType == typeof(P2P.WellKnown.C2S.GetUsersMessage)) { // 发送当前用户信息到所有登录客户 P2P.WellKnown.S2C.GetUsersResponseMessage srvResMsg = new P2P.WellKnown.S2C.GetUsersResponseMessage(userList); buffer = FormatterHelper.Serialize(srvResMsg); foreach(User user in userList) { server.Send(buffer, buffer.Length, user.NetPoint); } } Thread.Sleep(500); } catch{} } } } } { using System; using System.Net; using System.Net.Sockets; using System.Threading; using P2P.WellKnown; /// <summary> /// AppClass 的摘要说明。 /// </summary> public class AppClass { public static void Main() { Client client = new Client("202.96.134.103"); client.ConnectToServer("myname", "mypassword"); client.Start(); Console.WriteLine("test arguments"); while (true) { string str = Console.ReadLine(); client.PaserCommand(str); } } } /// <summary> /// Client 的摘要说明。 /// </summary> public class Client : IDisposable { private const int MAXRETRY = 10; private UdpClient client; private IPEndPoint hostPoint; private IPEndPoint remotePoint; private UserCollection userList; private string myName; private bool ReceivedACK; private Thread listenThread; public Client(string serverIP) { ReceivedACK = false; remotePoint = new IPEndPoint(IPAddress.Any, 0); hostPoint = new IPEndPoint(IPAddress.Parse(serverIP), P2PConsts.SRV_PORT); client = new UdpClient(); userList = new UserCollection(); listenThread = new Thread(new ThreadStart(Run)); } public void Start() { if (this.listenThread.ThreadState==ThreadState.Unstarted) { this.listenThread.Start(); Console.WriteLine("You can input you command:"n"); Console.WriteLine("Command Type:""send"",""exit"",""getu"""); Console.WriteLine("Example : send Username Message"); Console.WriteLine(" exit"); Console.WriteLine(" getu"); } } public void ConnectToServer(string userName, string password) { myName = userName; // 发送登录消息到服务器 P2P.WellKnown.C2S.LoginMessage lginMsg = new P2P.WellKnown.C2S.LoginMessage(userName, password); byte[] buffer = FormatterHelper.Serialize(lginMsg); client.Send(buffer, buffer.Length, hostPoint); // 接受服务器的登录应答消息 buffer = client.Receive(ref remotePoint); P2P.WellKnown.S2C.GetUsersResponseMessage srvResMsg = (P2P.WellKnown.S2C.GetUsersResponseMessage)FormatterHelper.Deserialize(buffer); // 更新用户列表 userList.Clear(); foreach(User user in srvResMsg.UserList) { userList.Add(user); } this.DisplayUsers(userList); } /// <summary> /// 这是主要的函数:发送一个消息给某个用户(C) /// 流程:直接向某个用户的外网IP发送消息,如果此前没有联系过 /// 那么此消息将无法发送,发送端等待超时。 /// 超时后,发送端将发送一个请求信息到服务端,要求服务端发送 /// 给客户C一个请求,请求C给本机发送打洞消息 /// *以上流程将重复MAXRETRY次 /// </summary> /// <param name="toUserName">对方用户名</param> /// <param name="message">待发送的消息</param> /// <returns></returns> private bool SendMessageTo(string toUserName, string message) { User toUser = userList.Find(toUserName); if (toUser == null) { return false; } for (int i=0; i<MAXRETRY; i++) { P2P.WellKnown.P2P.WorkMessage workMsg = new P2P.WellKnown.P2P.WorkMessage(message); |