C#基于Windows服務(wù)的聊天程序(1)
本文將演示怎么通過C#開發(fā)部署一個(gè)Windows服務(wù),該服務(wù)提供各客戶端的信息通訊,適用于局域網(wǎng)。采用TCP協(xié)議,單一服務(wù)器連接模式為一對(duì)多;多臺(tái)服務(wù)器的情況下,當(dāng)客戶端連接數(shù)超過預(yù)設(shè)值時(shí)可自動(dòng)進(jìn)行負(fù)載轉(zhuǎn)移,當(dāng)然也可手動(dòng)切換服務(wù)器,這種場(chǎng)景在實(shí)際項(xiàng)目中應(yīng)用廣泛。
簡(jiǎn)單的消息則通過服務(wù)器轉(zhuǎn)發(fā),文件類的消息則讓客戶端自己建立連接進(jìn)行傳輸。后續(xù)功能將慢慢完善。
自定義協(xié)議:
1.新建Windows服務(wù)項(xiàng)目
2.修改配置文件添加
<appSettings> <add key="maxQueueCount" value="10"/> <add key="failoverServer" value="192.168.250.113,192.168.250.141"/> </appSettings>
說明:maxQueueCount為最大連接數(shù),failoverServer故障轉(zhuǎn)移備用服務(wù)器(多個(gè)服務(wù)器,隔開)
3.打開ChatService右鍵添加安裝程序,此時(shí)會(huì)自動(dòng)添加ProjectInstaller.cs文件,文件中會(huì)默認(rèn)添加serviceProcessInstaller1和serviceInstaller1兩個(gè)組件
修改serviceInstaller1和serviceProcessInstaller1的屬性信息如圖
StartType屬性說明:
Automatic 指示服務(wù)在系統(tǒng)啟動(dòng)時(shí)將由(或已由)操作系統(tǒng)啟動(dòng)。如果某個(gè)自動(dòng)啟動(dòng)的服務(wù)依賴于某個(gè)手動(dòng)啟動(dòng)的服務(wù),則手動(dòng)啟動(dòng)的服務(wù)也會(huì)在系統(tǒng)啟動(dòng)時(shí)自動(dòng)啟動(dòng)。
Disabled 指示禁用該服務(wù),以便它無法由用戶或應(yīng)用程序啟動(dòng)。
Manual 指示服務(wù)只由用戶(使用“服務(wù)控制管理器”)或應(yīng)用程序手動(dòng)啟動(dòng)。
Account屬性說明:
LocalService 充當(dāng)本地計(jì)算機(jī)上非特權(quán)用戶的帳戶,該帳戶將匿名憑據(jù)提供給所有遠(yuǎn)程服務(wù)器。
LocalSystem 服務(wù)控制管理員使用的帳戶,它具有本地計(jì)算機(jī)上的許多權(quán)限并作為網(wǎng)絡(luò)上的計(jì)算機(jī)。
NetworkService 提供廣泛的本地特權(quán)的帳戶,該帳戶將計(jì)算機(jī)的憑據(jù)提供給所有遠(yuǎn)程服務(wù)器。
User 由網(wǎng)絡(luò)上特定的用戶定義的帳戶。如果為 ServiceProcessInstaller.Account 成員指定 User,則會(huì)使系統(tǒng)在安裝服務(wù)時(shí)提示輸入有效的用戶名和密碼,除非您為 ServiceProcessInstaller 實(shí)例的 Username 和 Password 這兩個(gè)屬性設(shè)置值。
4.完成以后打開ChatService代碼,重寫OnStart和OnStop方法(即服務(wù)的啟動(dòng)和停止方法)。若要重寫其它方法請(qǐng)?jiān)赟erviceBase中查看。
5.在項(xiàng)目中添加服務(wù)注冊(cè)和卸載腳本文件
Install.bat @echo off path %SystemRoot%\Microsoft.NET\Framework\v4.0.30319;%path% installutil %~dp0\WindowsChat.exe %SystemRoot%\system32\sc failure "ChatService" reset= 30 actions= restart/1000 pause @echo on Uninstall.bat @echo off path %SystemRoot%\Microsoft.NET\Framework\v4.0.30319;%path% installutil -u %~dp0\WindowsChat.exe pause @echo on
說明:%~dp0 表示bat文件所在的目錄
文件屬性選擇 始終復(fù)制-內(nèi)容,這樣才能生成到輸出文件夾中
6.回到上面的重寫OnStart和OnStop方法
創(chuàng)建一個(gè)SocketHelper類
namespace WindowsChat { public delegate void WriteInfo(string info); public class SocketHelper { #region 構(gòu)造函數(shù) public SocketHelper() { } public SocketHelper(WriteInfo method) { this.method = method; } #endregion public static Socket LocalSocket = null; private object lockObj = new object(); public static List<Socket> Clients = new List<Socket>(); private WriteInfo method = null; /// <summary> /// 創(chuàng)建Socket /// </summary> /// <param name="port">端口默認(rèn) 11011</param> /// <param name="backlog">The maximum length of the pending connections queue.</param> /// <returns></returns> public Socket Create(int port = 11011, int backlog = 100) { if (LocalSocket == null) { IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);//本機(jī)預(yù)使用的IP和端口 LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); LocalSocket.Bind(ipEndPoint); LocalSocket.Listen(backlog); } return LocalSocket; } /// <summary> /// 查找客戶端連接 /// </summary> /// <param name="id">標(biāo)識(shí)</param> /// <returns></returns> private Socket FindLinked(string id) { foreach (var item in Clients) { if (item.RemoteEndPoint.ToString() == id) return item; } return null; } /// <summary> /// 接受遠(yuǎn)程連接 /// </summary> public void Accept() { if (LocalSocket != null) { while (true) { Socket client = LocalSocket.Accept(); Thread thread = new Thread(new ParameterizedThreadStart(Revice)); thread.Start(client); WriteLog("客戶端:" + client.RemoteEndPoint.ToString() + " 接入"); lock (lockObj) { Clients.Add(client); } BroadCast("ADD|" + client.RemoteEndPoint.ToString()); } } } /// <summary> /// 日志 /// </summary> /// <param name="info">信息</param> private void WriteLog(string info) { using (FileStream fs = new FileStream("C:\\chatservice.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) { using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) { sw.WriteLine(info); } } if (method != null) { method(info); } } /// <summary> /// 廣播 /// </summary> /// <param name="info">信息</param> public void BroadCast(string info) { foreach (var item in Clients) { try { item.Send(Encoding.UTF8.GetBytes(info)); } catch (Exception ex) { WriteLog(item.RemoteEndPoint.ToString() + ex.Message); continue; } } } /// <summary> /// 介紹信息 /// </summary> /// <param name="client"></param> public void Revice(object client) { Socket param = client as Socket; var remoteName = param.RemoteEndPoint.ToString(); if (param != null) { int res = 0; while (true) { byte[] buffer = new byte[10240]; int size = param.ReceiveBufferSize; try { res = param.Receive(buffer); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.ConnectionReset) { Clients.Remove(param); WriteLog("客戶端:" + remoteName + "斷開連接1"); BroadCast("REMOVE|" + remoteName); param.Close(); return; } } if (res == 0) { Clients.Remove(param); WriteLog("客戶端:" + remoteName + "斷開連接2"); BroadCast("REMOVE|" + remoteName); param.Close(); return; } var clientMsg = Encoding.UTF8.GetString(buffer, 0, res); WriteLog(string.Format("收到客戶端{(lán)0}命令:{1}", remoteName, clientMsg)); if (clientMsg == "GETALL") { StringBuilder sb = new StringBuilder(); foreach (var item in Clients) { sb.AppendFormat("{0}|", item.RemoteEndPoint.ToString()); } param.Send(Encoding.UTF8.GetBytes("ALL|" + sb.ToString())); } else if (clientMsg == "OFFLINE") { if (Clients.Contains(param)) { Clients.Remove(param); WriteLog("客戶端:" + remoteName + "斷開連接2"); BroadCast("REMOVE|" + remoteName); param.Close(); return; } } else if (clientMsg.StartsWith("TRANST|")) { var msgs = clientMsg.Split('|'); var toSocket = FindLinked(msgs[1]); if (toSocket != null) { WriteLog(remoteName + "發(fā)給" + msgs[1] + "的消息" + msgs[2]); toSocket.Send(Encoding.UTF8.GetBytes("TRANSF|" + remoteName + "|" + msgs[2])); } } } } } } }
重寫OnStart和OnStop方法
public partial class ChatService : ServiceBase { SocketHelper helper; Thread mainThread; public ChatService() { InitializeComponent(); } protected override void OnStart(string[] args) { if (helper == null) { helper = new SocketHelper(); } helper.Create(); mainThread = new Thread(new ThreadStart(helper.Accept)); mainThread.IsBackground = true; mainThread.Start(); } protected override void OnStop() { helper.BroadCast("SHUTDOWN"); } }
至此一個(gè)簡(jiǎn)易的Windows服務(wù)的聊天服務(wù)端開發(fā)完成,后續(xù)會(huì)在這基礎(chǔ)上進(jìn)行擴(kuò)展。
運(yùn)行install.bat(以管理員身份運(yùn)行)如圖
7.運(yùn)行 services.msc查找到ChatService服務(wù),能正常啟動(dòng)停止說明部署成功!
當(dāng)然你也可以將InstallUtil.exe拷貝到執(zhí)行文件所在目錄,比如c:\bin\
則部署腳本為
cd c:\bin\
InstallUtil WindowsChat.exe
卸載腳本
InstallUtil -u WindowsChat.exe
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:C#操作INI配置文件示例詳解
欄 目:C#教程
下一篇:C#實(shí)現(xiàn)的海盜分金算法實(shí)例
本文標(biāo)題:C#基于Windows服務(wù)的聊天程序(1)
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/5567.html
您可能感興趣的文章
- 01-10C#實(shí)現(xiàn)自定義windows系統(tǒng)日志的方法
- 01-10C#3.0使用EventLog類寫Windows事件日志的方法
- 01-10C#使用windows服務(wù)開啟應(yīng)用程序的方法
- 01-10基于C#實(shí)現(xiàn)簡(jiǎn)單離線注冊(cè)碼生成與驗(yàn)證
- 01-10C#基于UDP實(shí)現(xiàn)的P2P語(yǔ)音聊天工具
- 01-10C#實(shí)現(xiàn)基于加減按鈕形式控制系統(tǒng)音量及靜音的方法
- 01-10C#基于WebBrowser獲取cookie的實(shí)現(xiàn)方法
- 01-10C#基于委托實(shí)現(xiàn)多線程之間操作的方法
- 01-10C#微信開發(fā)(服務(wù)器配置)
- 01-10基于C#對(duì)用戶密碼使用MD5加密與解密


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻 器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置