目錄html
權限管理系統系列之序言 sql
首先說下題外話,有些園友看了前一篇【權限管理系統系列之序言】博客加了QQ羣(186841119),看了我寫的權限管理系統的相關文檔(主要是介紹已經開發的功能),給出了一些建議,感受很是好,但願後續有更多的園友能再接再礪給出更多的指導意見,在日常的開發中我的會結合大家的建議作出適當修改和完善,促進共同窗習和進步。關於源碼共享的問題,可能會過段時間公佈,不會如今公開源碼,我的還在不斷完善中,等完成差很少後會公開源碼。數據庫
客戶端與服務器的通訊在一個程序中會佔住關鍵的做用,處理起來可能會有不少方式,好比說Remoting、Socket、WebServices、WCF等等均可以實現。本人這幾種基本上都用過,Socket可能比較少些,一些聊天室的程序就會使用Socket,經過字節的形式接收數據;WebServices會WinCE開發中使用到,數據傳輸進行壓縮,這樣操做數據就比較方便,實時操做數據庫;Remoting主要用在MIS系統的客戶端與服務端通訊,我的也說不出那種好;WCF也是我最近一兩年才接觸到的,公司如今使用的就是WCF通訊的,我的感受用WCF比較方便和簡單,實用起來使用三個函數(一個函數是檢測客戶端與服務器端的心跳,一個是用於登陸的、一個是公共的接口,基本上全部的客戶端和服務端的通訊都是用這個函數),這個函數能夠搞定全部的客戶端訪問服務端的方法,全部的SQL在服務端執行,便於維護和平常的分工,不過在日常的開發中也不會分客戶端和服務端的開發,基本上也是一個一個模塊進行分工的。json
WCF的配置(包括客戶端和服務端)服務器
客戶端的配置文件:多線程
1 <?xml version="1.0"?>
2 <configuration>
3 <system.serviceModel>
4 <bindings>
5 <netTcpBinding>
6 <binding name="TcpBinding_AppService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="10485760" maxBufferSize="10485760" maxConnections="10" maxReceivedMessageSize="10485760">
7 <readerQuotas maxDepth="32" maxStringContentLength="10485760" maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760"/>
8 <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
9 <security mode="None">
10 <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
11 <message clientCredentialType="Windows"/>
12 </security>
13 </binding>
14
15 <binding name="TcpBinding_MessageService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="10485760" maxBufferSize="10485760" maxConnections="10" maxReceivedMessageSize="10485760">
16 <readerQuotas maxDepth="32" maxStringContentLength="10485760" maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760"/>
17 <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
18 <security mode="None">
19 <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
20 <message clientCredentialType="Windows"/>
21 </security>
22 </binding>
23
24 </netTcpBinding>
25 </bindings>
26 <client>
27 <endpoint address="net.tcp://localhost:9090/AppService" binding="netTcpBinding" bindingConfiguration="TcpBinding_AppService" contract="IAppService" name="TcpBinding_AppService"/>
28
29 <endpoint address="net.tcp://localhost:7070/MessageService" binding="netTcpBinding" bindingConfiguration="TcpBinding_MessageService" contract="IMessageService" name="TcpBinding_MessageService"/>
30 </client>
31 </system.serviceModel>
32 </configuration>
服務端的配置文件:併發
1 <?xml version="1.0"?>
2 <configuration>
3 <system.serviceModel>
4 <services>
5 <service behaviorConfiguration="Service.Behavior" name="Server.AppService">
6 <endpoint address="AppService" binding="netTcpBinding" bindingConfiguration="AppServiceBinding" name="TcpBinding_AppService" contract="Server.IAppService" />
7 <endpoint address="AppService/mex" binding="mexTcpBinding" contract="IMetadataExchange" />
8 <host>
9 <baseAddresses>
10 <add baseAddress="net.tcp://localhost:9090" />
11 </baseAddresses>
12 </host>
13 </service>
14 <service behaviorConfiguration="Service.Behavior" name="Server.MessageService">
15 <endpoint address="MessageService" binding="netTcpBinding" bindingConfiguration="MessageServiceBinding" name="TcpBinding_MessageService" contract="Server.IMessageService" />
16 <endpoint address="MessageService/mex" binding="mexTcpBinding" contract="IMetadataExchange" />
17 <host>
18 <baseAddresses>
19 <add baseAddress="net.tcp://localhost:7070" />
20 </baseAddresses>
21 </host>
22 </service>
23 </services>
24 <bindings>
25 <netTcpBinding>
26 <binding name="AppServiceBinding" maxBufferSize="10485760" maxReceivedMessageSize="10485760">
27 <readerQuotas maxDepth="32" maxStringContentLength="10485760"
28 maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760" />
29 <reliableSession ordered="true" inactivityTimeout="00:10:00"
30 enabled="false" />
31 <security mode="None" />
32 </binding>
33 <binding name="MessageServiceBinding" maxBufferSize="10485760" maxReceivedMessageSize="10485760">
34 <readerQuotas maxDepth="32" maxStringContentLength="10485760"
35 maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760" />
36 <reliableSession ordered="true" inactivityTimeout="00:10:00"
37 enabled="false" />
38 <security mode="None" />
39 </binding>
40 </netTcpBinding>
41 </bindings>
42 <behaviors>
43 <serviceBehaviors>
44 <behavior name="Service.Behavior">
45 <serviceMetadata />
46 <serviceDebug includeExceptionDetailInFaults="true" />
47 <!--會話最大數量(併發會話)-->
48 <serviceThrottling maxConcurrentSessions="100" />
49 <!--數據序列最大量-->
50 <dataContractSerializer maxItemsInObjectGraph="10485760" />
51 </behavior>
52 <behavior name="mexConfig">
53 <serviceDebug includeExceptionDetailInFaults="True" />
54 <serviceMetadata />
55 </behavior>
56 </serviceBehaviors>
57 </behaviors>
58 </system.serviceModel>
59 </configuration>
以上爲客戶端與服務端的配置文件,有兩個配置,一個爲基本通訊所用,一個爲雙工通訊所用。tcp
介紹完配置文件後再介紹實現函數:ide
1 [ServiceContract(Name = "IAppService", SessionMode = SessionMode.Allowed, Namespace = "http://tempuri.org/")] 2 public interface IAppService 3 { 4 //心跳 5 [OperationContract] 6 string HeartBeat(string echo); 7 8 //登陸 9 [OperationContract] 10 bool Login(string UserName, string Password); 11 12 //統一的應用業務請求調用,用於會話控制和調用轉發 13 [OperationContract] 14 Result AppCall(Request request); 15 } 16 17 #region * 推送消息 18 [ServiceContract(CallbackContract = typeof(IPushClient))] 19 public interface IMessageService 20 { 21 [OperationContract] 22 void RegisterClient(); 23 } 24 25 public interface IPushClient 26 { 27 [OperationContract(IsOneWay = true)] 28 void SendMessage(string message); 29 } 30 #endregion
分別有三個函數,一個是維持服務端與客戶端的心跳,一個爲登錄所用,一個爲全部函數的接口,基本上全部的通訊都是經過這個函數進行調用。最下面的爲雙工通訊的定義,定義成回調函數的形式。函數
以上函數實現邏輯:
1 //每一個會話一個實例,同一個會話下的多線程併發 2 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)] 3 public class AppService : IAppService 4 { 5 //記錄是否登陸 6 bool IsLogined = false; 7 Context context = new Context(); 8 9 private static readonly string _username = "Server"; 10 private static readonly string _password = "123456"; 11 12 //心跳請求,維持鏈路正常 13 public string HeartBeat(string echo) 14 { 15 Log.Log.Debug("收到請求:" + echo); 16 return "Re:" + echo; 17 } 18 19 //用於登陸 20 public bool Login(string UserName, string Password) 21 { 22 Log.Log.Debug("收到請求:" + UserName + ";" + Password); 23 if (_username.Equals(Encrypt.DecryptDES(UserName, Const.EncryptKey)) && _password.Equals(Encrypt.DecryptDES(Password, Const.EncryptKey))) 24 { 25 IsLogined = true; 26 context.UserName = UserName; 27 return IsLogined; 28 } 29 else 30 { 31 IsLogined = false; 32 context.UserName = ""; 33 return IsLogined; 34 } 35 } 36 37 //統一的業務請求調用代理 38 delegate Result ActionDelegate(string reqdata); 39 //統一的應用業務請求調用,用於會話控制和調用轉發 40 public Result AppCall(Request request) 41 { 42 if (!string.IsNullOrEmpty(request.data)) 43 { 44 if (request.data.Contains(JSON.CompressionFlag)) 45 { 46 Log.Log.Debug("收到請求:" + Compression.DecompressString(request.data.Replace(JSON.CompressionFlag, JSON.ReplaceFlag))); 47 } 48 else 49 { 50 Log.Log.Debug("收到請求:" + request.data); 51 } 52 } 53 if (!IsLogined) 54 { 55 Result result = new Result() { success = false, errors = "用戶未登陸,請登陸後再提交請求!" }; 56 Log.Log.Info("Server.AppService.AppCall(Request request):" + JSON.Object2Json(result, false)); 57 return result; 58 } 59 60 if (request.action == null || request.method == null) 61 { 62 Result result = new Result() { success = false, errors = "請求數據格式不正確{request.action==null || request.method==null},請檢查!" }; 63 Log.Log.Error("Server.AppService.AppCall(Request request)出錯:" + JSON.Object2Json(result, false)); 64 return result; 65 66 } 67 try 68 { 69 Type type = Type.GetType("Server.Action." + request.action); 70 object action = Activator.CreateInstance(type, new object[] { this.context }); 71 ActionDelegate doAction = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), action, request.method); 72 Result result = doAction(request.data); 73 return result; 74 } 75 catch (Exception e) 76 { 77 Log.Log.Error("Server.AppService.AppCall(Request request)異常:" + e.ToString()); 78 return new Result() { success = false, errors = "執行業務請求錯誤!" }; 79 } 80 } 81 } 82 83 #region * 推送消息 84 [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] 85 public class MessageService : IMessageService, IDisposable 86 { 87 public static List<IPushClient> ClientCallbackList { get; set; } 88 public MessageService() 89 { 90 ClientCallbackList = new List<IPushClient>(); 91 } 92 93 public void RegisterClient() 94 { 95 var client = OperationContext.Current.GetCallbackChannel<IPushClient>(); 96 var id = OperationContext.Current.SessionId; 97 //Console.WriteLine("{0}registered.", id); 98 Log.Log.Info(string.Format("{0}registered.", id)); 99 OperationContext.Current.Channel.Closing+=new EventHandler(Channel_Closing); 100 ClientCallbackList.Add(client); 101 } 102 103 private void Channel_Closing(object sender, EventArgs e) 104 { 105 lock (ClientCallbackList) 106 { 107 ClientCallbackList.Remove((IPushClient)sender); 108 } 109 } 110 111 public void Dispose() 112 { 113 ClientCallbackList.Clear(); 114 } 115 } 116 #endregion 117 }
通訊接口經過以上實體,success爲函數執行狀態,msg爲函數返回的信息,data爲返回的數據(格式爲json格式的),errors爲返回的錯誤信息,sql爲函數執行的sql,返回給前臺界面。
WCF服務生成客戶端的配置文件步驟:
a.打開vs命令行,用cd進入到exe文件目錄。
b.svcutil .exe
c.svcutil *.wsdl *.xsd
完成以上便可。
客戶調用CS文件:
1 //------------------------------------------------------------------------------ 2 // <auto-generated> 3 // 此代碼由工具生成。 4 // 運行時版本:4.0.30319.18444 5 // 6 // 對此文件的更改可能會致使不正確的行爲,而且若是 7 // 從新生成代碼,這些更改將會丟失。 8 // </auto-generated> 9 //------------------------------------------------------------------------------ 10 11 namespace Server.Domain 12 { 13 using System.Runtime.Serialization; 14 15 16 [System.Diagnostics.DebuggerStepThroughAttribute()] 17 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] 18 [System.Runtime.Serialization.DataContractAttribute(Name = "Request", Namespace = "http://schemas.datacontract.org/2004/07/Server.Domain")] 19 public partial class Request : object, System.Runtime.Serialization.IExtensibleDataObject 20 { 21 22 private System.Runtime.Serialization.ExtensionDataObject extensionDataField; 23 24 private string actionField; 25 26 private string dataField; 27 28 private string methodField; 29 30 public System.Runtime.Serialization.ExtensionDataObject ExtensionData 31 { 32 get 33 { 34 return this.extensionDataField; 35 } 36 set 37 { 38 this.extensionDataField = value; 39 } 40 } 41 42 [System.Runtime.Serialization.DataMemberAttribute()] 43 public string action 44 { 45 get 46 { 47 return this.actionField; 48 } 49 set 50 { 51 this.actionField = value; 52 } 53 } 54 55 [System.Runtime.Serialization.DataMemberAttribute()] 56 public string data 57 { 58 get 59 { 60 return this.dataField; 61 } 62 set 63 { 64 this.dataField = value; 65 } 66 } 67 68 [System.Runtime.Serialization.DataMemberAttribute()] 69 public string method 70 { 71 get 72 { 73 return this.methodField; 74 } 75 set 76 { 77 this.methodField = value; 78 } 79 } 80 } 81 82 [System.Diagnostics.DebuggerStepThroughAttribute()] 83 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] 84 [System.Runtime.Serialization.DataContractAttribute(Name = "Result", Namespace = "http://schemas.datacontract.org/2004/07/Server.Domain")] 85 public partial class Result : object, System.Runtime.Serialization.IExtensibleDataObject 86 { 87 88 private System.Runtime.Serialization.ExtensionDataObject extensionDataField; 89 90 private string dataField; 91 92 private string errorsField; 93 94 private string msgField; 95 96 private string sqlField; 97 98 private bool successField; 99 100 public System.Runtime.Serialization.ExtensionDataObject ExtensionData 101 { 102 get 103 { 104 return this.extensionDataField; 105 } 106 set 107 { 108 this.extensionDataField = value; 109 } 110 } 111 112 [System.Runtime.Serialization.DataMemberAttribute()] 113 public string data 114 { 115 get 116 { 117 return this.dataField; 118 } 119 set 120 { 121 this.dataField = value; 122 } 123 } 124 125 [System.Runtime.Serialization.DataMemberAttribute()] 126 public string errors 127 { 128 get 129 { 130 return this.errorsField; 131 } 132 set 133 { 134 this.errorsField = value; 135 } 136 } 137 138 [System.Runtime.Serialization.DataMemberAttribute()] 139 public string msg 140 { 141 get 142 { 143 return this.msgField; 144 } 145 set 146 { 147 this.msgField = value; 148 } 149 } 150 151 [System.Runtime.Serialization.DataMemberAttribute()] 152 public string sql 153 { 154 get 155 { 156 return this.sqlField; 157 } 158 set 159 { 160 this.sqlField = value; 161 } 162 } 163 164 [System.Runtime.Serialization.DataMemberAttribute()] 165 public bool success 166 { 167 get 168 { 169 return this.successField; 170 } 171 set 172 { 173 this.successField = value; 174 } 175 } 176 } 177 } 178 179 180 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 181 [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IAppService")] 182 public interface IAppService 183 { 184 185 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/HeartBeat", ReplyAction = "http://tempuri.org/IAppService/HeartBeatResponse")] 186 string HeartBeat(string echo); 187 188 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/Login", ReplyAction = "http://tempuri.org/IAppService/LoginResponse")] 189 bool Login(string UserName, string Password); 190 191 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/AppCall", ReplyAction = "http://tempuri.org/IAppService/AppCallResponse")] 192 Server.Domain.Result AppCall(Server.Domain.Request request); 193 } 194 195 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 196 public interface IAppServiceChannel : IAppService, System.ServiceModel.IClientChannel 197 { 198 } 199 200 [System.Diagnostics.DebuggerStepThroughAttribute()] 201 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 202 public partial class AppServiceClient : System.ServiceModel.ClientBase<IAppService>, IAppService 203 { 204 205 public AppServiceClient() 206 { 207 } 208 209 public AppServiceClient(string endpointConfigurationName) : 210 base(endpointConfigurationName) 211 { 212 } 213 214 public AppServiceClient(string endpointConfigurationName, string remoteAddress) : 215 base(endpointConfigurationName, remoteAddress) 216 { 217 } 218 219 public AppServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 220 base(endpointConfigurationName, remoteAddress) 221 { 222 } 223 224 public AppServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 225 base(binding, remoteAddress) 226 { 227 } 228 229 public string HeartBeat(string echo) 230 { 231 return base.Channel.HeartBeat(echo); 232 } 233 234 public bool Login(string UserName, string Password) 235 { 236 return base.Channel.Login(UserName, Password); 237 } 238 239 public Server.Domain.Result AppCall(Server.Domain.Request request) 240 { 241 return base.Channel.AppCall(request); 242 } 243 } 244 245 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 246 [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IMessageService", CallbackContract = typeof(IMessageServiceCallback))] 247 public interface IMessageService 248 { 249 250 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IMessageService/RegisterClient", ReplyAction = "http://tempuri.org/IMessageService/RegisterClientResponse")] 251 void RegisterClient(); 252 } 253 254 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 255 public interface IMessageServiceCallback 256 { 257 258 [System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://tempuri.org/IMessageService/SendMessage")] 259 void SendMessage(string message); 260 } 261 262 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 263 public interface IMessageServiceChannel : IMessageService, System.ServiceModel.IClientChannel 264 { 265 } 266 267 [System.Diagnostics.DebuggerStepThroughAttribute()] 268 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 269 public partial class MessageServiceClient : System.ServiceModel.DuplexClientBase<IMessageService>, IMessageService 270 { 271 272 public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance) : 273 base(callbackInstance) 274 { 275 } 276 277 public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) : 278 base(callbackInstance, endpointConfigurationName) 279 { 280 } 281 282 public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) : 283 base(callbackInstance, endpointConfigurationName, remoteAddress) 284 { 285 } 286 287 public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 288 base(callbackInstance, endpointConfigurationName, remoteAddress) 289 { 290 } 291 292 public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 293 base(callbackInstance, binding, remoteAddress) 294 { 295 } 296 297 public void RegisterClient() 298 { 299 base.Channel.RegisterClient(); 300 } 301 }
1 public static AppServiceClient AppService; 2 public static ServiceHearBeat HeartBeat; 3 /// <summary> 4 /// 鏈接是否正常 5 /// </summary> 6 public static bool IsAlive 7 { 8 get { return HeartBeat.IsAlive; } 9 } 10 /// <summary> 11 /// 是否已經彈出過提示 12 /// </summary> 13 public static bool HasNotice 14 { 15 get { return HeartBeat.HasNotice; } 16 set { HeartBeat.HasNotice = value; } 17 } 18 19 20 //鏈接到應用服務 21 if (!ConnectToAppServer()) 22 { 23 Comm.MessageBox.Info("鏈接服務端失敗,請檢查服務端是否已經啓動。"); 24 return; 25 } 26 27 //鏈接到應用服務 28 public static bool ConnectToAppServer() 29 { 30 try 31 { 32 //若是服務存在,則嘗試關閉連接 33 if (AppService != null) 34 { 35 try 36 { 37 AppService.Close(); 38 } 39 catch (Exception) 40 { 41 } 42 } 43 //建立新的服務對象,並進行鏈接和登陸 44 AppService = new AppServiceClient(); 45 AppService.Open(); 46 if (!AppClient.AppService.Login(Encrypt.EncryptDES("Server", Const.EncryptKey), Encrypt.EncryptDES("123456", Const.EncryptKey))) 47 { 48 return false; 49 } 50 return true; 51 } 52 catch (EndpointNotFoundException) 53 { 54 Log.Error("鏈接服務端失敗,服務端IP端口配置錯誤或者是服務端還沒有啓動"); 55 } 56 catch (SocketException) 57 { 58 Log.Error("鏈接服務端失敗,服務端IP端口配置錯誤或者是服務端還沒有啓動"); 59 } 60 catch (Exception e) 61 { 62 Log.Error("鏈接服務端出錯:" + e.ToString()); 63 } 64 return false; 65 66 }
以上便可完成對客戶端鏈接服務端了,基本上完成之後步驟能夠說完成了WCF的通訊,實現了客戶端鏈接服務端。
服務端打開的效果:
客戶端打開的效果:
如對權限管理系統有興趣可加QQ羣:186841119,可參與相關話題討論。相互學習交流,共同進步。