本文將演示怎么通過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 未經允許不得轉載!