這兩天公司要用C#寫一個windows服務,作成安裝安裝包。製做的過程當中遇到了一些問題,寫完以後總結一下。若是之後在用到的話能夠能夠參考一下,並且因爲原來沒有作過,不知道這樣作是對是不對,請各位看官若是發現有不當之處請指教。windows
開始的時候個人開發工具VS 2012,須要用InstallShield,沒鬧明白,時間緊迫沒有搞,改用vs2010。服務器
首先建立一個windows服務:app
添加安裝程序: socket
設置服務的屬性: ide
這裏面簡單設置一下服務的屬性,ServiceName就是服務的名稱,DispalyName是在本地服務列表中現實的名稱,若是DispalyName沒有設置,那麼默認爲ServiceName。StartType是服務的啓動方式,Automatic爲服務開機啓動,Manual是手動。設置以下:工具
OK下面開始搞一下服務的邏輯部分開發工具
服務邏輯:this
就是寫一個Soket服務端,在Service1.cd文件中有以下代碼段:url
1 protected override void OnStart(string[] args) 2 { 3 4 } 5 6 protected override void OnStop() 7 { 8 9 }
從字面理解也很容易,一個是服務啓動時候一個是服務中止時候調用的方法,spa
我定義了一個處理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端口是否是處於監聽狀態:
沒問題,運行一下Socket客戶端,看一下結果:
沒問題,這樣就算成功了,可是有個問題,因爲服務所要開放的端口不少時候都是須要用戶在安裝的時候指定,那好吧,如今將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客戶端:
沒問題。大概就是這樣樣子,若是你們以爲有什麼地方不妥,或者有好的建議或者意見的話但願可以告訴我。謝謝。