zoukankan      html  css  js  c++  java
  • [转]C# UDP穿越NAT,UDP打洞,UDP Hole Punching源代码

    C#实现UDP穿越NAT程序运行效果图

    贴图图片
    (图一)运行在公网上的服务器程序,用于转发打洞消息.

    贴图图片
    (图二)运行在公网上的测试客户端程序A

    贴图图片
    (图三)运行在NAT网络上的测试客户端程序B


    贴图图片
    (图四) UDP打洞过程状态图



    ***阅读下面代码前请先了解UDP穿越NAT原理***



    1.服务器主窗体源代码
    public partial class frmServer : Form
    {
       private Server _server;
       
       public frmServer()
       {
          InitializeComponent();
       }
       
       private void button1_Click(object sender, EventArgs e)
       {
          _server = new Server();
          _server.OnWriteLog += new WriteLogHandle(server_OnWriteLog);
          _server.OnUserChanged += new UserChangedHandle(OnUserChanged);
          try
          {
             _server.Start();
          }
          catch (Exception ex)
          {
             MessageBox.Show(ex.Message);
          }
       }
       
       //刷新用户列表
       private void OnUserChanged(UserCollection users)
       {
          listBox2.DisplayMember = "FullName";
          listBox2.DataSource = null;
          listBox2.DataSource = users;
       }
       
       //显示跟踪消息
       public void server_OnWriteLog(string msg)
       {
          listBox1.Items.Add(msg);
          listBox1.SelectedIndex = listBox1.Items.Count - 1;
       }
       
       private void button2_Click(object sender, EventArgs e)
       {
          Application.Exit();
       }
       
       private void frmServer_FormClosing(object sender, FormClosingEventArgs e)
       {
          if (_server != null)
          _server.Stop();
       }
       
       private void button3_Click(object sender, EventArgs e)
       {
          //发送消息给所有在线用户
          P2P_TalkMessage msg = new P2P_TalkMessage(textBox1.Text);
          foreach (object o in listBox2.Items)
          {
             User user = o as User;
             _server.SendMessage(msg, user.NetPoint);
          }
       }
       
       private void button6_Click(object sender, EventArgs e)
       {
          listBox1.Items.Clear();
       }
    }
    如转载请注明本文来自易学网http://www.vjsdn.com/

    2.服务器业务类

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using vjsdn.net.library;
    using System.Windows.Forms;

    namespace vjsdn.net.library
    {
       /// <summary>
       /// 服务器端业务类
       /// </summary>
       public class Server
       {
          private UdpClient _server; //服务器端消息监听器
          private UserCollection _userList; //在线用户列表
          private Thread _serverThread;
          private IPEndPoint _remotePoint; //远程用户请求的IP地址及端口
          
          private WriteLogHandle _WriteLogHandle = null;
          private UserChangedHandle _UserChangedHandle = null;
          
          /// <summary>
          /// 显示跟踪消息
          /// </summary>
          public WriteLogHandle OnWriteLog
          {
             get { return _WriteLogHandle; }
             set { _WriteLogHandle = value; }
          }
          
          /// <summary>
          /// 当用户登入/登出时触发此事件
          /// </summary>
          public UserChangedHandle OnUserChanged
          {
             get { return _UserChangedHandle; }
             set { _UserChangedHandle = value; }
          }
          
          /// <summary>
          /// 构造器
          /// </summary>
          public Server()
          {
             _userList = new UserCollection();
             _remotePoint = new IPEndPoint(IPAddress.Any, 0);
             _serverThread = new Thread(new ThreadStart(Run));
          }
          
          /// <summary>
          ///显示跟踪记录
          /// </summary>
          /// <param name="log"></param>
          private void DoWriteLog(string log)
          {
             if (_WriteLogHandle != null)
             (_WriteLogHandle.Target as System.Windows.Forms.Control).Invoke(_WriteLogHandle, log);
          }
          
          /// <summary>
          /// 刷新用户列表
          /// </summary>
          /// <param name="list">用户列表</param>
          private void DoUserChanged(UserCollection list)
          {
             if (_UserChangedHandle != null)
             (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, list);
          }
          
          /// <summary>
          /// 开始启动线程
          /// </summary>
          public void Start()
          {
             try
             {
                _server = new UdpClient(Globals.SERVER_PORT);
                _serverThread.Start();
                DoWriteLog("服务器已经启动,监听端口:" + Globals.SERVER_PORT.ToString() + ",等待客户连接...");
             }
             catch (Exception ex)
             {
                DoWriteLog("启动服务器发生错误: " + ex.Message);
                throw ex;
             }
          }
          
          /// <summary>
          /// 停止线程
          /// </summary>
          public void Stop()
          {
             DoWriteLog("停止服务器...");
             try
             {
                _serverThread.Abort();
                _server.Close();
                DoWriteLog("服务器已停止.");
             }
             catch (Exception ex)
             {
                DoWriteLog("停止服务器发生错误: " + ex.Message);
                throw ex;
             }
          }
          
          //线程主方法
          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); }
             }
          }
          /// <summary>
          /// 发送消息
          /// </summary>
          public void SendMessage(MessageBase msg, IPEndPoint remoteIP)
          {
             DoWriteLog("正在发送消息:" + msg.ToString());
             if (msg == null) return;
             byte[] buffer = ObjectSerializer.Serialize(msg);
             _server.Send(buffer, buffer.Length, remoteIP);
             DoWriteLog("消息已发送.");
          }
       }
    }
    如转载请注明本文来自易学网http://www.vjsdn.com/

    3.客户端主窗体源代码
    public partial class frmClient : Form
    {
       private Client _client;
       
       public frmClient()
       {
          InitializeComponent();
       }
       
       private void frmClient_Load(object sender, EventArgs e)
       {
          _client = new Client();
          _client.OnWriteMessage = this.WriteLog;
          _client.OnUserChanged = this.OnUserChanged;
       }
       
       private void button1_Click(object sender, EventArgs e)
       {
          _client.Login(textBox2.Text, "");
          _client.Start();
       }
       
       private void WriteLog(string msg)
       {
          listBox2.Items.Add(msg);
          listBox2.SelectedIndex = listBox2.Items.Count - 1;
       }
       
       private void button4_Click(object sender, EventArgs e)
       {
          this.Close();
       }
       
       private void button3_Click(object sender, EventArgs e)
       {
          if (_client != null)
          {
             User user = listBox1.SelectedItem as User;
             _client.HolePunching(user);
          }
       }
       
       private void button2_Click(object sender, EventArgs e)
       {
          if (_client != null) _client.DownloadUserList();
       }
       
       private void frmClient_FormClosing(object sender, FormClosingEventArgs e)
       {
          if (_client != null) _client.Logout();
       }
       
       private void OnUserChanged(UserCollection users)
       {
          listBox1.DisplayMember = "FullName";
          listBox1.DataSource = null;
          listBox1.DataSource = users;
       }
       
       private void button5_Click(object sender, EventArgs e)
       {
          P2P_TalkMessage msg = new P2P_TalkMessage(textBox1.Text);
          User user = listBox1.SelectedItem as User;
          _client.SendMessage(msg, user);
       }
       
       private void button6_Click(object sender, EventArgs e)
       {
          listBox2.Items.Clear();
       }
    }
    如转载请注明本文来自易学网http://www.vjsdn.com/

    4.客户端业务逻辑代码
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using vjsdn.net.library;
    using System.Windows.Forms;
    using System.IO;

    namespace vjsdn.net.library
    {
       /// <summary>
       /// 客户端业务类
       /// </summary>
       public class Client : IDisposable
       {
          //private const int MAX_RETRY_SEND_MSG = 1; //打洞时连接次数,正常情况下一次就能成功
          
          private UdpClient _client;//客户端监听器
          private IPEndPoint _hostPoint; //主机IP
          private IPEndPoint _remotePoint; //接收任何远程机器的数据
          private UserCollection _userList;//在线用户列表
          private Thread _listenThread; //监听线程
          private string _LocalUserName; //本地用户名
          //private bool _HoleAccepted = false; //A->B,接收到B用户的确认消息
          
          private WriteLogHandle _OnWriteMessage;
          public WriteLogHandle OnWriteMessage
          {
             get { return _OnWriteMessage; }
             set { _OnWriteMessage = value; }
          }
          
          private UserChangedHandle _UserChangedHandle = null;
          public UserChangedHandle OnUserChanged
          {
             get { return _UserChangedHandle; }
             set { _UserChangedHandle = value; }
          }
          
          /// <summary>
          ///显示跟踪记录
          /// </summary>
          /// <param name="log"></param>
          private void DoWriteLog(string log)
          {
             if (_OnWriteMessage != null)
             (_OnWriteMessage.Target as Control).Invoke(_OnWriteMessage, log);
          }
          
          /// <summary>
          /// 构造器
          /// </summary>
          /// <param name="serverIP"></param>
          public Client()
          {
             string serverIP = this.GetServerIP();
             _remotePoint = new IPEndPoint(IPAddress.Any, 0); //任何与本地连接的用户IP地址。
             _hostPoint = new IPEndPoint(IPAddress.Parse(serverIP), Globals.SERVER_PORT); //服务器地址
             _client = new UdpClient();//不指定端口,系统自动分配
             _userList = new UserCollection();
             _listenThread = new Thread(new ThreadStart(Run));
          }
          
          /// <summary>
          /// 获取服务器IP,INI文件内设置
          /// </summary>
          /// <returns></returns>
          private string GetServerIP()
          {
             string file = Application.StartupPath + "\\ip.ini";
             string ip = File.ReadAllText(file);
             return ip.Trim();
          }
          
          /// <summary>
          /// 启动客户端
          /// </summary>
          public void Start()
          {
             if (this._listenThread.ThreadState == ThreadState.Unstarted)
             {
                this._listenThread.Start();
             }
          }
          
          /// <summary>
          /// 客户登录
          /// </summary>
          public void Login(string userName, string password)
          {
             _LocalUserName = userName;
             
             // 发送登录消息到服务器
             C2S_LoginMessage loginMsg = new C2S_LoginMessage(userName, password);
             this.SendMessage(loginMsg, _hostPoint);
          }
          
          /// <summary>
          /// 登出
          /// </summary>
          public void Logout()
          {
             C2S_LogoutMessage lgoutMsg = new C2S_LogoutMessage(_LocalUserName);
             this.SendMessage(lgoutMsg, _hostPoint);
             
             this.Dispose();
             System.Environment.Exit(0);
          }
          
          /// <summary>
          /// 发送请求获取用户列表
          /// </summary>
          public void DownloadUserList()
          {
             C2S_GetUsersMessage getUserMsg = new C2S_GetUsersMessage(_LocalUserName);
             this.SendMessage(getUserMsg, _hostPoint);
          }
          
          /// <summary>
          /// 显示在线用户
          /// </summary>
          /// <param name="users"></param>
          private void DisplayUsers(UserCollection users)
          {
             if (_UserChangedHandle != null)
             (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, users);
          }
          
          //运行线程
          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); }
          }
          
          private void UpdateConnection(string user, IPEndPoint ep)
          {
             User remoteUser = _userList.Find(user);
             if (remoteUser != null)
             {
                remoteUser.NetPoint = ep;//保存此次连接的IP及端口
                remoteUser.IsConnected = true;
                DoWriteLog(string.Format("您已经与{0}建立通信通道,IP:{1}!",
                remoteUser.UserName, remoteUser.NetPoint.ToString()));
                this.DisplayUsers(_userList);
             }
          }
          
          #region IDisposable 成员
          
          public void Dispose()
          {
             try
             {
                this._listenThread.Abort();
                this._client.Close();
             }
             catch
             {
                
             }
          }
          
          #endregion
          
          public void SendMessage(MessageBase msg, User user)
          {
             this.SendMessage(msg, user.NetPoint);
          }
          
          public void SendMessage(MessageBase msg, IPEndPoint remoteIP)
          {
             if (msg == null) return;
             DoWriteLog("正在发送消息给->" + remoteIP.ToString() + ",内容:" + msg.ToString());
             byte[] buffer = ObjectSerializer.Serialize(msg);
             _client.Send(buffer, buffer.Length, remoteIP);
             DoWriteLog("消息已发送.");
          }
          
          /// <summary>
          /// UDP打洞过程
          /// 假设A想连接B.首先A发送打洞消息给Server,让Server告诉B有人想与你建立通话通道,Server将A的IP信息转发给B
          /// B收到命令后向A发一个UDP包,此时B的NAT会建立一个与A通讯的Session. 然后A再次向B发送UDP包B就能收到了
          /// </summary>
          public void HolePunching(User user)
          {
             //A:自己; B:参数user
             //A发送打洞消息给服务器,请求与B打洞
             C2S_HolePunchingRequestMessage msg = new C2S_HolePunchingRequestMessage(_LocalUserName, user.UserName);
             this.SendMessage(msg, _hostPoint);
             
             Thread.Sleep(2000);//等待对方发送UDP包并建立Session
             
             //再向对方发送确认消息,如果对方收到会发送一个P2P_HolePunchingResponse确认消息,此时打洞成功
             P2P_HolePunchingTestMessage confirmMessage = new P2P_HolePunchingTestMessage(_LocalUserName);
             this.SendMessage(confirmMessage, user);
          }
       }
       
    }
    5.公共代码
    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Net;
    using System.Collections;

    namespace vjsdn.net.library
    {
       /// <summary>
       /// 显示跟踪消息的事件委托
       /// </summary>
       public delegate void WriteLogHandle(string msg);
       
       /// <summary>
       /// 刷新在线用户的事件委托
       /// </summary>
       public delegate void UserChangedHandle(UserCollection users);
       
       public class Globals
       {
          /// <summary>
          /// 服务器侦听端口
          /// </summary>
          public const int SERVER_PORT = 21134;
          
          /// <summary>
          /// 本地侦听端口
          /// </summary>
          public const int LOCAL_PORT = 19786;
       }
       
       /// <summary>
       /// User 的摘要说明。
       /// </summary>
       [Serializable]
       public class User
       {
          protected string _userName;
          protected IPEndPoint _netPoint;
          protected bool _conntected;
          
          public User(string UserName, IPEndPoint NetPoint)
          {
             this._userName = UserName;
             this._netPoint = NetPoint;
          }
          
          public string UserName
          {
             get { return _userName; }
          }
          
          public IPEndPoint NetPoint
          {
             get { return _netPoint; }
             set { _netPoint = value; }
          }
          
          public bool IsConnected //打洞标记
          {
             get { return _conntected; }
             set { _conntected = value; }
          }
          
          public string FullName { get { return this.ToString(); } }
          
          public override string ToString()
          {
             return _userName + "- [" + _netPoint.Address.ToString() + ":" + _netPoint.Port.ToString() + "] " + (_conntected ? "Y" : "N");
          }
       }
       
       /// <summary>
       /// 在线用户列表
       /// </summary>
       [Serializable]
       public class UserCollection : CollectionBase
       {
          public void Add(User user)
          {
             InnerList.Add(user);
          }
          
          public void Remove(User user)
          {
             InnerList.Remove(user);
          }
          
          public User this[int index]
          {
             get { return (User)InnerList[index]; }
          }
          
          public User Find(string userName)
          {
             foreach (User user in this)
             {
                if (string.Compare(userName, user.UserName, true) == 0)
                {
                   return user;
                }
             }
             return null;
          }
       }
       
       /// <summary>
       /// 序列化反序列化对象
       /// </summary>
       public class ObjectSerializer
       {
          public static byte[] Serialize(object obj)
          {
             BinaryFormatter binaryF = new BinaryFormatter();
             MemoryStream ms = new MemoryStream(1024 * 10);
             binaryF.Serialize(ms, obj);
             ms.Seek(0, SeekOrigin.Begin);
             byte[] buffer = new byte[(int)ms.Length];
             ms.Read(buffer, 0, buffer.Length);
             ms.Close();
             return buffer;
          }
          
          public static object Deserialize(byte[] buffer)
          {
             BinaryFormatter binaryF = new BinaryFormatter();
             MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, false);
             object obj = binaryF.Deserialize(ms);
             ms.Close();
             return obj;
          }
       }
    }

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


    6.消息类(转发命令)

    6.1 消息基类.所有命令的基类
    /// <summary>
    /// 消息基类,抽象类
    /// </summary>
    [Serializable]
    public abstract class MessageBase
    {
       //消息基类
    }

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


    6.2 Client to Server命令
    #region 客户端发送到服务器的消息

    /// <summary>
    /// 客户端发送到服务器的消息基类
    /// </summary>
    [Serializable]
    public abstract class C2S_MessageBase : MessageBase
    {
       private string _fromUserName;
       
       protected C2S_MessageBase(string fromUserName)
       {
          _fromUserName = fromUserName;
       }
       
       public string FromUserName
       {
          get { return _fromUserName; }
       }
    }

    /// <summary>
    /// 用户登录消息
    /// </summary>
    [Serializable]
    public class C2S_LoginMessage : C2S_MessageBase
    {
       private string _password;
       
       public C2S_LoginMessage(string userName, string password)
       : base(userName)
       {
          this._password = password;
       }
       
       public string Password
       {
          get { return _password; }
       }
    }

    /// <summary>
    /// 用户登出消息
    /// </summary>
    [Serializable]
    public class C2S_LogoutMessage : C2S_MessageBase
    {
       
       public C2S_LogoutMessage(string userName)
       : base(userName)
       { }
    }

    /// <summary>
    /// 请求用户列表消息
    /// </summary>
    [Serializable]
    public class C2S_GetUsersMessage : C2S_MessageBase
    {
       public C2S_GetUsersMessage(string userName)
       : base(userName)
       { }
    }

    /// <summary>
    /// 请求Purch Hole消息
    /// </summary>
    [Serializable]
    public class C2S_HolePunchingRequestMessage : C2S_MessageBase
    {
       protected string toUserName;
       
       public C2S_HolePunchingRequestMessage(string fromUserName, string toUserName)
       : base(fromUserName)
       {
          this.toUserName = toUserName;
       }
       
       public string ToUserName
       {
          get { return this.toUserName; }
       }
    }

    #endregion

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


    6.3 Server to Client命令
    #region 服务器发送到客户端消息

    /// <summary>
    /// 服务器发送到客户端消息基类
    /// </summary>
    [Serializable]
    public abstract class S2C_MessageBase : MessageBase
    {
    }

    /// <summary>
    /// 请求用户列表应答消息
    /// </summary>
    [Serializable]
    public class S2C_UserListMessage : S2C_MessageBase
    {
       private UserCollection userList;
       
       public S2C_UserListMessage(UserCollection users)
       {
          this.userList = users;
       }
       
       public UserCollection UserList
       {
          get { return userList; }
       }
    }

    /// <summary>
    /// 转发请求Purch Hole消息
    /// </summary>
    [Serializable]
    public class S2C_HolePunchingMessage : S2C_MessageBase
    {
       protected System.Net.IPEndPoint _remotePoint;
       
       public S2C_HolePunchingMessage(System.Net.IPEndPoint point)
       {
          this._remotePoint = point;
       }
       
       public System.Net.IPEndPoint RemotePoint
       {
          get { return _remotePoint; }
       }
    }

    /// <summary>
    /// 服务器通知所有在线用户,
    /// </summary>
    [Serializable]
    public class S2C_UserAction : S2C_MessageBase
    {
       protected User _User;
       protected UserAction _Action;
       
       public S2C_UserAction(User user, UserAction action)
       {
          _User = user;
          _Action = action;
       }
       
       public User User
       {
          get { return _User; }
       }
       
       public UserAction Action
       {
          get { return _Action; }
       }
    }

    #endregion

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


    6.4 Client to Client(Peer to Peer P2P)命令

    #region 点对点消息

    /// <summary>
    /// 点对点消息基类
    /// </summary>
    [Serializable]
    public abstract class P2P_MessageBase : MessageBase
    {
       //
    }

    /// <summary>
    /// 聊天消息
    /// </summary>
    [Serializable]
    public class P2P_TalkMessage : P2P_MessageBase
    {
       private string message;
       
       public P2P_TalkMessage(string msg)
       {
          message = msg;
       }
       
       public string Message
       {
          get { return message; }
       }
    }

    /// <summary>
    /// UDP打洞测试消息
    /// </summary>
    [Serializable]
    public class P2P_HolePunchingTestMessage : P2P_MessageBase
    {
       private string _UserName;
       
       public P2P_HolePunchingTestMessage(string userName)
       {
          _UserName = userName;
       }
       
       public string UserName
       {
          get { return _UserName; }
       }
    }

    /// <summary>
    /// 收到消息的回复确认
    /// 如A与B想建立通话通道,些命令由B发出确认打洞成功
    /// </summary>
    [Serializable]
    public class P2P_HolePunchingResponse : P2P_MessageBase
    {
       private string _UserName;
       
       public P2P_HolePunchingResponse(string userName)
       {
          _UserName = userName;
       }
       
       public string UserName
       {
          get { return _UserName; }
       }
    }

    #endregion

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

    6.5 定义用户的动作

    /// <summary>
    /// 用户动作
    /// </summary>
    public enum UserAction
    {
       Login,
       Logout

    .... more action
    }

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

  • 相关阅读:
    Android-自定义meta-data扩展数据
    (转载)【Android】ViewGroup全面分析
    小问题记录
    Gson使用 .Net中的日期序列化成带字母T的格式,Gson发序列化问题
    Android中自定义textview可以进行自体设置
    .Net版的BeanUtils
    Ksoap2-android 添加网络超时的实现
    Html5 viewport 解释
    计算两个日期之间相差的月数
    Android中的Adapter内,有getView方法
  • 原文地址:https://www.cnblogs.com/LeoWong/p/1574266.html
Copyright © 2011-2022 走看看