C# 制作Windows服務安裝包
? ? ? ?這兩天公司要用C#寫一個windows服務,做成安裝安裝包。制作的過程中遇到了一些問題,寫完之后總結一下。如果以后在用到的話可以可以參考一下,而且由于原來沒有做過,不知道這樣做是對是不對,請各位看官如果發現有不當之處請指教。
? ? ? 開始的時候我的開發工具VS 2012,需要用 InstallShield,沒鬧明白,時間緊迫沒有搞,改用vs2010。
? ? ? 首先創建一個windows服務:
? ? ?? 添加安裝程序: ? ? ?
? ? ? ? ? ? ? 這里面簡單設置一下服務的屬性,ServiceName就是服務的名稱,DispalyName是在本地服務列表中現實的名稱,如果DispalyName沒有設置,那么默認為ServiceName。StartType是服務的啟動方式,Automatic為服務開機啟動,Manual是手動。設置如下:
? ? ? ? ? ? OK下面開始搞一下服務的邏輯部分
? ? ? ? ?? 服務邏輯:
? ? ? ? ? 就是寫一個Soket服務端,在Service1.cd文件中有如下代碼段:
? ? ? ? ? ?
1 protected override void OnStart( string [] args) 2 { 3 4 } 5 6 protected override void OnStop() 7 { 8 9 }
? ? ? ? ? 從字面理解也很容易,一個是服務啟動時候一個是服務停止時候調用的方法,
? ? 我定義了一個處理Socket的幫助類SocketHandle.cs
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net.Sockets; 6 using System.Net; 7 using System.Threading; 8 9 namespace SocketServer 10 { 11 class SocketHandle 12 { 13 private static Socket serverSocket; 14 int pointInt = 8888 ; 15 private static byte [] result = new byte [ 1024 ]; 16 private static SocketHandle socketHandle; 17 18 public SocketHandle() 19 { 20 21 } 22 23 public static SocketHandle getInstance() 24 { 25 if (socketHandle == null ) 26 { 27 socketHandle = new SocketHandle(); 28 } 29 30 return socketHandle; 31 } 32 33 public void InitSocketServer() 34 { 35 IPAddress localIp = GetLocalIP(); 36 if (localIp == null ) 37 { 38 localIp = IPAddress.Parse( " 127.0.0.1 " ); 39 } 40 try 41 { 42 if (serverSocket == null ) 43 { 44 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 45 serverSocket.Bind( new IPEndPoint(localIp, pointInt)); 46 serverSocket.Listen( 10 ); 47 Thread myThread = new Thread(ListenClientConnect); 48 myThread.Start(); 49 50 } 51 } 52 catch (Exception ex) 53 { 54 } 55 } 56 57 58 59 /// <summary> 60 /// 監聽客戶端連接 61 /// </summary> 62 private static void ListenClientConnect() 63 { 64 while ( true ) 65 { 66 try 67 { 68 Socket clientSocket = serverSocket.Accept(); 69 Thread receiveThread = new Thread(ReceiveMessage); 70 receiveThread.Start(clientSocket); 71 } 72 catch (Exception ex) 73 { 74 } 75 } 76 } 77 78 79 80 /// <summary> 81 /// 接收消息 82 /// </summary> 83 /// <param name="clientSocket"></param> 84 private static void ReceiveMessage( object clientSocket) 85 { 86 Socket myClientSocket = (Socket)clientSocket; 87 byte [] bs = new byte [ 1024 ]; 88 while ( true ) 89 { 90 try 91 { 92 // 通過clientSocket接收數據 93 int receiveNumber = myClientSocket.Receive(result); 94 string data = Encoding.ASCII.GetString(result, 0 , receiveNumber); 95 bs = Encoding.ASCII.GetBytes( " Receive Data " + data); 96 } 97 catch (Exception ex) 98 { 99 myClientSocket.Shutdown(SocketShutdown.Both); 100 myClientSocket.Send(bs, bs.Length, 0 ); // 返回信息給客戶端 101 myClientSocket.Close(); 102 break ; 103 } 104 myClientSocket.Send(bs, bs.Length, 0 ); 105 myClientSocket.Close(); 106 break ; 107 } 108 109 } 110 111 /// <summary> 112 /// 獲取本地IP地址 113 /// </summary> 114 /// <returns></returns> 115 private IPAddress GetLocalIP() 116 { 117 IPAddress localIp = null ; 118 try 119 { 120 IPAddress[] ipArray; 121 ipArray = Dns.GetHostAddresses(Dns.GetHostName()); 122 123 localIp = ipArray.First(ip => ip.AddressFamily == AddressFamily.InterNetwork); 124 } 125 catch (Exception ex) 126 { 127 } 128 129 return localIp; 130 } 131 } 132 }
?
?
? ? ? ? ? ? 在Service1.cs文件中寫成如下形式:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Diagnostics; 6 using System.Linq; 7 using System.ServiceProcess; 8 using System.Text; 9 using System.Threading; 10 11 namespace SocketServer 12 { 13 public partial class Service1 : ServiceBase 14 { 15 private Thread thread; 16 17 public Service1() 18 { 19 InitializeComponent(); 20 } 21 22 /// <summary> 23 /// 服務啟動的時候,初始化socket服務。 24 /// </summary> 25 private void RequestHandle() 26 { 27 try 28 { 29 SocketHandle socketHandle = SocketHandle.getInstance(); 30 socketHandle.InitSocketServer(); 31 } 32 catch (Exception ex) 33 { 34 } 35 } 36 37 protected override void OnStart( string [] args) 38 { 39 thread = new Thread( new ThreadStart( this .RequestHandle)); 40 thread.Start(); 41 } 42 43 protected override void OnStop() 44 { 45 thread.Abort(); 46 } 47 } 48 }
? ? ? ? ?OK,邏輯部分已經完成了,下面看關鍵的操作,將服務打包,首先在本解決方案中添加安裝部署項目,
? 創建安裝項目以后是這個樣子的:?
在應用程序文件夾中添加項目輸出:
? ? ? ? ? ?選擇SocketServer作為主輸出:
創建自定義操作:
選擇自定義操作進入到自定義操作界面,在 自定義操作 上選擇添加自定義操作:
? ? ?在彈出的選擇項目中的項對話框中選擇 應用程序文件夾 中選擇主輸出項目:
?
然后分別重新生成SocketServer和SocketSetUp項目,查看解決方案的屬性,
查看項目的配置,到對應的文件中去找安裝文件,如果是Debug那么就去對應項目中的Debug目錄下去找生成的文件,我們的目錄是F:\SocketServer,所以得去F:\SocketServer\SocketSetUp\Debug目錄去找安裝文件。
?
下面安裝一下看一下結果。(/ □ \),在安裝的時候讓我填寫服務器名稱密碼什么的
,按說我自己在電腦上裝東西應該不需要授權啊,回頭在看一下,問題出在安裝程序上,看到serviceProcessInstaller1的屬性的時候發現了授權的信息,Account這個選項中選擇LocalSystem應該就是本地安裝應該是權限最大的不需要額外授權。試試看吧。OK沒有問題,安裝成功。下面看一下咱們的邏輯是不是正確,先看已安裝程序中是不是存在SocketSetUp程序
沒問題,下面在看一下我們SocketServer Release服務是不是運行正常:
沒問題,查看8888端口是不是處于監聽狀態:
? ?
沒問題,這樣就算成功了,但是有個問題,由于服務所要開放的端口很多時候都是需要用戶在安裝的時候指定,那好吧,現在將Socke服務端的端口由用戶指定。
? ?在安裝項目的視圖中選擇用戶界面,在啟動選項上右鍵添加對話框: ?
?
在彈出的添加對話框中選擇文本框,設置文件框的屬性:
這樣文本框就添加完了,文本框屬性很簡單的可以看出來,只需要一個,其他的都隱藏就好了,下面就如何獲取用戶輸入的端口值,在自定義操作中的主輸出xxxxx的屬性選項卡中添加參數
下面只等著接收了,接收之后將指定的端口保存到本地的安裝目錄,這樣還需要設置一個安裝路徑的參數應該是這樣/Port="[PORT]" /targetdir="[TARGETDIR]/"
俺寫了一個XML的幫助類,用來保存端口:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Xml; 6 7 namespace SocketServer 8 { 9 class XmlHandle 10 { 11 public void updatePort( string path, string port) 12 { 13 try 14 { 15 XmlTextWriter xmlWriter = new XmlTextWriter(path + " \\server.config " , Encoding.UTF8); 16 xmlWriter.WriteStartDocument(); 17 xmlWriter.WriteStartElement( " Server " ); 18 19 xmlWriter.WriteStartElement( " port " , "" ); 20 xmlWriter.WriteString(port); 21 xmlWriter.WriteEndElement(); 22 23 xmlWriter.WriteEndDocument(); 24 xmlWriter.Close(); 25 } 26 catch (Exception ex) 27 { 28 29 } 30 } 31 32 33 public static string getPort() 34 { 35 string port = " 8888 " ; 36 try 37 { 38 XmlDocument doc = new XmlDocument(); 39 doc.Load(AppDomain.CurrentDomain.BaseDirectory + " \\server.config " ); 40 41 XmlNode xnRoot = doc.SelectSingleNode( " Server " ); 42 XmlNodeList xnl = xnRoot.ChildNodes; 43 foreach (XmlNode xn in xnl) 44 { 45 XmlElement xe = (XmlElement)xn; 46 byte [] bts = Encoding.UTF8.GetBytes(xe.Name); 47 if (xe.Name == " port " ) 48 { 49 port = xe.InnerText; 50 byte [] bt = Encoding.UTF8.GetBytes(port); 51 break ; 52 } 53 } 54 55 } 56 catch (Exception ex) 57 { 58 59 } 60 return port; 61 } 62 } 63 }
在ProjectInstaller中處理其中的邏輯,如果需要安裝完成以后馬上運行,那么就需要這樣:
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.ComponentModel; 5 using System.Configuration.Install; 6 using System.Linq; 7 8 9 namespace SocketServer 10 { 11 [RunInstaller( true )] 12 public partial class ProjectInstaller : System.Configuration.Install.Installer 13 { 14 string port = "" ; 15 public ProjectInstaller() 16 { 17 InitializeComponent(); 18 } 19 20 protected override void OnBeforeInstall(IDictionary savedState) 21 { 22 // 從用戶界面獲取的參數 23 port = Context.Parameters[ " Port " ]; 24 } 25 26 protected override void OnAfterInstall(IDictionary savedState) 27 { 28 string appPath = Context.Parameters[ " targetdir " ]; 29 XmlHandle xml = new XmlHandle(); 30 xml.updatePort(appPath, port); 31 System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName); 32 if (sc != null ) 33 { 34 sc.Start(); 35 } 36 } 37 } 38 }
這樣就在服務安裝以后服務就能啟動,但是在服務啟動的時候,需要從本地的配置中獲取端口號,那么就需要在SocketHandle.cs中做一點點修改,改成如下的樣子:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net.Sockets; 6 using System.Net; 7 using System.Threading; 8 9 namespace SocketServer 10 { 11 class SocketHandle 12 { 13 14 private string pointStr = XmlHandle.getPort(); 15 private static Socket serverSocket; 16 int pointInt = 8888 ; 17 private static byte [] result = new byte [ 1024 ]; 18 private static SocketHandle socketHandle; 19 20 public SocketHandle() 21 { 22 23 } 24 25 public static SocketHandle getInstance() 26 { 27 if (socketHandle == null ) 28 { 29 socketHandle = new SocketHandle(); 30 } 31 32 return socketHandle; 33 } 34 35 public void InitSocketServer() 36 { 37 IPAddress localIp = GetLocalIP(); 38 if (localIp == null ) 39 { 40 localIp = IPAddress.Parse( " 127.0.0.1 " ); 41 } 42 try 43 { 44 if (serverSocket == null ) 45 { 46 if (pointStr is string ) 47 { 48 pointInt = Int32.Parse(pointStr); 49 } 50 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 51 serverSocket.Bind( new IPEndPoint(localIp, pointInt)); 52 serverSocket.Listen( 10 ); 53 Thread myThread = new Thread(ListenClientConnect); 54 myThread.Start(); 55 56 } 57 } 58 catch (Exception ex) 59 { 60 } 61 } 62 63 64 65 /// <summary> 66 /// 監聽客戶端連接 67 /// </summary> 68 private static void ListenClientConnect() 69 { 70 while ( true ) 71 { 72 try 73 { 74 Socket clientSocket = serverSocket.Accept(); 75 Thread receiveThread = new Thread(ReceiveMessage); 76 receiveThread.Start(clientSocket); 77 } 78 catch (Exception ex) 79 { 80 } 81 } 82 } 83 84 85 86 /// <summary> 87 /// 接收消息 88 /// </summary> 89 /// <param name="clientSocket"></param> 90 private static void ReceiveMessage( object clientSocket) 91 { 92 Socket myClientSocket = (Socket)clientSocket; 93 byte [] bs = new byte [ 1024 ]; 94 while ( true ) 95 { 96 try 97 { 98 // 通過clientSocket接收數據 99 int receiveNumber = myClientSocket.Receive(result); 100 string data = Encoding.ASCII.GetString(result, 0 , receiveNumber); 101 bs = Encoding.ASCII.GetBytes( " Receive Data " + data); 102 } 103 catch (Exception ex) 104 { 105 myClientSocket.Shutdown(SocketShutdown.Both); 106 myClientSocket.Send(bs, bs.Length, 0 ); // 返回信息給客戶端 107 myClientSocket.Close(); 108 break ; 109 } 110 myClientSocket.Send(bs, bs.Length, 0 ); 111 myClientSocket.Close(); 112 break ; 113 } 114 115 } 116 117 /// <summary> 118 /// 獲取本地IP地址 119 /// </summary> 120 /// <returns></returns> 121 private IPAddress GetLocalIP() 122 { 123 IPAddress localIp = null ; 124 try 125 { 126 IPAddress[] ipArray; 127 ipArray = Dns.GetHostAddresses(Dns.GetHostName()); 128 129 localIp = ipArray.First(ip => ip.AddressFamily == AddressFamily.InterNetwork); 130 } 131 catch (Exception ex) 132 { 133 } 134 135 return localIp; 136 } 137 } 138 }
下面運行一下,在看一下結果:
在服務的安裝界面出現了添加端口的輸入框:
?
看端口:
沒問題,再看Socket客戶端:
沒問題。大概就是這樣樣子,如果大家覺得有什么地方不妥,或者有好的建議或者意見的話希望能夠告訴我。謝謝。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
