標籤:visual 互動 位元組 src 網路服務 event handle 說明 orm
Prepare
本文將使用一個NuGet公開的組件技術來實現一個區域網路聊天程式,利用組件提供的高效能非同步網路機制實現,免去了手動編寫底層的困擾,易於二次開發,擴充自己的功能。
在Visual Studio 中的NuGet管理器中可以下載安裝,也可以直接在NuGet控制台輸入下面的指令安裝:
Install-Package HslCommunication
NuGet安裝教程 http://www.cnblogs.com/dathlin/p/7705014.html
支援人員QQ群:592132877 (組件的版本更新細節也將第一時間在群裡發布)
Summary
之前已經有篇部落格說明了同步網路通訊的開發,同步網路通訊適用於什麼樣的情境呢,適用於用戶端向伺服器請求資料,必須有資料返回的情況,無論成功還是失敗。地址:http://www.cnblogs.com/dathlin/p/7697782.html
而非同步網路通訊適用於什麼情況呢,適用於伺服器進行群發資料的時候,比如發送訊息給所有的線上用戶端,為了更好的說明非同步網路通訊的實現機制,開發一個多用戶端的區域網路聊天程式來示範非同步作業。
特性如下:
- 區域網路聊天室支援多人線上,上限取決於伺服器的電腦效能。
- 支援使用者名稱登入,支援重複的使用者名稱登入。
- 支援顯示所有線上用戶端的資訊顯示,包括線上時間,上線時間,ip地址,使用者名稱等等。
- 支援伺服器主動發訊息給用戶端。
- 支援伺服器強制關閉用戶端。
- 支援其他人的上下線資訊跟蹤。
本聊天程式是基於C-S架構設計的,需要建立3個項目,一個伺服器項目,用來中轉所有的訊息的,一個是用戶端項目,也就是實際的聊天程式,本次項目還顯示所有線上的用戶端資訊,ip地址,名字。
至於賬戶,本次不採用任何的使用者名稱密碼登入機制,就採用簡易化處理,直接輸入一個名字即可,當然,你也可以更改成使用者名稱密碼登入的機制,也不是特別困難。
簡易的聊天程式不支援圖片,表情包的發送接收,這部分實現起來不是同一個次元的,這部分以後攻克了再開新的博文。
------------> 小插曲
如果需要更複雜的功能,比如賬戶的登入,密碼修改,版本控制,賬戶支援頭像等等,一個基於本組件擴充出來的CS架構的基礎模版項目,二次基於此進行方便的二次開發,該項目使用了好幾處的檔案管理:
https://github.com/dathlin/ClientServerProject
一個C-S模版,該模版由三部分的程式組成,一個服務端啟動並執行程式,一個用戶端啟動並執行程式,還有一個公用的組件,實現了基礎的賬戶管理功能,版本控制,軟體升級,公告管理,訊息群發,共用檔案上傳下載,批量檔案傳送功能。具體的操作方法見示範就行。本項目的一個目標是:提供一個基礎的中小型系統的C-S架構,用戶端有四種模式,無縫整合訪問,winform版本,wpf版本,asp.net mvc版本,Android版本。方便企業進行中小型系統的二次開發和個人學習。
Reference
日誌組件所有的功能類都在 HslCommunication 和 HslCommunication.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上的原始碼。
C# 非同步通訊 網路聊天程式開發 區域網路聊天室開發