我不打算回答这个问题,因为你已经回答了,(哦,什么?现在已经 2.5 岁了!?)答案...
为了让我的服务与桌面交互,无论 WHAT 桌面,也无论有多少桌面,甚至该服务是否在与桌面应用程序相同的计算机上运行!这与我得到的东西无关......我不会让你厌烦细节,我只会给你肉和土豆,如果你想看更多,请告诉我......
好的。我做的第一件事是创建一个广告服务。这是服务运行的一个线程,它打开一个 UDP 套接字来监听网络上的广播。然后,使用相同的代码,我与客户端应用程序共享它,但它调用 Advertise.CLIENT,而不是 Advertise.SERVER...客户端打开我希望服务打开的端口,并广播一条消息,“你好……有人在外面吗??”,询问他们是否有任何服务器在监听,如果有,回复这个 IP 地址您的计算机名称、IP 地址和端口号,我可以在哪里找到 .NET 远程处理服务...”然后它会等待一小段超时时间,收集它得到的响应,如果不止一个,它会显示用户有一个对话框和一个响应的服务列表......然后客户端选择一个,或者,如果只有一个响应,它将调用 Connect((TServerResponse) res); 对此,建立连接。此时,服务器正在使用带有 WellKnownClientType 和 WellKnownServerType 的 Remoting Services 将自己放在那里...
我认为您对我的“自动服务定位器”不太感兴趣,因为很多人不喜欢 UDP,当您的应用开始在大型网络上广播时更是如此。所以,我假设你会对我的 RemotingHelper 更感兴趣,它可以让客户端连接到服务器。它看起来像这样:
public static Object GetObject(Type type)
{
try {
if(_wellKnownTypes == null) {
InitTypeCache();
}
WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type];
if(entr == null) {
throw new RemotingException("Type not found!");
}
return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl);
} catch(System.Net.Sockets.SocketException sex) {
DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject(). Error: {0}.", sex.Message);
Disconnect();
if(Connect()) {
return GetObject(type);
}
}
return null;
}
private static void InitTypeCache()
{
if(m_AdvertiseServer == null) {
throw new RemotingException("AdvertisementServer cannot be null when connecting to a server.");
}
_wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>();
Dictionary<string, object> channelProperties = new Dictionary<string, object>();
channelProperties["port"] = 0;
channelProperties["name"] = m_AdvertiseServer.ChannelName;
Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>();
binFormatterProperties["typeFilterLevel"] = "Full";
if(Environment.UserInteractive) {
BinaryServerFormatterSinkProvider binFormatterProvider = new BinaryServerFormatterSinkProvider(binFormatterProperties, null);
_serverChannel = new TcpServerChannel(channelProperties, binFormatterProvider);
// LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this
// channel registered in this AppDomain.
ChannelServices.RegisterChannel(_serverChannel, false);
}
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus)));
RegisterType(typeof(IPawnStatServiceStatus),m_AdvertiseServer.RunningStatusURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService)));
RegisterType(typeof(IPawnStatService), m_AdvertiseServer.RunningServerURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration)));
RegisterType(typeof(IServiceConfiguration), m_AdvertiseServer.RunningConfigURL.ToString());
}
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)]
public static void RegisterType(Type type, string serviceUrl)
{
WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl);
if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) {
RemotingConfiguration.RegisterWellKnownClientType(clientType);
}
_wellKnownTypes[type] = clientType;
}
public static bool Connect()
{
// Init the Advertisement Service, and Locate any listening services out there...
m_AdvertiseServer.InitClient();
if(m_AdvertiseServer.LocateServices(iTimeout)) {
if(!Connected) {
bConnected = true;
}
} else {
bConnected = false;
}
return Connected;
}
public static void Disconnect()
{
if(_wellKnownTypes != null) {
_wellKnownTypes.Clear();
}
_wellKnownTypes = null;
if(_serverChannel != null) {
if(Environment.UserInteractive) {
// LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel...
ChannelServices.UnregisterChannel(_serverChannel);
// LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered!
_serverChannel = null;
}
}
bConnected = false;
}
}
所以,这就是我的远程代码的核心,它允许我编写一个客户端,而不必知道服务安装在哪里,或者网络上运行了多少服务。这使我可以通过网络或本地计算机与它进行通信。让两个或两个以上的人运行该应用程序不是问题,但是,你的可能。现在,我有一些复杂的回调代码,我在其中注册事件以通过远程处理通道,所以我必须有代码检查客户端是否仍然连接,然后再向客户端发送发生事件的通知.另外,如果您为多个用户运行,您可能不想使用 Singleton 对象。这对我来说很好,因为服务器拥有对象,它们是服务器所说的任何东西。因此,例如,我的 STATS 对象是一个 Singleton。当每个人都将看到相同的数据时,没有理由为每个连接创建它的实例,对吧?
如有必要,我可以提供更多代码块。当然,这只是完成这项工作的整体情况的一小部分……更不用说订阅提供商等等了。
为了完整起见,我包含了代码块,以使您的服务在整个流程的生命周期内保持连接。
public override object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if(lease.CurrentState == LeaseState.Initial) {
lease.InitialLeaseTime = TimeSpan.FromHours(24);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(30);
lease.RenewOnCallTime = TimeSpan.FromHours(1);
}
return lease;
}
#region ISponsor Members
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
public TimeSpan Renewal(ILease lease)
{
return TimeSpan.FromHours(12);
}
#endregion
如果你将 ISponsor 接口作为你的服务器对象的一部分,你可以实现上面的代码。
希望其中的一些有用。