|
本文将演示怎么通过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 }
SocketHelper 重写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 |
|
|