C# 异步通讯 网络聊天程序开发 局域网聊天室开发

Prepare


 本文将使用一个NuGet公开的组件技术来实现一个局域网聊天程序,利用组件提供的高性能异步网络机制实现,免去了手动编写底层的困扰,易于二次开发,扩展本身的功能。html

 联系做者及加群方式(激活码在群里发放):http://www.hslcommunication.cn/Cooperationgit

在Visual Studio 中的NuGet管理器中能够下载安装,也能够直接在NuGet控制台输入下面的指令安装:github

Install-Package HslCommunication

 

NuGet安装教程  http://www.cnblogs.com/dathlin/p/7705014.html安全

 

 

Summary


以前已经有篇博客说明了同步网络通讯的开发,同步网络通讯适用于什么样的场景呢,适用于客户端向服务器请求数据,必须有数据返回的状况,不管成功仍是失败。地址:http://www.cnblogs.com/dathlin/p/7697782.html服务器

而异步的网络通讯适用于什么状况呢,适用于服务器进行群发数据的时候,好比发送消息给全部的在线客户端,为了更好的说明异步网络通讯的实现机制,开发一个多客户端的局域网聊天程序来演示异步操做。网络

特性以下:架构

  • 局域网聊天室支持多人在线,上限取决于服务器的电脑性能。
  • 支持用户名登陆,支持重复的用户名登陆。
  • 支持显示全部在线客户端的信息显示,包括在线时间,上线时间,ip地址,用户名等等。
  • 支持服务器主动发消息给客户端。
  • 支持服务器强制关闭客户端。
  • 支持其余人的上下线信息跟踪。

 

本聊天程序是基于C-S架构设计的,须要建立3个项目,一个服务器项目,用来中转全部的消息的,一个是客户端项目,也就是实际的聊天程序,本次项目还显示全部在线的客户端信息,ip地址,名字。mvc

至于帐户,本次不采用任何的用户名密码登陆机制,就采用简易化处理,直接输入一个名字便可,固然,你也能够更改为用户名密码登陆的机制,也不是特别困难。框架

简易的聊天程序不支持图片,表情包的发送接收,这部分实现起来不是同一个次元的,这部分之后攻克了再开新的博文。asp.net

 

------------> 小插曲

若是须要更复杂的功能,好比帐户的登陆,密码修改,版本控制,帐户支持头像等等,一个基于本组件扩展出来的CS架构的基础模版项目,二次基于此进行方便的二次开发,该项目使用了好几处的文件管理:

https://github.com/dathlin/ClientServerProject

一个C-S模版,该模版由三部分的程序组成,一个服务端运行的程序,一个客户端运行的程序,还有一个公共的组件,实现了基础的帐户管理功能,版本控制,软件升级,公告管理,消息群发,共享文件上传下载,批量文件传送功能。具体的操做方法见演示就行。本项目的一个目标是:提供一个基础的中小型系统的C-S框架,客户端有四种模式,无缝集成访问,winform版本,wpf版本,asp.net mvc版本,Android版本。方便企业进行中小型系统的二次开发和我的学习。

 

 

Reference


 

日志组件全部的功能类都在 HslCommunicationHslCommunication.Enthernet 命名空间,因此再使用以前先添加:在服务器程序和客户端程序都要添加

using HslCommunication;
using HslCommunication.Enthernet;

 

Start Program


首先先建立三个项目,server项目,client项目,common项目,而后使用Nuget将客户端和服务器两个项目都安装组件,而后切换到服务器程序,接下来就是真的建立程序了。

  • common项目:存放一些服务器和客户端共同用到的类。
  • server项目:消息路由中心。全部的客户端发送的消息都先通过服务器转发。
  • client项目:和用户交互的客户端,接受用户的输入而且显示出来。

在整个项目中,核心部分就是网络通讯了,须要实现客户端向服务器发送消息,这个相对比较好实现,由于服务器的ip地址和端口都是公开的。可是客户端的ip和端口是未知的,由于咱们要实现任意的电脑都能登陆客户端。因此咱们须要使用HslCommunication来方便的实现这些操做。

 

在server端和client端都须要安装HslCommunication组件。由于咱们要实如今客户端和服务器端进行通讯,通讯功能众多,因此须要进行约定,消息的id,咱们最终根据消息的id来区分不一样的消息。

  • 1    系统消息,用于显示谁谁谁上线了,谁谁谁下线了
  • 2    是用户发送的消息,在聊天窗口进行显示的
  • 3    客户端在线信息,因此在线客户端的信息
  • 4    强制客户端下线,用于服务器向客户端发送关闭的指令,客户端接收到后退出程序。

综上所述,这个项目已经初步成型,并且经过消息id能够实现其余本身功能扩展,能够实现任何的交互操做。不必定是聊天系统,各类数据同步机制,推送机制,局域网机制的游戏程序也能够实现。

 

本项目的源代码地址以下:https://github.com/dathlin/NetChatRoom

Server


先填写核心块

        #region 核心网络服务相关


        private NetComplexServer complexServer;

        private void ComplexServerInitialization()
        {
            complexServer = new NetComplexServer();                                             // 实例化
            complexServer.KeyToken = new Guid("91625bad-d581-44ab-b121-ffff5bcb83fb");          // 设置令牌,提高安全性
            complexServer.LogNet = new HslCommunication.LogNet.LogNetSingle("log.txt");         // 设置日志记录,若是不须要,能够删除
            complexServer.ClientOnline += ComplexServer_ClientOnline;                           // 客户端上线时触发
            complexServer.ClientOffline += ComplexServer_ClientOffline;                         // 客户端下线时触发
            complexServer.AllClientsStatusChange += ComplexServer_AllClientsStatusChange;       // 只要有客户端上线或下线就触发
            complexServer.AcceptString += ComplexServer_AcceptString;                           // 客户端发来消息时触发
            complexServer.ServerStart(12345);                                                   // 启动服务,须要选择一个端口
        }

        private void ComplexServer_AllClientsStatusChange(string object1)
        {
            
        }

        private void ComplexServer_AcceptString(AsyncStateOne object1, NetHandle object2, string object3)
        {
            // 咱们规定
            // 1 是系统消息,
            // 2 是用户发送的消息
            // 3 客户端在线信息
            // 4 强制客户端下线
            // 当你的消息头种类不少之后,能够在一个统一的类中心进行规定
            if (object2 == 2)
            {
                // 来自客户端的消息,就只有这么一种状况
                NetMessage msg = new NetMessage()
                {
                    FromName = object1.LoginAlias,
                    Time = DateTime.Now,
                    Type = "string",
                    Content = object3,
                };

                // 群发出去
                complexServer.SendAllClients(2, JObject.FromObject(msg).ToString());
            }
        }

        private void ComplexServer_ClientOffline(AsyncStateOne object1, string object2)
        {
            // 客户端下线,发送消息给客户端
            complexServer.SendAllClients(1, object1.IpAddress + " " + object1.LoginAlias + " : " + object2);
            // 发送在线信息
            complexServer.SendAllClients(3, RemoveOnLine(object1.ClientUniqueID));

            // 在主界面显示信息
            ShowMsg(object1.IpAddress + " " + object1.LoginAlias + " : " + object2);
            ShowOnlineClient( );
        }

        private void ComplexServer_ClientOnline(AsyncStateOne object1)
        {
            // 客户端上线,发送消息给客户端
            complexServer.SendAllClients(1, object1.IpAddress + " " + object1.LoginAlias + " : 上线");
            // 发送在线信息
            NetAccount account = new NetAccount()
            {
                Guid = object1.ClientUniqueID,
                Ip = object1.IpAddress,
                Name = object1.LoginAlias,
                OnlineTime = DateTime.Now.ToString(),
            };
            complexServer.SendAllClients(3,  AddOnLine(account));

            // 在主界面显示信息
            ShowMsg(object1.IpAddress + " " + object1.LoginAlias + " : 上线");
            ShowOnlineClient( );
        }


        #endregion

 在此处有个功能是实现对在线客户端的信息记录,包含了许多的信息,并能够实现扩展

        #region 在线客户端信息实现块

        private List<NetAccount> all_accounts = new List<NetAccount>();
        private object obj_lock = new object();

        // 新增一个用户帐户到在线客户端
        private string AddOnLine(NetAccount item)
        {
            string result = string.Empty;
            lock(obj_lock)
            {
                all_accounts.Add(item);
                result = JArray.FromObject(all_accounts).ToString();
            }
            return result;
        }

        // 移除在线帐户并返回相应的在线信息
        private string RemoveOnLine(string guid)
        {
            string result = string.Empty;
            lock (obj_lock)
            {
                for (int i = 0; i < all_accounts.Count; i++)
                {
                    if(all_accounts[i].Guid == guid)
                    {
                        all_accounts.RemoveAt(i);
                        break;
                    }
                }
                result = JArray.FromObject(all_accounts).ToString();
            }
            return result;
        }


        #endregion

 关于在线信息的类

    /// <summary>
    /// 扩展实现的帐户信息,记录惟一标记,ip地址,上线时间,名字
    /// </summary>
    public class NetAccount
    {
        /// <summary>
        /// 惟一ID
        /// </summary>
        public string Guid { get; set; }
        /// <summary>
        /// Ip地址
        /// </summary>
        public string Ip { get; set; }
        /// <summary>
        /// 上线时间
        /// </summary>
        public string OnlineTime { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 字符串标识形式
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "[" + Ip + "] : " + Name;
        }
    }

 下面演示在服务器端如何发送一个系统消息给全部客户端

        private void userButton1_Click(object sender, EventArgs e)
        {
            // 服务器发送系统消息到客户端
            if(!string.IsNullOrEmpty(textBox2.Text))
            {
                // 来自客户端的消息,就只有这么一种状况
                NetMessage msg = new NetMessage()
                {
                    FromName = "系统",
                    Time = DateTime.Now,
                    Type = "string",
                    Content = textBox2.Text,
                };

                // 群发出去
                complexServer.SendAllClients(2, JObject.FromObject(msg).ToString());
            }
        }

 这样就能够实现消息的发送了。

 

Client


先填写核心块

       #region 客户端网络块


        private NetComplexClient net_socket_client = new NetComplexClient();

        private void Net_Socket_Client_Initialization()
        {
            try
            {
                net_socket_client.KeyToken = new Guid("91625bad-d581-44ab-b121-ffff5bcb83fb");          // 设置令牌,必须与链接的服务器令牌一致
                net_socket_client.EndPointServer = new System.Net.IPEndPoint(
                    System.Net.IPAddress.Parse("127.0.0.1"),12345);                                     // 链接的服务器的地址,必须和服务器端的信息对应
                net_socket_client.ClientAlias = LoginName;                                              // 传入帐户名
                net_socket_client.AcceptString += Net_socket_client_AcceptString;                       // 接收到字符串信息时触发
                net_socket_client.ClientStart();
            }
            catch (Exception ex)
            {
                SoftBasic.ShowExceptionMessage(ex);
            }
        }

        /// <summary>
        /// 接收到服务器的字节数据的回调方法
        /// </summary>
        /// <param name="state">网络链接对象</param>
        /// <param name="customer">用户自定义的指令头,用来区分数据用途</param>
        /// <param name="data">数据</param>
        private void Net_socket_client_AcceptString(AsyncStateOne state, NetHandle customer, string data)
        {
            // 咱们规定
            // 1 是系统消息,
            // 2 是用户发送的消息
            // 3 客户端在线信息
            // 4 退出指令
            // 当你的消息头种类不少之后,能够在一个统一的类中心进行规定
            if (customer == 1)
            {
                ShowSystemMsg(data);
            }
            else if(customer == 2)
            {
                ShowMsg(data);
            }
            else if(customer == 3)
            {
                ShowOnlineClient(data);
            }
            else if(customer == 4)
            {
                // 退出系统
                QuitSystem( );
            }
        }




        #endregion

 用户在输入发送信息的时候,就调用以下的方法:

        // 发送消息
        private void userButton1_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(textBox3.Text)) return;

            net_socket_client.Send(2, textBox3.Text);
            textBox3.Clear();
        }

 

 

具体的代码逻辑还须要参照github上的源代码。

相关文章
相关标签/搜索