包包版网络游戏大厅+桥牌系统 4.终于可以聊天了 您所在的位置:网站首页 手机桥牌游戏大厅 包包版网络游戏大厅+桥牌系统 4.终于可以聊天了

包包版网络游戏大厅+桥牌系统 4.终于可以聊天了

2024-07-16 14:08| 来源: 网络整理| 查看: 265

包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_主线程有了上一章所搭建的网络通信框架,我们就可以自由发挥了。只要把握好HandShake的顺序,就可以了。比如说我下面要介绍的大厅里的聊天机制,就是通过实现了503和504协议的“有问必答”原理。

​​返回目录​​

     有了上一章所搭建的网络通信框架,我们就可以自由发挥了。只要把握好HandShake的顺序,就可以了。比如说我下面要介绍的大厅里的聊天机制,就是通过实现了503和504协议的“有问必答”原理。

     重构后的版本,代码在这里下载:​​PlayCard 2.2​​

     聊天室的截图如下,以下是一个Server端和两个Client端(注意到,kitty是最后登录的,所以看不到先前的聊天信息):

包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_实体类_02     

     详细介绍如下:     

     这里,503协议是Client端发送的聊天信息(Request),504协议是Server端将接受到的聊天信息转发(Response)给所有Client端(包括发送聊天信息的Client端,也就是说,即使是自己发送的消息,也要等Server端Response后才能显示)。

     注:在这套源码的自定义协议中,单数协议为Client端发送的Request,双数协议为Server端发送的Response。

     在CommonClassLibrary类库,添加503和504协议的实体类ChatMessage,其中包括发送用户名UserName和发送消息Message两个属性:

包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_实体类_03包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_通信框架_04Code    [Serializable()]     public class ChatMessage : CommonProtocol     {         private string message;         private string userName;         public ChatMessage()         {         }         public string UserName         {             get             {                 return userName;             }             set             {                 userName = value;             }         }         public string Message         {             get             {                 return message;             }             set             {                 message = value;             }         }     }

     Client端修改:

     登陆成功后,进入MainForm界面,此时会重新建立异步回调的循环

            AsyncCallback GetMsgCallback = new AsyncCallback(GetMsg);             (Client.GetStream()).BeginRead(recByte, 0, 1024, GetMsgCallback, this);

     这里的GetMsg回调方法和LoginForm的GetMsg方法基本相同,唯一区别在else分支,MainForm界面在处理完接收到的数据包后,继续侦听,于是多了以下两条语句:

                lock (Client.GetStream())                 {                     AsyncCallback GetStreamMsgCallback = new AsyncCallback(GetMsg);                     Client.GetStream().BeginRead(recByte, 0, 1024, GetStreamMsgCallback, this);                 }

     而LoginForm的else分支在处理完数据包后,也就是得到验证结果后,不再进行异步回调。说得详细些:验证成功,就跳转到MainForm界面,LoginForm不再继续侦听;验证失败,则立刻终止侦听,直到下一次点击登录按钮,才会重新建立Socket并进行侦听。

     在点击Send按钮后,将携带聊天信息的503协议封装到ChatMessage实体,序列化后发送Request到Server端。

            if (txtMessage.Text.Trim() != "")             {                 ChatMessage u = new ChatMessage();                 u.Protocol = "503";                 u.Message = txtMessage.Text.Trim();                  SendText(SerializationFormatter.GetSerializationBytes(u));                 txtMessage.Text = "";             }

     而处理接收数据包的方法仍然是BuildText,这里是对504协议进行解析:

                case "504":     //按Hall发送Client的聊天信息                    ChatMessage chat = (ChatMessage)obj;                     string message = chat.UserName + " : " + chat.Message + ""r"n";                     this.Invoke(new DisplayMessage(DisplayText), message);                      break;

     BuildText方法所在线程不属于MainForm窗体主线程,但凡是有多线程编程经验的都知道,BuildText方法是不可以直接操作MainForm的控件(DisplayText方法),只能使用this.Invoke技术回调DisplayText的方法指针,正如上面代码所示。

     Server端修改

       为消息事件添加MessageEventArgs类,将Client接收到的消息封装到MessageEventArgs参数后传递给MainThread类的方法:

包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_实体类_03包包版网络游戏大厅+桥牌系统 4.终于可以聊天了_通信框架_04Code    public class MessageEventArgs : EventArgs     {         private string msg;         public string Message         {             get             {                 return msg;             }             set             {                 msg = value;             }         }     }

      Client类

              添加MessageReceived事件

public event EventHandler MessageReceived;

              在BuildText方法中添加对503协议的处理

                case "503":                     if (MessageReceived != null)                     {                         ChatMessage meg = (ChatMessage)obj;                         MessageEventArgs e = new MessageEventArgs();                         e.Message = meg.Message;                         MessageReceived(this, e);                     }                      break;

       MainThread类

              将OnMessageReceived方法附属到新添加的事件MessageReceived上:

             newClient.MessageReceived += OnMessageReceived; 

              而添加OnMessageReceived方法如下,从而把这条聊天信息转发给所有在线用户: 

        public void OnMessageReceived(object sender, MessageEventArgs e)         {             //Message sender client             Client temp = (Client)sender;             AddLog(temp.UserName + " :" + e.Message);             ChatMessage chat = new ChatMessage();             chat.Protocol = "504";             chat.UserName = temp.UserName;             chat.Message = e.Message;             byte[] message = SerializationFormatter.GetSerializationBytes(chat);             Client tempClient;             DataTable dt = ClientList.Instance().GetUserList();             foreach (DataRow row in dt.Rows)             {                 string uid = (string)row["UserID"];                 tempClient = (Client)clientTable[uid];                 tempClient.Send(message);             }         }

     以上是Client端和Server端的修改,归结出“程咬金三板斧”,以后每次添加新协议都如法炮制:

       1.携带新信息的协议,就在CommonClassLibrary类库添加相应的实体类,派生于CommonProtocol基类。

       2.永远是Client端先发Request请求,也就是一个单数协议,如501(登录)、503(发聊天消息),以后还会有很多。这是一个主动的动作,来自UI的的操作,注意,这里只发送消息就可以,而不要等待结果——所谓异步编程的思路。让我们再来看一下点击发送按钮的方法:

            if (txtMessage.Text.Trim() != "")             {                 ChatMessage u = new ChatMessage();                 u.Protocol = "503";                 u.Message = txtMessage.Text.Trim();                 SendText(SerializationFormatter.GetSerializationBytes(u));                 txtMessage.Text = "";             }

       3.Server端永远是被动的接收来自Client的Request请求——一个单数协议,在BuildText方法中对其进行解析后,触发主线程的相应事件,于是在相应的方法中,发送偶数协议,也就是Response。这里,可能是发给原先Request的Client端(如502协议登录验证结果),也可能是群发给其他Client端(如504协议转发聊天信息)

       4.无论Server端还是Client端,都是在BuildText方法中对接收到的数据包进行反序列化,然后根据协议的不同进行不同的处理。以后我们每次添加新协议,都要这里加上case分支语句,注意到Server端处理单数协议,Client处理偶数协议。

       相应的,在这些处理模块中,要进行方法回调,从而操作主线程或UI。这里,Server端使用了事件机制;而Client端使用了委托回调机制。

     补充:小赵指出我使用了HashTable这个老古董存储client对象不是很好,于是我将其改造为Dictionary范型:

        private Dictionary clientTable;         clientTable = new Dictionary();

     也许有人会问,游戏大厅需要聊天么?是的,可以没有这个功能。我演示的目的是承上启下,介绍一下在我这个框架下如何轻松地开发新功能,制定一个套路,为下面的大厅通信打下基础。

     此外,在Client端,目前还没有显示其他用户进入或离开的消息,以及显示用户列表的功能。

     下一章,我要搭建游戏大厅,并对聊天功能进行改进。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有