源代码
注:原文代码是用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