狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

C# p2p UDP穿越NAT,UDP打洞源碼

admin
2018年2月10日 10:7 本文熱度 8157

思路如下(參照源代碼):

  1、 frmServer啟動兩個網絡偵聽,主連接偵聽,協助打洞的偵聽。

  2、 frmClientA和frmClientB分別與frmServer的主連接保持聯系。

  3、 當frmClientA需要和frmClientB建立直接的udp連接時,首先連接frmServer的協助打洞端口,并發送協助連接申請,同時在該端口號上啟動偵聽。

     4、  frmServer的協助打洞連接收到frmClientA的申請后通過主連接通知frmClientB,并將frmClientA經過NAT-A轉換后的公網IP地址和端口等信息告訴frmClientB。

  5、 frmClientB收到frmServer的連接通知后首先與frmServer的協助打洞端口連接,發送一些數據后立即斷開,目的是讓frmServer能知道frmClientB經過NAT-B轉換后的公網IP和端口號。

  6、 frmClientB嘗試與frmClientA的經過NAT-A轉換后的公網IP地址和端口進行connect,不同的路由器會有不同的結果,多數路由器對未知不請自到的SYN請求包直接丟棄而導致connect失敗,但NAT-A會紀錄此次連接的源地址和端口號,為接下來真正的連接做好了準備,這就是所謂的打洞,即frmClientB向frmClientA打了一個洞,下次frmClientA就能直接連接到frmClientB剛才使用的端口號了。

  7、 客戶端frmClientB打洞的同時在相同的端口上啟動偵聽。frmClientB在一切準備就緒以后通過與frmServer的主連接回復消息“可以了,已經準備”,frmServer在收到以后將frmClientB經過NAT-B轉換后的公網IP和端口號告訴給frmClientA。

  8、 frmClientA收到frmServer回復的frmClientB的公網IP和端口號等信息以后,開始連接到frmClientB公網IP和端口號,由于在步驟6中frmClientB曾經嘗試連接過frmClientA的公網IP地址和端口,NAT-A紀錄了此次連接的信息,所以當frmClientA主動連接frmClientB時,NAT-B會認為是合法的SYN數據,并允許通過,從而直接的udp連接建立起來了。

  • frmClientB

客戶端核心代碼:

private void Run()
        {
            try
            {
                byte[] buffer;//接受數據用
                while (true)
                {
                    buffer = _client.Receive(ref _remotePoint);//_remotePoint變量返回當前連接的用戶IP地址

                    object msgObj = ObjectSerializer.Deserialize(buffer);
                    Type msgType = msgObj.GetType();
                    DoWriteLog("接收到消息:" + msgType.ToString() + " From:" + _remotePoint.ToString());

                    if (msgType == typeof(S2C_UserListMessage))
                    {
                        // 更新用戶列表
                        S2C_UserListMessage usersMsg = (S2C_UserListMessage)msgObj;
                        _userList.Clear();

                        foreach (User user in usersMsg.UserList)
                            _userList.Add(user);

                        this.DisplayUsers(_userList);
                    }
                    else if (msgType == typeof(S2C_UserAction))
                    {
                        //用戶動作,新用戶登錄/用戶登出
                        S2C_UserAction msgAction = (S2C_UserAction)msgObj;
                        if (msgAction.Action == UserAction.Login)
                        {
                            _userList.Add(msgAction.User);
                            this.DisplayUsers(_userList);
                        }
                        else if (msgAction.Action == UserAction.Logout)
                        {
                            User user = _userList.Find(msgAction.User.UserName);
                            if (user != null) _userList.Remove(user);
                            this.DisplayUsers(_userList);
                        }
                    }
                    else if (msgType == typeof(S2C_HolePunchingMessage))
                    {
                        //接受到服務器的打洞命令
                        S2C_HolePunchingMessage msgHolePunching = (S2C_HolePunchingMessage)msgObj;

                        //NAT-B的用戶給NAT-A的用戶發送消息,此時UDP包肯定會被NAT-A丟棄,
                        //因為NAT-A上并沒有A->NAT-B的合法Session, 但是現在NAT-B上就建立了有B->NAT-A的合法session了!
                        P2P_HolePunchingTestMessage msgTest = new P2P_HolePunchingTestMessage(_LocalUserName);
                        this.SendMessage(msgTest, msgHolePunching.RemotePoint);
                    }
                    else if (msgType == typeof(P2P_HolePunchingTestMessage))
                    {
                        //UDP打洞測試消息
                        //_HoleAccepted = true;
                        P2P_HolePunchingTestMessage msgTest = (P2P_HolePunchingTestMessage)msgObj;
                        UpdateConnection(msgTest.UserName, _remotePoint);

                        //發送確認消息
                        P2P_HolePunchingResponse response = new P2P_HolePunchingResponse(_LocalUserName);
                        this.SendMessage(response, _remotePoint);
                    }
                    else if (msgType == typeof(P2P_HolePunchingResponse))
                    {
                        //_HoleAccepted = true;//打洞成功
                        P2P_HolePunchingResponse msg = msgObj as P2P_HolePunchingResponse;
                        UpdateConnection(msg.UserName, _remotePoint);

                    }
                    else if (msgType == typeof(P2P_TalkMessage))
                    {
                        //用戶間對話消息
                        P2P_TalkMessage workMsg = (P2P_TalkMessage)msgObj;
                        DoWriteLog(workMsg.Message);
                    }
                    else
                    {
                        DoWriteLog("收到未知消息!");
                    }
                }
            }
            catch (Exception ex) { DoWriteLog(ex.Message); }
        }

  • frmClientA

服務端核心代碼:

private void Run()
        {
            byte[] msgBuffer = null;

            while (true)
            {
                msgBuffer = _server.Receive(ref _remotePoint); //接受消息
                try
                {
                    //將消息轉換為對象
                    object msgObject = ObjectSerializer.Deserialize(msgBuffer);
                    if (msgObject == null) continue;

                    Type msgType = msgObject.GetType();
                    DoWriteLog("接收到消息:" + msgType.ToString());
                    DoWriteLog("From:" + _remotePoint.ToString());

                    //新用戶登錄
                    if (msgType == typeof(C2S_LoginMessage))
                    {
                        C2S_LoginMessage lginMsg = (C2S_LoginMessage)msgObject;
                        DoWriteLog(string.Format("用戶’{0}’已登錄!", lginMsg.FromUserName));

                        // 添加用戶到列表
                        IPEndPoint userEndPoint = new IPEndPoint(_remotePoint.Address, _remotePoint.Port);
                        User user = new User(lginMsg.FromUserName, userEndPoint);
                        _userList.Add(user);

                        this.DoUserChanged(_userList);

                        //通知所有人,有新用戶登錄
                        S2C_UserAction msgNewUser = new S2C_UserAction(user, UserAction.Login);
                        foreach (User u in _userList)
                        {
                            if (u.UserName == user.UserName) //如果是自己,發送所有在線用戶列表
                                this.SendMessage(new S2C_UserListMessage(_userList), u.NetPoint);
                            else
                                this.SendMessage(msgNewUser, u.NetPoint);
                        }
                    }
                    else if (msgType == typeof(C2S_LogoutMessage))
                    {
                        C2S_LogoutMessage lgoutMsg = (C2S_LogoutMessage)msgObject;
                        DoWriteLog(string.Format("用戶’{0}’已登出!", lgoutMsg.FromUserName));

                        // 從列表中刪除用戶
                        User logoutUser = _userList.Find(lgoutMsg.FromUserName);
                        if (logoutUser != null) _userList.Remove(logoutUser);

                        this.DoUserChanged(_userList);

                        //通知所有人,有用戶登出
                        S2C_UserAction msgNewUser = new S2C_UserAction(logoutUser, UserAction.Logout);
                        foreach (User u in _userList)
                            this.SendMessage(msgNewUser, u.NetPoint);
                    }
                    else if (msgType == typeof(C2S_HolePunchingRequestMessage))
                    {
                        //接收到A給B打洞的消息,打洞請求,由客戶端發送給服務器端
                        C2S_HolePunchingRequestMessage msgHoleReq = (C2S_HolePunchingRequestMessage)msgObject;

                        User userA = _userList.Find(msgHoleReq.FromUserName);
                        User userB = _userList.Find(msgHoleReq.ToUserName);

                        // 發送打洞(Punching Hole)消息
                        DoWriteLog(string.Format("用戶:[{0} IP:{1}]想與[{2} IP:{3}]建立對話通道.",
                        userA.UserName, userA.NetPoint.ToString(),
                        userB.UserName, userB.NetPoint.ToString()));

                        //由Server發送消息給B,將A的IP的IP地址信息告訴B,然后由B發送一個測試消息給A.
                        S2C_HolePunchingMessage msgHolePunching = new S2C_HolePunchingMessage(_remotePoint);
                        this.SendMessage(msgHolePunching, userB.NetPoint); //Server->B
                    }
                    else if (msgType == typeof(C2S_GetUsersMessage))
                    {
                        // 發送當前用戶信息
                        S2C_UserListMessage srvResMsg = new S2C_UserListMessage(_userList);
                        this.SendMessage(srvResMsg, _remotePoint);
                    }
                }
                catch (Exception ex) { DoWriteLog(ex.Message); }
            }
        }

  • frmServer 


該文章在 2018/2/10 10:08:37 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved