由于服务器端的IP地址是变化的,所以客户端在登录前需要修改连接地址。
思路一:修改客户端配置文件app.config的<client>节点上<endpoint>的address。
处理方法如下,但是这个方法有个缺点,就是即便修改配置文件中的地址后,即便是新创建的客户端代理对象,其address依然是修改前的地址,除非重新启动客户端。
方法如下:
private void UpdateConfig(string serverIPAddress, string serverPort) { //Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location); Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ConfigurationSectionGroup sct = config.SectionGroups["system.serviceModel"]; ServiceModelSectionGroup serviceModelSectionGroup = sct as ServiceModelSectionGroup; ClientSection clientSection = serviceModelSectionGroup.Client; foreach (ChannelEndpointElement item in clientSection.Endpoints) { string pattern = "://.*/"; string address = item.Address.ToString(); string replacement = string.Format("://{0}:{1}/", serverIPAddress, serverPort); address = Regex.Replace(address, pattern, replacement); item.Address = new Uri(address); } config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("system.serviceModel"); }
至于是否有办法可以刷新这个地址,答案是否定的。原因是.Net framework 在客户端启动时就将app.config 读入了,而且后面即便修改了app.config文件,也不会刷新app.config的内容。
至于为什么不重新加载,文章【1】说在通常使用时不可能的。如果要刷新app.config的内容,需要知道app.config的内容何时改变了,这可以通过注册“文件改变通知”事件来实现。然而一旦检测到app.config的内容改变,需要通知所有使用了app.config的组件。
那么问题就来了, .Net framework 如何来知道哪些组件使用了app.config,由于注册机制的缺陷,是不可能做到。进一步说,实现了所有需要读取app.config的组件在.Net framework中注册这个机制。当.Net framework检测到app.config的改变,它将通知每个注册组件app.config已改变。那么组件该现在该如何做呢?
为了让app.config新的改变生效,组件不得不重新启动它自己。这意味着组件需要能够刷新旧的配置,以及那些基于旧的配置的数据和行为,然后读取新的配置,并从新的配置开始刷新。
这是不可能的。举个绑定策略的例子。如果旧的绑定策略认为它需要从装配件(assembly)A中获得版本1,而新的策略认为它需要从从装配件(assembly)A中获得版本2。在装配件(assembly)A的版本1已经加载的情况下, .Net framework 无法为新的绑定策略从装配件(assembly)A中获得版本2。
仅仅只有一小部分的配置数据可以刷新。这些包括无状态影响或是影响很容易被消除的配置数据。缓存大小就是一个好的例子。在缓存大小改变上,你能重新设置缓存大小,数据仍然保存着,或者简单移除旧的缓存并开启一个新的缓存块。
通常来说,当app.config发生改变时,确保所有组件一致性的方法是重启应用程序。这个方法.Net framework所采用的。
ASP.Net有内置的关于app.config发生改变的检测机制,它在监控web.config上发生的改变。一旦它检测到改变,它将关闭旧的应用程序重新启动新的应用程序。
因此,.Net framework将来也不会提供配置数据刷新的特征,如果你认为这个对于你很重要,你必须自己去实现这个。
幸运的是企业库("Enterprise Library")那帮人理解了这个需求,他们在最新的企业库版本中开发了个“应用程序配置块”(Configuration Application Block)。
思路二:如何在不重新启动客户端的情况下改变服务器端的地址。总共有两大类,各有两种情况。
第一大类:对于使用代理类
两种情况:
(1)不使用身份验证
(2)使用身份验证
/// <summary> /// 调用方案一 /// </summary> /// <param name="serverAddress">服务器地址</param> private static void CallFirstScheme(string serverAddress) { /* /*----------------------方案一:对于使用代理类-----------------------------------------*/ Console.WriteLine("方案一:"); //1.测试连接(不使用身份验证) Console.WriteLine("1.1.1 创建代理对象 userNamePwdValidator."); EndpointAddress address = new EndpointAddress("net.tcp://" + serverAddress + ":8086/UserNamePwdValidator/UserNamePwdValidatorService"); UserNamePwdValidatorClient userNamePwdValidator = new UserNamePwdValidatorClient("UserNamePwdValidatorService", address); bool result = userNamePwdValidator.Validate(string.Empty, string.Empty); Console.WriteLine("1.1.2 测试连接成功."); //2.验证用户名和密码(与测试连接调用同样的接口,不使用身份验证) //bool result = userNamePwdValidator.Validate("admin", admin); //3.调用服务(使用身份验证) Console.WriteLine("1.2.1 创建服务代理对象 user."); UserClient user = new UserClient("UserService"); user.Endpoint.Address = new EndpointAddress(new Uri("net.tcp://" + serverAddress + ":8086/User"), user.Endpoint.Address.Identity, user.Endpoint.Address.Headers); user.ClientCredentials.UserName.UserName = "admin"; user.ClientCredentials.UserName.Password = "admin"; user.Insert(); Console.WriteLine("1.2.2 调用服务成功./n"); /*----------------------方案一:结束-------------------------------------------------*/ }
第二大类:对于使用工厂类
两种情况:
(1)不使用身份验证
(2)使用身份验证
/// <summary> /// 调用方案二 /// </summary> /// <param name="serverAddress">服务器地址</param> private static void CallSecondScheme(string serverAddress) { /*----------------------方案二:对于使用工厂类-----------------------------------------*/ Console.WriteLine("方案二:"); //1.测试连接(不使用身份验证) Console.WriteLine("2.1.1 创建接口对象 userNamePwdValidator."); ChannelFactory<WCFContracts.IUserNamePwdValidator> channelFactory = new ChannelFactory<WCFContracts.IUserNamePwdValidator>("Another_UserNamePwdValidatorService"); channelFactory.Endpoint.Address = new EndpointAddress("net.tcp://" + serverAddress + ":8086/UserNamePwdValidator/UserNamePwdValidatorService"); WCFContracts.IUserNamePwdValidator otherUserNamePwdValidator = channelFactory.CreateChannel(); bool result = otherUserNamePwdValidator.Validate(string.Empty, string.Empty); Console.WriteLine("2.1.2 测试连接成功."); //2.验证用户名和密码(与测试连接调用同样的接口,不使用身份验证) //bool result = userNamePwdValidator.Validate("admin", admin); //3.调用服务 Console.WriteLine("2.2.1 创建接口对象 user."); ChannelFactory<WCFContracts.IUser> otherChannelFactory = new ChannelFactory<WCFContracts.IUser>("Another_UserService"); otherChannelFactory.Credentials.UserName.UserName = "admin"; otherChannelFactory.Credentials.UserName.Password = "admin"; otherChannelFactory.Endpoint.Address = new EndpointAddress(new Uri("net.tcp://" + serverAddress + ":8086/User"), otherChannelFactory.Endpoint.Address.Identity, otherChannelFactory.Endpoint.Address.Headers); WCFContracts.IUser otherUser = otherChannelFactory.CreateChannel(); otherUser.Insert(); Console.WriteLine("2.2.2 调用服务成功./n"); /*----------------------方案二:结束-------------------------------------------------*/ }
附带身份验证:
在VS工具的命令行中:
(1)添加证书:
makecert -sr LocalMachine -ss My -a sha1 -n CN=MyWCFServer -sky exchange -pe
(2)将证书设置成可信任的
certmgr -add -r LocalMachine -s My -c -n MyWCFServer -s TrustedPeople
请自行修改代码中关于服务器端和客户端配置文件与证书相关的部分。
代码下载
【1】http://blogs.msdn.com/b/junfeng/archive/2005/02/20/376880.aspx