//UDPP2PSock.cs
usingSystem;usingSystem.Collections.Generic;usingSystem.Text;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Threading;usingP2PWellKnown;namespaceUDPP
{///
///UDPP2P套接字管理类///
public classUDPP2PSock
{///
///用户登录事件///
public eventUdpUserLogInDelegate OnUserLogInU;///
///一般UDP消息事件///
public eventUdpMessageDelegate OnSockMessageU;///
///初始化一个新连接事件///
public eventUdpNewConnectDelegate OnNewConnectU;///
///UDP服务器///
privateUdpClient m_udpServer;///
///UDP客户端///
privateUdpClient m_udpClient;///
///服务器实际上在本地机器上监听的///端口,用于当一台计算机上同时启///动两个可两以上服务器进程时,标///识不同的服务器进程///
private intm_iMyServerPort;///
///客户端在本地机器上实际使用的端口,///用于当一台计算机上同时有两个或两///个以上客户端进程在运行时,标识不///同的客户端进程///
private intm_iMyClientPort;///
///标识是否已成功创服务器///
private boolm_bServerCreated;///
///标识是否已成功创建客户端///
private boolm_bClientCreated;///
///服务器使用的线程///
privateThread m_serverThread;///
///客户端使用的线程///
privateThread m_clientThread;///
///打洞线程///
//private Thread m_burrowThread;
///
///远端节点///
privateIPEndPoint m_remotePoint;///
///当前进程作为客户端的公共终端///
private stringm_strMyPublicEndPoint;///
///当前进程作为客户端的私有终端///
private stringm_strMyPrivateEndPoint;///
///用于接受信息的 StringBuilder实例///
private StringBuilder m_sbResponse = newStringBuilder();///
///P2P打洞时标识是否收到回应消息///
private bool m_bRecvAck = false;///
///请求向其方向打洞的私有终端///
privateIPEndPoint m_requestPrivateEndPoint;///
///请求向其方向打洞的公共终端///
privateIPEndPoint m_requestPublicEndPoint;///
///打洞消息要发向的节点///
privateToEndPoint m_toEndPoint;///
///用于标识是否已经和请求客户端建立点对连接///
//private bool m_bHasConnected=false ;
///
///创建服务器或客户端的最大尝试///次数,为(65536-60000),防止///因不能创建而限入死循环或使用///无效端口///
private const int MAX_CREATE_TRY = 5536;///
///打洞时尝试连接的最大尝试次数///
private const int MAX_CONNECT_TRY = 10;///
///构造函数,初始化UDPP2P实例///
publicUDPP2PSock()
{
m_iMyServerPort=P2PConsts.UDP_SRV_PORT;
m_iMyClientPort= 60000;
m_bClientCreated= false;
m_bServerCreated= false;
m_toEndPoint= newToEndPoint();
m_serverThread= new Thread(newThreadStart(RunUDPServer));
m_clientThread= new Thread(newThreadStart(RunUDPClient));//m_burrowThread = new Thread(new ThreadStart(BurrowProc));
}///
///创建UDP 服务器///
public voidCreateUDPSever()
{int iTryNum = 0;//开始尝试创建服务器
while (!m_bServerCreated && iTryNum
{try{
m_udpServer= newUdpClient(m_iMyServerPort);
m_bServerCreated= true;
}catch{
m_iMyServerPort++;
iTryNum++;
}
}//创建失败,抛出异常
if (!m_bServerCreated && iTryNum ==MAX_CREATE_TRY)
{throw new Exception("创建服务器尝试失败!");
}
m_serverThread.Start();
}///
///创建UDP客户端///
///
///
public void CreateUDPClient(string strServerIP, intiServerPort)
{int iTryNum = 0;//开始尝试创建服务器
while (!m_bClientCreated && iTryNum
{try{
m_udpClient= newUdpClient(m_iMyClientPort);
m_bClientCreated= true;string strIPAddress = (System.Net.Dns.GetHostAddresses("localhost")[0]).ToString();
m_strMyPrivateEndPoint= strIPAddress + ":" +m_iMyClientPort.ToString();
}catch{
m_iMyClientPort++;
iTryNum++;
}
}//创建失败,抛出异常
if (!m_bClientCreated && iTryNum ==MAX_CREATE_TRY)
{throw new Exception("创建客户端尝试失败!");
}
IPEndPoint hostPoint= newIPEndPoint(IPAddress.Parse(strServerIP), iServerPort);string strLocalIP = (System.Net.Dns.GetHostAddresses("localhost"))[0].ToString();
SendLocalPoint(strLocalIP, m_iMyClientPort, hostPoint);
m_clientThread.Start();
}///
///运行UDP 服务器///
private voidRunUDPServer()
{while (true)
{byte[] msgBuffer = m_udpServer.Receive(refm_remotePoint);
m_sbResponse.Append(System.Text.Encoding.Default.GetString(msgBuffer));
CheckCommand();
Thread.Sleep(10);
}
}///
///运行UDP客户端///
private voidRunUDPClient()
{while (true)
{byte[] msgBuffer = m_udpClient.Receive(refm_remotePoint);
m_sbResponse.Append(System.Text.Encoding.Default.GetString(msgBuffer));
CheckCommand();
Thread.Sleep(10);
}
}///
///销毁UDP 服务器///
public voidDisposeUDPServer()
{
m_serverThread.Abort();
m_udpServer.Close();
}///
///销毁UDP客房端///
public voidDisposeUDPClient()
{
m_clientThread.Abort();
m_udpClient.Close();
}///
///发送消息///
///
///
public void SendData(stringstrMsg, IPEndPoint REP)
{byte[] byMsg =System.Text.Encoding.Default.GetBytes(strMsg.ToCharArray());
m_udpClient.Send(byMsg, byMsg.Length, REP);
}///
///发送消息,服务器专用///
///
///
private void ServerSendData(stringstrMsg, IPEndPoint REP)
{byte[] byMsg =System.Text.Encoding.Default.GetBytes(strMsg.ToCharArray());
m_udpServer.Send(byMsg, byMsg.Length, REP);
}///
///发送本地节点信息///
///
///
public void SendLocalPoint(string strLocalIP, intiLocalPort, IPEndPoint REP)
{string strLocalPoint = "\x01\x02" + strLocalIP + ":" + iLocalPort.ToString() + "\x02\x01";
SendData(strLocalPoint, REP);
}///
///同时向指定的终端(包括公共终端和私有终端)打洞///
///
///
/// 打洞成功返回true,否则返回false
public voidStartBurrowTo(IPEndPoint pubEndPoint, IPEndPoint prEndPoint)
{
Thread burrowThread= new Thread(newThreadStart(BurrowProc));
m_toEndPoint.m_privateEndPoint=prEndPoint;
m_toEndPoint.m_publicEndPoint=pubEndPoint;
burrowThread.Start();
}///
///打洞线程///
private voidBurrowProc()
{
IPEndPoint prEndPoint=m_toEndPoint.m_privateEndPoint;
IPEndPoint pubEndPoint=m_toEndPoint.m_publicEndPoint;int j = 0;for (int i = 0; i < MAX_CONNECT_TRY; i++)
{
SendData("\x01\x07\x07\x01", prEndPoint);
SendData("\x01\x07\x07\x01", pubEndPoint);//等待接收线程标记修改
for (j = 0; j < MAX_CONNECT_TRY; j++)
{if(m_bRecvAck)
{
m_bRecvAck= false;
SendData("\x01\x07\x07\x01", prEndPoint);
Thread.Sleep(50);
SendData("\x01\x07\x07\x01", pubEndPoint);
UDPSockEventArgs args= new UDPSockEventArgs("");
args.RemoteEndPoint=pubEndPoint;if (OnNewConnectU != null)
{
OnNewConnectU(this, args);
}//Thread .Sleep (System .Threading.Timeout .Infinite );
return;
}else{
Thread.Sleep(100);
}
}//如果没有收到目标主机的回应,表明本次打//洞尝试失败,等待100毫秒后尝试下一次打洞
Thread.Sleep(100);
}//MAX_CONNECT_TRY 尝试都失败,表明打洞失败,抛出异常//throw new Exception(" 打洞失败!");
System.Windows.Forms.MessageBox.Show("打洞失败!");}///
///转发打洞请求消息,在服务器端使用///
///
///
///
public void SendBurrowRequest(string strSrcPrEndpoint, stringstrSrcPubEndPoint, IPEndPoint REP)
{string strBurrowMsg = "\x04\x07" + strSrcPrEndpoint + " " + strSrcPubEndPoint + "\x07\x04";
ServerSendData(strBurrowMsg, REP);
}///
///检查字符串中的命令///
private voidCheckCommand()
{intnPos;string strCmd =m_sbResponse.ToString();//如果接收远端用户名
if ((nPos = strCmd.IndexOf("\x01\x02")) > -1)
{
ReceiveName(strCmd, nPos);//反馈公共终给端远端主机
string strPubEPMsg = "\x03\x07" + m_remotePoint.ToString() + "\x07\x03";
SendData(strPubEPMsg, m_remotePoint);return;
}//如果接收我的公共终端
if ((nPos = strCmd.IndexOf("\x03\x07")) > -1)
{
ReceiveMyPublicEndPoint(strCmd, nPos);return;
}//如果是打洞请求消息
if ((nPos = strCmd.IndexOf("\x04\x07")) > -1)
{
ReceiveAndSendAck(strCmd, nPos);return;
}//如果是打洞回应消息
if ((nPos = strCmd.IndexOf("\x01\x07")) > -1)
{
m_bRecvAck= true;int nPos2 = strCmd.IndexOf("\x07\x01");if (nPos2 > -1)
{
m_sbResponse.Remove(nPos, nPos2- nPos + 2);
}return;
}//一般聊天消息
m_sbResponse.Remove(0, strCmd.Length);
RaiseMessageEvent(strCmd);
}///
///接收远端用户名///
///
///
private void ReceiveName(string strCmd, intnPos)
{int nPos2 = strCmd.IndexOf("\x02\x01");if (nPos2 == -1)
{return;
}
m_sbResponse.Remove(nPos, nPos2- nPos + 2);string strUserName = strCmd.Substring(nPos + 2, nPos2 - nPos - 2);
UDPSockEventArgs e= new UDPSockEventArgs("");
e.RemoteUserName=strUserName;
e.RemoteEndPoint=m_remotePoint;//触发用户登录事件
if (OnUserLogInU != null)
{
OnUserLogInU(this, e);
}
}///
///接收打洞请求的消息并发送回应///
///
///
private void ReceiveAndSendAck(string strCmd, intnPos)
{int nPos2 = strCmd.IndexOf("\x07\x04");if (nPos2 == -1)
{return;
}
m_sbResponse.Remove(nPos, nPos2- nPos + 2);string strBurrowMsg = strCmd.Substring(nPos + 2, nPos2 - nPos - 2);string[] strSrcPoint = strBurrowMsg.Split(' ');//分析控制字符串包含的节点信息
string[] strPrEndPoint = strSrcPoint[0].Split(':');string[] strPubEndPoint = strSrcPoint[1].Split(':');
m_requestPrivateEndPoint= new IPEndPoint(IPAddress.Parse(strPrEndPoint[0]), int.Parse(strPrEndPoint[1]));
m_requestPublicEndPoint= new IPEndPoint(IPAddress.Parse(strPubEndPoint[0]), int.Parse(strPubEndPoint[1]));//向请求打洞终端的方向打洞
StartBurrowTo(m_requestPublicEndPoint, m_requestPrivateEndPoint);
}///
///接收我的公共终端///
///
///
private void ReceiveMyPublicEndPoint(string strCmd, intnPos)
{int nPos2 = strCmd.IndexOf("\x07\x03");if (nPos2 == -1)
{return;
}
m_sbResponse.Remove(nPos, nPos2- nPos + 2);
m_strMyPublicEndPoint= strCmd.Substring(nPos + 2, nPos2 - nPos - 2);
}///
///触发一般UDP消息事件///
///
private void RaiseMessageEvent(stringstrMsg)
{
UDPSockEventArgs args= new UDPSockEventArgs("");
args.SockMessage=strMsg;
args.RemoteEndPoint=m_remotePoint;if (OnSockMessageU != null)
{
OnSockMessageU(this, args);
}
}///
///获取当前进程作为客户端的公共终端///
public stringMyPublicEndPoint
{get{returnm_strMyPublicEndPoint;
}
}///
///获取当前进程作为客户端的私有终端///
public stringMyPrivateEndPoint
{get{returnm_strMyPrivateEndPoint;
}
}
}///
///保存打洞消息要发向的节点信息///
classToEndPoint
{///
///私有节点///
publicIPEndPoint m_privateEndPoint;///
///公共节点///
publicIPEndPoint m_publicEndPoint;
}
}