代码改变世界

C#实现P2P之UDP穿透NAT及其原理讲解1

2009-04-21 20:48  观海看云  阅读(2009)  评论(0编辑  收藏  举报



2. P2Pserver
namespace P2P.P2PServer
{

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{}

}

}

}
}


3. P2Pclient
namespace P2P.P2PClient
{

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);

byte[] buffer = FormatterHelper.Serialize(workMsg);

client.Send(buffer, buffer.Length, toUser.NetPoint);



// 等待接收线程将标记修改

for (int j=0; j<10; j++)

{

if (this.ReceivedACK)

{

this.ReceivedACK = false;

return true;

}

else

{

Thread.Sleep(300);

}

}

// 没有接收到目标主机的回应,认为目标主机的端口映射没有

// 打开,那么发送请求信息给服务器,要服务器告诉目标主机

// 打开映射端口(UDP打洞)

P2P.WellKnown.C2S.TranslateMessage transMsg = new P2P.WellKnown.C2S.TranslateMessage(myName, toUserName);

buffer = FormatterHelper.Serialize(transMsg);

client.Send(buffer, buffer.Length, hostPoint);

// 等待对方先发送信息


Thread.Sleep(100);

}

return false;

}


public void PaserCommand(string cmdstring)

{

cmdstring = cmdstring.Trim();

string[] args = cmdstring.Split(new char[]{' '});

if (args.Length > 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<args.Length; i++)


{

if (args == "") message += " ";

else message += args;

}

if (this.SendMessageTo(toUserName, message))

{

Console.WriteLine("Send OK!");

}

else

Console.WriteLine("Send Failed!");

}


}

else if (string.Compare(args[0], "getu", true) == 0)

{

P2P.WellKnown.C2S.GetUsersMessage getUserMsg = new P2P.WellKnown.C2S.GetUsersMessage(myName);

byte[] buffer = FormatterHelper.Serialize(getUserMsg);

client.Send(buffer, buffer.Length, hostPoint);

}

else

{

Console.WriteLine("Unknown command {0}", cmdstring);

}

}

}


private void DisplayUsers(UserCollection users)

{

foreach (User user in users)

{

Console.WriteLine("Username: {0}, IP:{1}, Port:{2}", user.UserName, user.NetPoint.Address.ToString(), user.NetPoint.Port);

}

}


private void Run()

{

byte[] buffer;

while (true)

{

buffer = client.Receive(ref remotePoint);

object msgObj = FormatterHelper.Deserialize(buffer);


Type msgType = msgObj.GetType();

if (msgType == typeof(P2P.WellKnown.S2C.GetUsersResponseMessage))

{

// 转换消息

P2P.WellKnown.S2C.GetUsersResponseMessage usersMsg = (P2P.WellKnown.S2C.GetUsersResponseMessage)msgObj;

// 更新用户列表

userList.Clear();

foreach(User user in usersMsg.UserList)

{

userList.Add(user);

}

this.DisplayUsers(userList);

}

else if (msgType == typeof(P2P.WellKnown.S2C.SomeOneCallYouMessage))

{

// 转换消息

P2P.WellKnown.S2C.SomeOneCallYouMessage purchReqMsg = (P2P.WellKnown.S2C.SomeOneCallYouMessage)msgObj;

// 发送打洞消息到远程主机

P2P.WellKnown.P2P.TrashMessage trashMsg = new P2P.WellKnown.P2P.TrashMessage();

buffer = FormatterHelper.Serialize(trashMsg);

client.Send(buffer, buffer.Length, purchReqMsg.RemotePoint);

}

else if (msgType == typeof(P2P.WellKnown.P2P.WorkMessage))

{

// 转换消息

P2P.WellKnown.P2P.WorkMessage workMsg = (P2P.WellKnown.P2P.WorkMessage)msgObj;

Console.WriteLine("Receive a message: {0}", workMsg.Message);

// 发送应答消息

P2P.WellKnown.P2P.ACKMessage ackMsg = new P2P.WellKnown.P2P.ACKMessage();

buffer = FormatterHelper.Serialize(ackMsg);

client.Send(buffer, buffer.Length, remotePoint);

}

else if (msgType == typeof(P2P.WellKnown.P2P.ACKMessage))

{

this.ReceivedACK = true;

}

else if (msgType == typeof(P2P.WellKnown.P2P.TrashMessage))

{

Console.WriteLine("Recieve a trash message");

}

Thread.Sleep(100);

}

}

#region IDisposable 成员


public void Dispose()

{

try

{

this.listenThread.Abort();

this.client.Close();

}

catch

{}

}


#endregion

}
}