Remoting 获取客户端IP地址
网络文章一大抄真是不假,为了解决在Remoting中获取客户端IP的问题,Google,Baidu了许多文章,
都一个样,真是气死。不过好歹调试通了,把代码贡献出来:
采用Sink方式,至于这个Sink到底是什么原理,还没搞明白,我想大概是和Filter一类的管道类似吧,不管怎么说,
先抄对了能用再说。
以下这段代码,在Remoting服务器端,不用做修改直接照抄,为了简单,把2个类都放一个Sink.cs里面了。
/////////////////////////////////////////////////////////////////////////////////
//Sink.cs Begin
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Channels;
using System.Collections;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Net;
namespace IServer
{
public class ClientIPServerSinkProvider : IServerChannelSinkProvider
{
private IServerChannelSinkProvider next = null;
public ClientIPServerSinkProvider()
{
}
public ClientIPServerSinkProvider(IDictionary properties, ICollection providerData)
{
}
public void GetChannelData(IChannelDataStore channelData)
{
}
public IServerChannelSink CreateSink(IChannelReceiver channel)
{
IServerChannelSink nextSink = null;
if (next != null)
{
nextSink = next.CreateSink(channel);
}
return new ClientIPServerSink(nextSink);
}
public IServerChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
public class ClientIPServerSink : BaseChannelObjectWithProperties, IServerChannelSink, IChannelSinkBase
{
private IServerChannelSink _next;
public ClientIPServerSink(IServerChannelSink next)
{
_next = next;
}
public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers, Stream stream)
{
}
public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers)
{
return null;
}
public System.Runtime.Remoting.Channels.ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream)
{
if (_next != null)
{
IPAddress ip = requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;
//Console.WriteLine(ip.ToString());
CallContext.SetData("ClientIPAddress", ip);
ServerProcessing spres = _next.ProcessMessage(sinkS
tack, requestMsg, requestHeaders, requestStream, out responseMsg, out responseHeaders, out responseStream);
return spres;
}
else
{
responseMsg = null;
responseHeaders = null;
responseStream = null;
return new ServerProcessing();
}
}
public IServerChannelSink NextChannelSink
{
get { return _next; }
set { _next = value; }
}
}
}
/
/Sink.cs End
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
//Server.cs Begin
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using IObject;
尹能静
using cfc.Window;
using cfc.RSA;
using System.Collections;
using System.Runtime.Remoting.Lifetime;
namespace IServer
{
public class Server
{
FormMain f = null;       
TcpServerChannel channel = null;
//因为我是在一个Form里面启动了这个服务端,为了在Form里显示一些信息,所以我把这个From传递进了Server类
public Server(FormMain o)
{
f = o;
}
public void Start()
{
//这是之前用于启动的代码,这种启动方式,是不能获取IP地址的
//channel = new TcpServerChannel(7575);
//ChannelServices.RegisterChannel(channel, false);           
//RemotingConfiguration.RegisterWellKnownServiceType(typeof(IObject.ConfigObject), "Config", WellKnownObjectMode.Singleton);
//------------------------------------------------------------
//这段代码,就是用于启动Remoting的服务端
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
ClientIPServerSinkProvider IpProvider = new ClientIPServerSinkProvider();
provider.Next = IpProvider;//加入接收链
IDictionary props = new Hashtable();
props["port"] = 7575;
channel = new TcpServerChannel(props, provider);
ChannelServices.RegisterChannel(channel, false);
//LifetimeServices.LeaseTime = TimeSpan.Zero;//租用周期,不知道有啥用
RemotingConfiguration.RegisterWellKnownServiceType(typeof(IObject.ConfigObject), "Config", WellKnownObjectMode.Singleton);
//------------------------------------------------------------
//这两个都是Form里我自己定义的方法,各位看官不要看错了. :)
f.WriteMsg("程序已经启动,Port:7575!");
f.Start();
}
public void Stop()
{
if (channel == null) return;           
ChannelServices.UnregisterChannel(channel);
f.WriteMsg("程序已经停止!");
f.Stop();
}
}
}
//Server.cs End
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//FormMain.cs Begin
//FormMain就是用于启动和关闭Remoting Server的WinForm,为了简洁,只保留了部分代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;南阳王朱灿
using System.Windows.Forms;
using cfc.Window;
namespace IServer
{
public partial class FormMain : Form
{
//声明一个Server变量
Server server = null;
public FormMain()
{
InitializeComponent();         
}
private void FormMain_Load(object sender, EventArgs e)
{
server = new Server(this);           
}     
//Server里启动服务端后,调用f.Start(),就是修改一下按钮的状态而已
public void Start()
{
this.button_stop.Enabled = true;
this.button_begin.Enabled = false;
}
public void Stop()
{
this.button_begin.Enabled = true;
this.button_stop.Enabled = false;
}   
//启动按钮,启动服务端
private void button_begin_Click(object sender, EventArgs e)
{
server.Start();
}
//停止按钮,停止服务
private void button_stop_Click(object sender, EventArgs e)
朋友圈设置三天可见怎么设置{
server.Stop();
}
无法获取ip地址}
}
//FormMain.cs End
////////////////////////////////////////////////////////////////////////////////////
/
///////////////////////////////////////////////////////////////////////////////////
//ConfigObject.cs,OnlineClient.cs Begin
//这两个类,其实是Remoting的远程对象,我放在了一个单独的项目里,这个项目Remoting的服务端和客户端都需要引用
using System;
using System.Collections.Generic;
using System.Text;
namespace IObject
{
public class ConfigObject : System.MarshalByRefObject
{
public string RemoteMethod(string key)
{
try
{
//在线用户的记录,在这个方法里可以获取IP地址
string ip = ((System.Net.IPAddress)System.Runtime.Remoting.Messaging.CallContext.GetData("ClientIPAddress")).ToString();
long ticks = System.DateTime.Now.Ticks;
/*
我的需求,是用Remoting做一个应用程序的服务端,这个服务端为若干个客户端提供服务。这个服务端的作用,可以控制客户端的数量,
可以为客
户端提供数据的中转服务,可以为客户端提供数据库连接的参数。(这样就不用每个客户端都配置,也
防止暴露数据库的安全信息)
这个RemoteMethody应该是由Client来调用,但是是在Server端运行,这样Client每次调用的时候,获取到IP地址,记录到OnlineClient类中
OnlineClient是一个静态类,在IServer这个项目中可以访问到
*/
OnlineClient.dic.Add(ip, ticks);
return "OK";
}
catch (Exception ex)
{
//Log(ex.Message);
}
return null;
}
// 这是个心跳函数,由窗口中的Timer定时执行,这样,服务端可以根据OnlineClient.dic中的信息,判断客户端是否在线。
// ticks是时间戳,比如每10秒钟发一次心跳信息,那么在服务端就可以判断,如果超过20秒还没有心跳信息的客户端,就可以认为是离线了
public void HeartBeat(string key)
{
long ticks = System.DateTime.Now.Ticks;
OnlineClient.dic.Add(key, ticks);           
}
}
}
//
using System;
using System.Collections.Generic;
using System.Text;
namespace IObject
{
public static class OnlineClient
{
public static int maxClients = -1; //如果是-1,说明不限制客户端数量,这个值,由IServer来赋值
public static Dictionary<string,long> dic = new Dictionary<string,long>(); 
}
}
//ConfigObject.cs,OnlineClient.cs End
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//客户端代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using IObject;
namespace IClient
{
public partial class WinLogin : Form
{
public WinLogin()
{
InitializeComponent();
}
private void WinLogin_Load(object sender, EventArgs e)
{           
}
//连接服务器的方法
private bool Connect()
{         
string str = string.Format("tcp://{0}:7575/Config", "127.0.0.1");
TcpClientChannel tcp = null;
鳄鱼龟饲养try
{
tcp = new TcpClientChannel();
ChannelServices.RegisterChannel(tcp, false);
ConfigObject obj = (ConfigObject)Activator.GetObject(typeof(ConfigObject), str);
string result = obj.RemoteMethod("key");
if (string.IsNullOrEmpty(result))
{   
return false;
}
}
catch (Exception ex)
{               
return false;
}
finally
{
if (tcp != null) ChannelServices.UnregisterChannel(tcp);
}
return true;
}
入了心的人}
}
////////////////////////////////////////////////////////////////////////////////////
最后总结:
一共有3个项目,分别是IServer(服务端),IClient(客户端),IObject(远程对象)
IServer: Server.cs,Sink.cs,FormMain.cs
IObject:ConfigObject.cs,OnlineClient.cs
IClient:WinLogin.cs
IServer和IClient都需要引用IObject项目
ConfigObject obj = (ConfigObject)Activator.GetObject(typeof(ConfigObject), "tcp://127.0.0.1:7575/Config");
tcp://127.0.0.1:7575/Config中的"Config"是在Server中自己定义的
RemotingConfiguration.RegisterWellKnownServiceType(typeof(IObject.ConfigObject), "Config", WellKnownObjectMode.Singleton);
以上代码都是在Vs2005 c#2.0下测试运行通过

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。