询问者
使用remoting时,服务器已拒绝客户端凭据。求助解决方法。

问题
-
我是一个业余爱好者,目前正在用VB.Net写一个基于P2P的桌面小游戏。
虽然对Remoting的机制了解得不多,但是从网上找到了一些代码通过TCPChannel建立和调用远程对象。
以下是一些背景信息:
1、利用UPnP穿越路由器,如果玩家的使用的是支持UPnP的路由器,就可以获得一个允许其他人接入的公网EndPoint。具有公网EndPoint的玩家可以建立主机。
2、主机和客户端的关系。主机端注册WellKnowServiceType(Game类),Singleton模式。客户端通过EndPoint信息获得主机的Game(Transparent Proxy),然后把客户端的Player类添加到主机维护的Game类的PlayList当中。从而实现Server-Client的游戏模型。
一开始遇到的问题是:如果不设置安全级别,比如(ImpersonationLevel 或者 protectionLevel)的话,程序在两台不同的电脑直接就无法建立连接,会报错说:身份验证期间未满足远程端安全要求。请尝试增加 ProtectionLevel 和/或 ImpersonationLevel。
后来从一个blog当中看到介绍说需要自己实现一个IAuthorizeRemotingConnection接口来完成自定义的身份验证。
程序当中所有有关服务器和客户端注册服务通道的代码都是用一个类封装的,具体如下:
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports System.ComponentModel '该类型的代码来自于http://wayfarer.cnblogs.com/archive/2004/07/30/28723.html ' 'System.Runtime.Remoting提供了一系列的方法用于访问远程结构 Public Class RemoteServices Private ServerChannel As TcpChannel Private ClientChannel As TcpChannel Private ServiceRef As Object <Description("在制定的端口号注册通讯通道建立服务器。")> _ Public Sub SetupServer(ByVal port As Integer) Dim provider As New System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider() Dim bcprovider As New System.Runtime.Remoting.Channels.BinaryClientFormatterSinkProvider() provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full Dim props As IDictionary = New Hashtable() props("port") = port props("typeFilterLevel") = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full props("secure") = True 'props("impersonate") = True props("protectionLevel") = System.Net.Security.ProtectionLevel.Sign 'props("tokenImpersonationLevel") = System.Security.Principal.TokenImpersonationLevel.Impersonation props("authorizationModule") = "BloodHime.AuthorizationModule,BloodHime" props("useDefaultCredentials") = "false" ServerChannel = New System.Runtime.Remoting.Channels.Tcp.TcpChannel(props, bcprovider, provider) System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(ServerChannel, True) System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = TimeSpan.Zero End Sub <Description("在服务器端注册一个唯一实例模式的服务对象,供远程用户连接访问。")> _ Public Sub RegistWellKnownSinglton(ByVal obj As MarshalByRefObject, ByVal RemoteTypeName As String) '注册广播服务 System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(obj.GetType, RemoteTypeName, System.Runtime.Remoting.WellKnownObjectMode.Singleton) '注册服务名称 System.Runtime.Remoting.RemotingConfiguration.ApplicationName = RemoteTypeName '注册服务名称 System.Runtime.Remoting.RemotingConfiguration.RegisterActivatedServiceType(obj.GetType) Dim objRef1 As System.Runtime.Remoting.ObjRef = System.Runtime.Remoting.RemotingServices.Marshal(CType(obj, MarshalByRefObject), "LocalGame") 'Dim objRef2 As System.Runtime.Remoting.ObjRef = System.Runtime.Remoting.RemotingServices.Marshal(CType(service2, MarshalByRefObject), "AppService2") End Sub Public Sub Shutdown() '获得当前已注册的通道; Dim channels As IChannel() = ChannelServices.RegisteredChannels '关闭指定名为MyTcp的通道; For Each eachChannel As IChannel In channels If eachChannel.ChannelName = "MyTcp" Then Dim tcpChannel As TcpChannel = CType(eachChannel, TcpChannel) '关闭监听; tcpChannel.StopListening(Nothing) '注销通道; ChannelServices.UnregisterChannel(tcpChannel) End If Next End Sub <Description("用来注册TCP信道和序列化规则,配置服务对象租期。")> _ Public Sub SetupClient() '需要确定这个对象和服务器的TCP Channel是否有冲突。 Dim provider As New BinaryServerFormatterSinkProvider() Dim bcprovider As New BinaryClientFormatterSinkProvider() provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full 'System.ServiceModel.TcpTransportSecurity.ClientCredentialType = ServiceModel.TcpClientCredentialType.None Dim props As IDictionary = New Hashtable() props("port") = 0 props("typeFilterLevel") = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full props("secure") = True 'props("impersonate") = True props("protectionLevel") = System.Net.Security.ProtectionLevel.Sign props("tokenImpersonationLevel") = System.Security.Principal.TokenImpersonationLevel.Impersonation 'props("authorizationModule") = "BloodHime.AuthorizationModule,BloodHime" props("useDefaultCredentials") = True props("username") = "MIO" props("password") = "BASS" props("domain") = "KON" ClientChannel = New TcpChannel(props, bcprovider, provider) ChannelServices.RegisterChannel(ClientChannel, True) System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = TimeSpan.Zero End Sub <Description("调用此方法注册WellKnownType之后,用new WellKnownType就可以创建一个对服务器端SingleCall或者Singlton的本地引用。")> _ Public Sub RegistRemoteServiceType(Of WellKnownType)(ByVal URI As String) RemotingConfiguration.RegisterWellKnownClientType(GetType(WellKnownType), URI) End Sub Protected Overrides Sub Finalize() Shutdown() MyBase.Finalize() End Sub End Class Public Class AuthorizationModule Implements IAuthorizeRemotingConnection Public Function IsConnectingEndPointAuthorized(ByVal endPoint As System.Net.EndPoint) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingEndPointAuthorized Return True End Function Public Function IsConnectingIdentityAuthorized(ByVal identity As System.Security.Principal.IIdentity) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingIdentityAuthorized Return True End Function End Class
我尝试修改其中的Impersonation和Protection参数,但都会在客户端尝试把自己的Player实例添加到服务器端的Game实例当中时报错。
Private Sub thr加入游戏() mGame.加入游戏(mPlayer) End Sub
如果把ProtectionLevel设置成SignAndEncrypt就会报错说:签名被修改。
如果ProtectionLevel仅设置成Sign,就会报错说:服务器已拒绝客户端凭据。
为了诊断问题究竟在哪里,我开了两个DEV实例(一个是Server,一个Client)进行调试。发现在客户端连接过程中,服务器端的IsConnectingEndPointAuthorized确实被调用过,返回True。但是服务器端的IsConnectingIdentityAuthorized在被调用之前,客户端已经报错“服务器已拒绝客户端凭据。”或者“签名被修改。”了。我觉得原因很可能是第二个函数没有被调用造成的。至于为什么没有被调用,可能还是注册通道时参数列表“props”当中还需要设置些什么。
Public Class AuthorizationModule Implements IAuthorizeRemotingConnection Public Function IsConnectingEndPointAuthorized(ByVal endPoint As System.Net.EndPoint) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingEndPointAuthorized Return True End Function Public Function IsConnectingIdentityAuthorized(ByVal identity As System.Security.Principal.IIdentity) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingIdentityAuthorized Return True End Function End Class
网上搜到也有说在配置文件当中设置“Security Mode=None”。但是这个配置文件是WCF程序使用的,针对于Socket或者Port的设置。我目前用的这个程序模板不是WCF程序,TCPChannel当中也没有Socket的参数,不知道该怎么办了。肯请了解Remoting的朋友能给我些帮助或提示。
谢谢!