基于Windows服务的聊天程序
本文将演示怎么通过C#开发部署一个Windows服务,该服务提供各客户端的信息通讯,适用于局域网。采用TCP协议,单一服务器连接模式为一对多;多台服务器的情况下,当客户端连接数超过预设值时可自动进行负载转移,当然也可手动切换服务器,这种场景在实际项目中应用广泛。
简单的消息则通过服务器转发,文件类的消息则让客户端自己建立连接进行传输。后续功能将慢慢完善。
自定义协议:
标识 |
含义 |
参数 |
From |
GETALL |
获取所有在线终端 |
|
Client |
OFFLINE |
客户端下线 |
|
Client |
SHUTDOWN |
服务器下线 |
|
Server |
ALL|{0} |
在线终端 |
{0}所有在线客户端标识|分隔 |
Server |
REMOVE|{0} |
通知下线 |
{0}下线客户端的标识 |
Server |
TRANST|{0}|{1} |
发送消息 |
{0}接受消息客户端的标识{1}消息 |
Client |
TRANSF|{0}|{1} |
发送消息 |
{0}发送消息客户端的标识{1}消息 |
Server |
BROADCAST |
广播 |
|
Server |
FILE |
文件 |
|
Client |
LINKTO|{0} |
连接 |
|
Server |
… |
|
|
|
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类
1 namespace WindowsChat 2 { 3 public delegate void WriteInfo(string info); 4 5 public class SocketHelper 6 { 7 #region 构造函数 8 public SocketHelper() 9 { 10 } 11 public SocketHelper(WriteInfo method) 12 { 13 this.method = method; 14 } 15 #endregion 16 17 public static Socket LocalSocket = null; 18 private object lockObj = new object(); 19 public static List<Socket> Clients = new List<Socket>(); 20 private WriteInfo method = null; 21 22 /// <summary> 23 /// 创建Socket 24 /// </summary> 25 /// <param name="port">端口默认 11011</param> 26 /// <param name="backlog">The maximum length of the pending connections queue.</param> 27 /// <returns></returns> 28 public Socket Create(int port = 11011, int backlog = 100) 29 { 30 if (LocalSocket == null) 31 { 32 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);//本机预使用的IP和端口 33 LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 34 LocalSocket.Bind(ipEndPoint); 35 LocalSocket.Listen(backlog); 36 } 37 return LocalSocket; 38 } 39 40 /// <summary> 41 /// 查找客户端连接 42 /// </summary> 43 /// <param name="id">标识</param> 44 /// <returns></returns> 45 private Socket FindLinked(string id) 46 { 47 foreach (var item in Clients) 48 { 49 if (item.RemoteEndPoint.ToString() == id) 50 return item; 51 } 52 return null; 53 } 54 55 /// <summary> 56 /// 接受远程连接 57 /// </summary> 58 public void Accept() 59 { 60 if (LocalSocket != null) 61 { 62 while (true) 63 { 64 Socket client = LocalSocket.Accept(); 65 Thread thread = new Thread(new ParameterizedThreadStart(Revice)); 66 thread.Start(client); 67 WriteLog("客户端:" + client.RemoteEndPoint.ToString() + " 接入"); 68 lock (lockObj) 69 { 70 Clients.Add(client); 71 } 72 BroadCast("ADD|" + client.RemoteEndPoint.ToString()); 73 } 74 } 75 } 76 77 /// <summary> 78 /// 日志 79 /// </summary> 80 /// <param name="info">信息</param> 81 private void WriteLog(string info) 82 { 83 using (FileStream fs = new FileStream("C:\\chatservice.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) 84 { 85 using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) 86 { 87 sw.WriteLine(info); 88 } 89 } 90 if (method != null) 91 { 92 method(info); 93 } 94 } 95 96 /// <summary> 97 /// 广播 98 /// </summary> 99 /// <param name="info">信息</param> 100 public void BroadCast(string info) 101 { 102 foreach (var item in Clients) 103 { 104 try 105 { 106 item.Send(Encoding.UTF8.GetBytes(info)); 107 } 108 catch (Exception ex) 109 { 110 WriteLog(item.RemoteEndPoint.ToString() + ex.Message); 111 continue; 112 } 113 } 114 } 115 116 /// <summary> 117 /// 介绍信息 118 /// </summary> 119 /// <param name="client"></param> 120 public void Revice(object client) 121 { 122 Socket param = client as Socket; 123 var remoteName = param.RemoteEndPoint.ToString(); 124 if (param != null) 125 { 126 int res = 0; 127 while (true) 128 { 129 byte[] buffer = new byte[10240]; 130 int size = param.ReceiveBufferSize; 131 try 132 { 133 res = param.Receive(buffer); 134 } 135 catch (SocketException ex) 136 { 137 if (ex.SocketErrorCode == SocketError.ConnectionReset) 138 { 139 Clients.Remove(param); 140 WriteLog("客户端:" + remoteName + "断开连接1"); 141 BroadCast("REMOVE|" + remoteName); 142 param.Close(); 143 return; 144 } 145 } 146 147 if (res == 0) 148 { 149 Clients.Remove(param); 150 WriteLog("客户端:" + remoteName + "断开连接2"); 151 BroadCast("REMOVE|" + remoteName); 152 param.Close(); 153 return; 154 } 155 var clientMsg = Encoding.UTF8.GetString(buffer, 0, res); 156 WriteLog(string.Format("收到客户端{0}命令:{1}", remoteName, clientMsg)); 157 if (clientMsg == "GETALL") 158 { 159 StringBuilder sb = new StringBuilder(); 160 foreach (var item in Clients) 161 { 162 sb.AppendFormat("{0}|", item.RemoteEndPoint.ToString()); 163 } 164 param.Send(Encoding.UTF8.GetBytes("ALL|" + sb.ToString())); 165 } 166 else if (clientMsg == "OFFLINE") 167 { 168 if (Clients.Contains(param)) 169 { 170 Clients.Remove(param); 171 WriteLog("客户端:" + remoteName + "断开连接2"); 172 BroadCast("REMOVE|" + remoteName); 173 param.Close(); 174 return; 175 } 176 } 177 else if (clientMsg.StartsWith("TRANST|")) 178 { 179 var msgs = clientMsg.Split('|'); 180 var toSocket = FindLinked(msgs[1]); 181 if (toSocket != null) 182 { 183 WriteLog(remoteName + "发给" + msgs[1] + "的消息" + msgs[2]); 184 toSocket.Send(Encoding.UTF8.GetBytes("TRANSF|" + remoteName + "|" + msgs[2])); 185 } 186 } 187 } 188 } 189 } 190 } 191 }
重写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
本文地址:http://www.cnblogs.com/liuxiaobo93/p/7205904.html 未经允许不得转载!