源代码 注:原文代码是用C++写的,这里仅附上C#代码 1. WellKnown公用库 namespace P2P.WellKnown { using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; /// /// P2PConsts 的摘要说明。 /// public class P2PConsts { /// /// 服务器侦听端口号 /// public const int SRV_PORT = 2280; } /// /// User 的摘要说明。 /// [Serializable] public class User { protected string userName; protected IPEndPoint netPoint; public User(string UserName, IPEndPoint NetPoint) { this.userName = userName; this.netPoint = NetPoint; } public string UserName { get { return userName; } } public IPEndPoint NetPoint { get { return netPoint; } set { netPoint = value;} } } /// /// UserCollection 的摘要说明。 /// [Serializable] public class UserCollection : CollectionBase { public void Add(User user) { InnerList.Add(user); } public void Remove(User user) { InnerList.Remove(user); } public User this[int index] { get { return (User)InnerList[index]; } } public User Find(string userName) { foreach(User user in this) { if (string.Compare(userName, user.UserName, true) == 0) { return user; } } return null; } } /// /// FormatterHelper 序列化,反序列化消息的帮助类 /// public class FormatterHelper { public static byte[] Serialize(object obj) { BinaryFormatter binaryF = new BinaryFormatter(); MemoryStream ms = new MemoryStream(1024*10); binaryF.Serialize(ms, obj); ms.Seek(0, SeekOrigin.Begin); byte[] buffer = new byte[(int)ms.Length]; ms.Read(buffer, 0, buffer.Length); ms.Close(); return buffer; } public static object Deserialize(byte[] buffer) { BinaryFormatter binaryF = new BinaryFormatter(); MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, false); object obj = binaryF.Deserialize(ms); ms.Close(); return obj; } } /// /// Message base class /// [System.Serializable] public abstract class MessageBase { } // Message from Client to Server namespace C2S { /// /// 客户端发送到服务器的消息基类 /// public abstract class CSMessage : MessageBase { private string userName; protected CSMessage(string anUserName) { userName = anUserName; } public string UserName { get { return userName; } } } /// /// 用户登录消息 /// public class LoginMessage : CSMessage { private string password; public LoginMessage(string userName, string password) : base(userName) { this.password = password; } public string Password { get { return password; } } } /// /// 用户登出消息 /// public class LogoutMessage : CSMessage { public LogoutMessage(string userName) : base(userName) {} } /// /// 请求用户列表消息 /// public class GetUsersMessage : CSMessage { public GetUsersMessage(string userName) : base(userName) {} } /// /// 请求Purch Hole消息 /// public class TranslateMessage : CSMessage { protected string toUserName; public TranslateMessage(string userName, string toUserName) : base(userName) { this.toUserName = toUserName; } public string ToUserName { get { return this.toUserName; } } } } // Message from server to the client namespace S2C { /// /// 服务器发送到客户端消息基类 /// public abstract class SCMessage : MessageBase {} /// /// 请求用户列表应答消息 /// public class GetUsersResponseMessage : SCMessage { private UserCollection userList; public GetUsersResponseMessage(UserCollection users) { this.userList = users; } public UserCollection UserList { get { return userList; } } } /// /// 转发请求Purch Hole消息 /// public class SomeOneCallYouMessage : SCMessage { protected System.Net.IPEndPoint remotePoint; public SomeOneCallYouMessage(System.Net.IPEndPoint point) { this.remotePoint = point; } public System.Net.IPEndPoint RemotePoint { get { return remotePoint; } } } } // Message from peer to the peer namespace P2P { /// /// 点对点消息基类 /// public abstract class PPMessage : MessageBase {} /// /// 测试消息 /// public class WorkMessage : PPMessage { private string message; public WorkMessage(string msg) { message = msg; } public string Message { get { return message; } } } /// /// 测试应答消息 /// public class ACKMessage : PPMessage { } /// /// P2P Purch Hole Message /// public class TrashMessage : PPMessage {} } } 2. P2Pserver namespace P2P.P2PServer { using System; using System.Net; using System.Net.Sockets; using System.Threading; using P2P.WellKnown; /// /// AppClass 的摘要说明。 /// public class AppClass { public static void Main() { Server server = new Server(); try { server.Start(); Console.ReadLine(); server.Stop(); } catch { } } } /// /// Server 的摘要说明。 /// 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{} } } } } 3. P2Pclient namespace P2P.P2PClient { using System; using System.Net; using System.Net.Sockets; using System.Threading; using P2P.WellKnown; /// /// AppClass 的摘要说明。 /// 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); } } } /// /// Client 的摘要说明。 /// 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); } /// /// 这是主要的函数:发送一个消息给某个用户(C) /// 流程:直接向某个用户的外网IP发送消息,如果此前没有联系过 /// 那么此消息将无法发送,发送端等待超时。 /// 超时后,发送端将发送一个请求信息到服务端,要求服务端发送 /// 给客户C一个请求,请求C给本机发送打洞消息 /// *以上流程将重复MAXRETRY次 /// /// 对方用户名 /// 待发送的消息 /// private bool SendMessageTo(string toUserName, string message) { User toUser = userList.Find(toUserName); if (toUser == null) { return false; } for (int i=0; i 0) { if (string.Compare(args[0], "exit", true) == 0) { P2P.WellKnown.C2S.LogoutMessage lgoutMsg = new P2P.WellKnown.C2S.LogoutMessage(myName); byte[] buffer = FormatterHelper.Serialize(lgoutMsg); client.Send(buffer, buffer.Length, hostPoint); // do clear something here Dispose(); System.Environment.Exit(0); } else if (string.Compare(args[0], "send", true) == 0) { if (args.Length > 2) { string toUserName = args[1]; string message = ""; for(int i=2; i