C#实现UDP打洞 原理及代码(三)

来源:互联网 发布:Windows 10专业版64位 编辑:程序博客网 时间:2024/05/06 22:20
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="strMsg">消息内容</param>
        /// <param name="REP">接收节点</param>
        public void SendData(string strMsg,IPEndPoint REP)
        {
            byte[] byMsg = System.Text.Encoding.Default.GetBytes(strMsg.ToCharArray());
            m_udpClient.Send(byMsg, byMsg.Length, REP);
        }


        /// <summary>
        /// 发送消息,服务器专用
        /// </summary>
        /// <param name="strMsg">消息内容</param>
        /// <param name="REP">接收节点</param>
        private void ServerSendData(string strMsg,IPEndPoint REP)
        {
            byte[] byMsg = System.Text.Encoding.Default.GetBytes(strMsg.ToCharArray());
            m_udpServer.Send(byMsg, byMsg.Length, REP);
        }


        /// <summary>
        /// 发送本地节点信息
        /// </summary>
        /// <param name="strLocalIP">本地IP</param>
        /// <param name="iLocalPort">本地端口</param>
        public void SendLocalPoint(string strLocalIP, int iLocalPort,IPEndPoint REP)
        {
            string strLocalPoint = "/x01/x02" + strLocalIP + ":" + iLocalPort.ToString() + "/x02/x01";
            SendData(strLocalPoint, REP);
        }


        /// <summary>
        /// 同时向指定的终端(包括公共终端和私有终端)打洞
        /// </summary>
        /// <param name="pubEndPoint">公共终端</param>
        /// <param name="prEndPoint">私有终端</param>
        /// <returns>打洞成功返回true,否则返回false</returns>
        public void StartBurrowTo(IPEndPoint pubEndPoint, IPEndPoint prEndPoint)
        {
            Thread burrowThread = new Thread(new ThreadStart(BurrowProc));
            m_toEndPoint.m_privateEndPoint = prEndPoint;
            m_toEndPoint.m_publicEndPoint = pubEndPoint;
            burrowThread.Start();
        }

       
        /// <summary>
        /// 打洞线程
        /// </summary>
        private void BurrowProc()
        {
            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("打洞失败!");////////////
        }
        

        /// <summary>
        /// 转发打洞请求消息,在服务器端使用
        /// </summary>
        /// <param name="strSrcPrEndpoint">请求转发的源私有终端</param>
        /// <param name="strSrcPubEndPoint">请求转发的源公共终端</param>
        /// <param name="REP">转发消息到达的目的终端</param>
        public void SendBurrowRequest(string strSrcPrEndpoint, string strSrcPubEndPoint,IPEndPoint REP)
        {
            string strBurrowMsg = "/x04/x07" + strSrcPrEndpoint + " " + strSrcPubEndPoint + "/x07/x04";
            ServerSendData(strBurrowMsg, REP);
        }


        /// <summary>
        /// 检查字符串中的命令
        /// </summary>
        private void CheckCommand()
        {
            int nPos;
            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);
        }