基於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 }
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   未經允許不得轉載!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM