C#基于Windows服务的聊天程序(1)
本文将演示怎么通过C#开发部署一个Windows服务,该服务提供各客户端的信息通讯,适用于局域网。采用TCP协议,单一服务器连接模式为一对多;多台服务器的情况下,当客户端连接数超过预设值时可自动进行负载转移,当然也可手动切换服务器,这种场景在实际项目中应用广泛。
简单的消息则通过服务器转发,文件类的消息则让客户端自己建立连接进行传输。后续功能将慢慢完善。
自定义协议:
1.新建Windows服务项目
2.修改配置文件添加
<appSettings> <add key="maxQueueCount" value="10"/> <add key="failoverServer" value="192.168.250.113,192.168.250.141"/> </appSettings>
说明:maxQueueCount为最大连接数,failoverServer故障转移备用服务器(多个服务器,隔开)
3.打开ChatService右键添加安装程序,此时会自动添加ProjectInstaller.cs文件,文件中会默认添加serviceProcessInstaller1和serviceInstaller1两个组件
修改serviceInstaller1和serviceProcessInstaller1的属性信息如图
StartType属性说明:
Automatic 指示服务在系统启动时将由(或已由)操作系统启动。如果某个自动启动的服务依赖于某个手动启动的服务,则手动启动的服务也会在系统启动时自动启动。
Disabled 指示禁用该服务,以便它无法由用户或应用程序启动。
Manual 指示服务只由用户(使用“服务控制管理器”)或应用程序手动启动。
Account属性说明:
LocalService 充当本地计算机上非特权用户的帐户,该帐户将匿名凭据提供给所有远程服务器。
LocalSystem 服务控制管理员使用的帐户,它具有本地计算机上的许多权限并作为网络上的计算机。
NetworkService 提供广泛的本地特权的帐户,该帐户将计算机的凭据提供给所有远程服务器。
User 由网络上特定的用户定义的帐户。如果为 ServiceProcessInstaller.Account 成员指定 User,则会使系统在安装服务时提示输入有效的用户名和密码,除非您为 ServiceProcessInstaller 实例的 Username 和 Password 这两个属性设置值。
4.完成以后打开ChatService代码,重写OnStart和OnStop方法(即服务的启动和停止方法)。若要重写其它方法请在ServiceBase中查看。
5.在项目中添加服务注册和卸载脚本文件
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文件所在的目录
文件属性选择 始终复制-内容,这样才能生成到输出文件夹中
6.回到上面的重写OnStart和OnStop方法
创建一个SocketHelper类
namespace WindowsChat { public delegate void WriteInfo(string info); public class SocketHelper { #region 构造函数 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> /// 创建Socket /// </summary> /// <param name="port">端口默认 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);//本机预使用的IP和端口 LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); LocalSocket.Bind(ipEndPoint); LocalSocket.Listen(backlog); } return LocalSocket; } /// <summary> /// 查找客户端连接 /// </summary> /// <param name="id">标识</param> /// <returns></returns> private Socket FindLinked(string id) { foreach (var item in Clients) { if (item.RemoteEndPoint.ToString() == id) return item; } return null; } /// <summary> /// 接受远程连接 /// </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("收到客户端{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 + "发给" + 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"); } }
至此一个简易的Windows服务的聊天服务端开发完成,后续会在这基础上进行扩展。
运行install.bat(以管理员身份运行)如图
7.运行 services.msc查找到ChatService服务,能正常启动停止说明部署成功!
当然你也可以将InstallUtil.exe拷贝到执行文件所在目录,比如c:\bin\
则部署脚本为
cd c:\bin\
InstallUtil WindowsChat.exe
卸载脚本
InstallUtil -u WindowsChat.exe
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
上一篇:C#操作INI配置文件示例详解
栏 目:C#教程
下一篇:C#实现的海盗分金算法实例
本文地址:https://www.xiuzhanwang.com/a1/C_jiaocheng/5567.html
您可能感兴趣的文章
- 01-10C#实现自定义windows系统日志的方法
- 01-10C#3.0使用EventLog类写Windows事件日志的方法
- 01-10C#使用windows服务开启应用程序的方法
- 01-10基于C#实现简单离线注册码生成与验证
- 01-10C#基于UDP实现的P2P语音聊天工具
- 01-10C#实现基于加减按钮形式控制系统音量及静音的方法
- 01-10C#基于WebBrowser获取cookie的实现方法
- 01-10C#基于委托实现多线程之间操作的方法
- 01-10C#微信开发(服务器配置)
- 01-10基于C#对用户密码使用MD5加密与解密
阅读排行
本栏相关
- 01-10C#通过反射获取当前工程中所有窗体并
- 01-10关于ASP网页无法打开的解决方案
- 01-10WinForm限制窗体不能移到屏幕外的方法
- 01-10WinForm绘制圆角的方法
- 01-10C#实现txt定位指定行完整实例
- 01-10WinForm实现仿视频 器左下角滚动新
- 01-10C#停止线程的方法
- 01-10C#实现清空回收站的方法
- 01-10C#通过重写Panel改变边框颜色与宽度的
- 01-10C#实现读取注册表监控当前操作系统已
随机阅读
- 01-11ajax实现页面的局部加载
- 01-10C#中split用法实例总结
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 04-02jquery与jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-10SublimeText编译C开发环境设置