zoukankan      html  css  js  c++  java
  • C# p2p UDP穿越NAT,UDP打洞源码

    思路如下(参照源代码):

      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

    客户端核心代码:

     1 private void Run()
     2         {
     3             try
     4             {
     5                 byte[] buffer;//接受数据用 
     6                 while (true)
     7                 {
     8                     buffer = _client.Receive(ref _remotePoint);//_remotePoint变量返回当前连接的用户IP地址 
     9 
    10                     object msgObj = ObjectSerializer.Deserialize(buffer);
    11                     Type msgType = msgObj.GetType();
    12                     DoWriteLog("接收到消息:" + msgType.ToString() + " From:" + _remotePoint.ToString());
    13 
    14                     if (msgType == typeof(S2C_UserListMessage))
    15                     {
    16                         // 更新用户列表 
    17                         S2C_UserListMessage usersMsg = (S2C_UserListMessage)msgObj;
    18                         _userList.Clear();
    19 
    20                         foreach (User user in usersMsg.UserList)
    21                             _userList.Add(user);
    22 
    23                         this.DisplayUsers(_userList);
    24                     }
    25                     else if (msgType == typeof(S2C_UserAction))
    26                     {
    27                         //用户动作,新用户登录/用户登出 
    28                         S2C_UserAction msgAction = (S2C_UserAction)msgObj;
    29                         if (msgAction.Action == UserAction.Login)
    30                         {
    31                             _userList.Add(msgAction.User);
    32                             this.DisplayUsers(_userList);
    33                         }
    34                         else if (msgAction.Action == UserAction.Logout)
    35                         {
    36                             User user = _userList.Find(msgAction.User.UserName);
    37                             if (user != null) _userList.Remove(user);
    38                             this.DisplayUsers(_userList);
    39                         }
    40                     }
    41                     else if (msgType == typeof(S2C_HolePunchingMessage))
    42                     {
    43                         //接受到服务器的打洞命令 
    44                         S2C_HolePunchingMessage msgHolePunching = (S2C_HolePunchingMessage)msgObj;
    45 
    46                         //NAT-B的用户给NAT-A的用户发送消息,此时UDP包肯定会被NAT-A丢弃, 
    47                         //因为NAT-A上并没有A->NAT-B的合法Session, 但是现在NAT-B上就建立了有B->NAT-A的合法session了! 
    48                         P2P_HolePunchingTestMessage msgTest = new P2P_HolePunchingTestMessage(_LocalUserName);
    49                         this.SendMessage(msgTest, msgHolePunching.RemotePoint);
    50                     }
    51                     else if (msgType == typeof(P2P_HolePunchingTestMessage))
    52                     {
    53                         //UDP打洞测试消息 
    54                         //_HoleAccepted = true; 
    55                         P2P_HolePunchingTestMessage msgTest = (P2P_HolePunchingTestMessage)msgObj;
    56                         UpdateConnection(msgTest.UserName, _remotePoint);
    57 
    58                         //发送确认消息 
    59                         P2P_HolePunchingResponse response = new P2P_HolePunchingResponse(_LocalUserName);
    60                         this.SendMessage(response, _remotePoint);
    61                     }
    62                     else if (msgType == typeof(P2P_HolePunchingResponse))
    63                     {
    64                         //_HoleAccepted = true;//打洞成功 
    65                         P2P_HolePunchingResponse msg = msgObj as P2P_HolePunchingResponse;
    66                         UpdateConnection(msg.UserName, _remotePoint);
    67 
    68                     }
    69                     else if (msgType == typeof(P2P_TalkMessage))
    70                     {
    71                         //用户间对话消息 
    72                         P2P_TalkMessage workMsg = (P2P_TalkMessage)msgObj;
    73                         DoWriteLog(workMsg.Message);
    74                     }
    75                     else
    76                     {
    77                         DoWriteLog("收到未知消息!");
    78                     }
    79                 }
    80             }
    81             catch (Exception ex) { DoWriteLog(ex.Message); }
    82         }
    View Code
    • frmClientA

    服务端核心代码:

     1 private void Run()
     2         {
     3             byte[] msgBuffer = null;
     4 
     5             while (true)
     6             {
     7                 msgBuffer = _server.Receive(ref _remotePoint); //接受消息 
     8                 try
     9                 {
    10                     //将消息转换为对象 
    11                     object msgObject = ObjectSerializer.Deserialize(msgBuffer);
    12                     if (msgObject == null) continue;
    13 
    14                     Type msgType = msgObject.GetType();
    15                     DoWriteLog("接收到消息:" + msgType.ToString());
    16                     DoWriteLog("From:" + _remotePoint.ToString());
    17 
    18                     //新用户登录 
    19                     if (msgType == typeof(C2S_LoginMessage))
    20                     {
    21                         C2S_LoginMessage lginMsg = (C2S_LoginMessage)msgObject;
    22                         DoWriteLog(string.Format("用户’{0}’已登录!", lginMsg.FromUserName));
    23 
    24                         // 添加用户到列表 
    25                         IPEndPoint userEndPoint = new IPEndPoint(_remotePoint.Address, _remotePoint.Port);
    26                         User user = new User(lginMsg.FromUserName, userEndPoint);
    27                         _userList.Add(user);
    28 
    29                         this.DoUserChanged(_userList);
    30 
    31                         //通知所有人,有新用户登录 
    32                         S2C_UserAction msgNewUser = new S2C_UserAction(user, UserAction.Login);
    33                         foreach (User u in _userList)
    34                         {
    35                             if (u.UserName == user.UserName) //如果是自己,发送所有在线用户列表
    36                                 this.SendMessage(new S2C_UserListMessage(_userList), u.NetPoint);
    37                             else
    38                                 this.SendMessage(msgNewUser, u.NetPoint);
    39                         }
    40                     }
    41                     else if (msgType == typeof(C2S_LogoutMessage))
    42                     {
    43                         C2S_LogoutMessage lgoutMsg = (C2S_LogoutMessage)msgObject;
    44                         DoWriteLog(string.Format("用户’{0}’已登出!", lgoutMsg.FromUserName));
    45 
    46                         // 从列表中删除用户 
    47                         User logoutUser = _userList.Find(lgoutMsg.FromUserName);
    48                         if (logoutUser != null) _userList.Remove(logoutUser);
    49 
    50                         this.DoUserChanged(_userList);
    51 
    52                         //通知所有人,有用户登出 
    53                         S2C_UserAction msgNewUser = new S2C_UserAction(logoutUser, UserAction.Logout);
    54                         foreach (User u in _userList)
    55                             this.SendMessage(msgNewUser, u.NetPoint);
    56                     }
    57                     else if (msgType == typeof(C2S_HolePunchingRequestMessage))
    58                     {
    59                         //接收到A给B打洞的消息,打洞请求,由客户端发送给服务器端 
    60                         C2S_HolePunchingRequestMessage msgHoleReq = (C2S_HolePunchingRequestMessage)msgObject;
    61 
    62                         User userA = _userList.Find(msgHoleReq.FromUserName);
    63                         User userB = _userList.Find(msgHoleReq.ToUserName);
    64 
    65                         // 发送打洞(Punching Hole)消息 
    66                         DoWriteLog(string.Format("用户:[{0} IP:{1}]想与[{2} IP:{3}]建立对话通道.",
    67                         userA.UserName, userA.NetPoint.ToString(),
    68                         userB.UserName, userB.NetPoint.ToString()));
    69 
    70                         //由Server发送消息给B,将A的IP的IP地址信息告诉B,然后由B发送一个测试消息给A. 
    71                         S2C_HolePunchingMessage msgHolePunching = new S2C_HolePunchingMessage(_remotePoint);
    72                         this.SendMessage(msgHolePunching, userB.NetPoint); //Server->B 
    73                     }
    74                     else if (msgType == typeof(C2S_GetUsersMessage))
    75                     {
    76                         // 发送当前用户信息 
    77                         S2C_UserListMessage srvResMsg = new S2C_UserListMessage(_userList);
    78                         this.SendMessage(srvResMsg, _remotePoint);
    79                     }
    80                 }
    81                 catch (Exception ex) { DoWriteLog(ex.Message); }
    82             }
    83         }
    View Code
    • frmServer 

    如转载请注明本文来自易学网http://www.vjsdn.com/

  • 相关阅读:
    【生活】我以为的周末 vs 实际上的周末
    【JS】553- 深入理解之undefined与null
    【React】552- React 中必会的 10 个概念
    【性能】551- 前端性能优化之重排和重绘
    【学习】复工至今,自我小结。你也是这样吗?
    【JS】550- 简单几步让你的 JS 写得更漂亮
    【数据结构】549- 8种常见数据结构(JS实现)
    ios 设计软件
    学习网站
    推送客户端学习网址
  • 原文地址:https://www.cnblogs.com/siqing99/p/3375161.html
Copyright © 2011-2022 走看看