查看: 2022|回复: 0

[ASP.NET教程] C#基于Windows服务的聊天程序(1)

发表于 2018-2-10 08:00:07

本文将演示怎么通过C#开发部署一个Windows服务,该服务提供各客户端的信息通讯,适用于局域网。采用TCP协议,单一服务器连接模式为一对多;多台服务器的情况下,当客户端连接数超过预设值时可自动进行负载转移,当然也可手动切换服务器,这种场景在实际项目中应用广泛。

简单的消息则通过服务器转发,文件类的消息则让客户端自己建立连接进行传输。后续功能将慢慢完善。

自定义协议:

1.新建Windows服务项目

2.修改配置文件添加

  1. <appSettings>
  2. <add key="maxQueueCount" value="10"/>
  3. <add key="failoverServer" value="192.168.250.113,192.168.250.141"/>
  4. </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.在项目中添加服务注册和卸载脚本文件

  1. Install.bat
  2. [url=home.php?mod=space&uid=1794]@echo[/url] off
  3. path %SystemRoot%\Microsoft.NET\Framework\v4.0.30319;%path%
  4. installutil %~dp0\WindowsChat.exe
  5. %SystemRoot%\system32\sc failure "ChatService" reset= 30 actions= restart/1000
  6. pause
  7. @echo on
  8. Uninstall.bat
  9. @echo off
  10. path %SystemRoot%\Microsoft.NET\Framework\v4.0.30319;%path%
  11. installutil -u %~dp0\WindowsChat.exe
  12. pause
  13. @echo on
复制代码

说明:%~dp0 表示bat文件所在的目录

文件属性选择 始终复制-内容,这样才能生成到输出文件夹中

6.回到上面的重写OnStart和OnStop方法

创建一个SocketHelper类

  1. namespace WindowsChat
  2. {
  3. public delegate void WriteInfo(string info);
  4. public class SocketHelper
  5. {
  6. #region 构造函数
  7. public SocketHelper()
  8. {
  9. }
  10. public SocketHelper(WriteInfo method)
  11. {
  12. this.method = method;
  13. }
  14. #endregion
  15. public static Socket LocalSocket = null;
  16. private object lockObj = new object();
  17. public static List<Socket> Clients = new List<Socket>();
  18. private WriteInfo method = null;
  19. /// <summary>
  20. /// 创建Socket
  21. /// </summary>
  22. /// <param name="port">端口默认 11011</param>
  23. /// <param name="backlog">The maximum length of the pending connections queue.</param>
  24. /// <returns></returns>
  25. public Socket Create(int port = 11011, int backlog = 100)
  26. {
  27. if (LocalSocket == null)
  28. {
  29. IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);//本机预使用的IP和端口
  30. LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  31. LocalSocket.Bind(ipEndPoint);
  32. LocalSocket.Listen(backlog);
  33. }
  34. return LocalSocket;
  35. }
  36. /// <summary>
  37. /// 查找客户端连接
  38. /// </summary>
  39. /// <param name="id">标识</param>
  40. /// <returns></returns>
  41. private Socket FindLinked(string id)
  42. {
  43. foreach (var item in Clients)
  44. {
  45. if (item.RemoteEndPoint.ToString() == id)
  46. return item;
  47. }
  48. return null;
  49. }
  50. /// <summary>
  51. /// 接受远程连接
  52. /// </summary>
  53. public void Accept()
  54. {
  55. if (LocalSocket != null)
  56. {
  57. while (true)
  58. {
  59. Socket client = LocalSocket.Accept();
  60. Thread thread = new Thread(new ParameterizedThreadStart(Revice));
  61. thread.Start(client);
  62. WriteLog("客户端:" + client.RemoteEndPoint.ToString() + " 接入");
  63. lock (lockObj)
  64. {
  65. Clients.Add(client);
  66. }
  67. BroadCast("ADD|" + client.RemoteEndPoint.ToString());
  68. }
  69. }
  70. }
  71. /// <summary>
  72. /// 日志
  73. /// </summary>
  74. /// <param name="info">信息</param>
  75. private void WriteLog(string info)
  76. {
  77. using (FileStream fs = new FileStream("C:\\chatservice.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
  78. {
  79. using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
  80. {
  81. sw.WriteLine(info);
  82. }
  83. }
  84. if (method != null)
  85. {
  86. method(info);
  87. }
  88. }
  89. /// <summary>
  90. /// 广播
  91. /// </summary>
  92. /// <param name="info">信息</param>
  93. public void BroadCast(string info)
  94. {
  95. foreach (var item in Clients)
  96. {
  97. try
  98. {
  99. item.Send(Encoding.UTF8.GetBytes(info));
  100. }
  101. catch (Exception ex)
  102. {
  103. WriteLog(item.RemoteEndPoint.ToString() + ex.Message);
  104. continue;
  105. }
  106. }
  107. }
  108. /// <summary>
  109. /// 介绍信息
  110. /// </summary>
  111. /// <param name="client"></param>
  112. public void Revice(object client)
  113. {
  114. Socket param = client as Socket;
  115. var remoteName = param.RemoteEndPoint.ToString();
  116. if (param != null)
  117. {
  118. int res = 0;
  119. while (true)
  120. {
  121. byte[] buffer = new byte[10240];
  122. int size = param.ReceiveBufferSize;
  123. try
  124. {
  125. res = param.Receive(buffer);
  126. }
  127. catch (SocketException ex)
  128. {
  129. if (ex.SocketErrorCode == SocketError.ConnectionReset)
  130. {
  131. Clients.Remove(param);
  132. WriteLog("客户端:" + remoteName + "断开连接1");
  133. BroadCast("REMOVE|" + remoteName);
  134. param.Close();
  135. return;
  136. }
  137. }
  138. if (res == 0)
  139. {
  140. Clients.Remove(param);
  141. WriteLog("客户端:" + remoteName + "断开连接2");
  142. BroadCast("REMOVE|" + remoteName);
  143. param.Close();
  144. return;
  145. }
  146. var clientMsg = Encoding.UTF8.GetString(buffer, 0, res);
  147. WriteLog(string.Format("收到客户端{0}命令:小贝", remoteName, clientMsg));
  148. if (clientMsg == "GETALL")
  149. {
  150. StringBuilder sb = new StringBuilder();
  151. foreach (var item in Clients)
  152. {
  153. sb.AppendFormat("{0}|", item.RemoteEndPoint.ToString());
  154. }
  155. param.Send(Encoding.UTF8.GetBytes("ALL|" + sb.ToString()));
  156. }
  157. else if (clientMsg == "OFFLINE")
  158. {
  159. if (Clients.Contains(param))
  160. {
  161. Clients.Remove(param);
  162. WriteLog("客户端:" + remoteName + "断开连接2");
  163. BroadCast("REMOVE|" + remoteName);
  164. param.Close();
  165. return;
  166. }
  167. }
  168. else if (clientMsg.StartsWith("TRANST|"))
  169. {
  170. var msgs = clientMsg.Split('|');
  171. var toSocket = FindLinked(msgs[1]);
  172. if (toSocket != null)
  173. {
  174. WriteLog(remoteName + "发给" + msgs[1] + "的消息" + msgs[2]);
  175. toSocket.Send(Encoding.UTF8.GetBytes("TRANSF|" + remoteName + "|" + msgs[2]));
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }
  182. }
复制代码

重写OnStart和OnStop方法

  1. public partial class ChatService : ServiceBase
  2. {
  3. SocketHelper helper;
  4. Thread mainThread;
  5. public ChatService()
  6. {
  7. InitializeComponent();
  8. }
  9. protected override void OnStart(string[] args)
  10. {
  11. if (helper == null)
  12. {
  13. helper = new SocketHelper();
  14. }
  15. helper.Create();
  16. mainThread = new Thread(new ThreadStart(helper.Accept));
  17. mainThread.IsBackground = true;
  18. mainThread.Start();
  19. }
  20. protected override void OnStop()
  21. {
  22. helper.BroadCast("SHUTDOWN");
  23. }
  24. }
复制代码

至此一个简易的Windows服务的聊天服务端开发完成,后续会在这基础上进行扩展。

运行install.bat(以管理员身份运行)如图

7.运行 services.msc查找到ChatService服务,能正常启动停止说明部署成功!

当然你也可以将InstallUtil.exe拷贝到执行文件所在目录,比如c:\bin\

则部署脚本为

  cd c:\bin\

  InstallUtil WindowsChat.exe

  卸载脚本

  InstallUtil -u WindowsChat.exe

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。

您可能感兴趣的文章:

  • c#开发的程序安装时动态指定windows服务名称
  • C#启动windows服务方法的相关问题分析
  • C#启动和停止windows服务的实例代码
  • C#编写Windows服务实例代码
  • C#开发Windows服务实例之实现禁止QQ运行
  • c#创建windows服务(Windows Services)详细步骤
  • c# 在windows服务中 使用定时器实例代码
  • c#创建windows服务入门教程实例
  • c#使用windows服务更新站点地图的详细示例
  • 基于C#实现Windows服务状态启动和停止服务的方法
  • C#使用windows服务开启应用程序的方法
  • C#通过创建Windows服务启动程序的方法详解
  • C#版Windows服务安装卸载小工具
  • C#添加Windows服务 定时任务
  • C#使用windows服务发送邮件
  • 使用C#创建Windows服务的实例代码
  • C#编写Windows服务程序详细步骤详解(图文)


回复

使用道具 举报