Remoting 获取客户端IP地址

转载 2013年12月04日 19:39:24
网络文章一大抄真是不假,为了解决在Remoting中获取客户端IP的问题,Google,Baidu了许多文章,
都一个样,真是气死。不过好歹调试通了,把代码贡献出来:
http://wenku.baidu.com/view/e8ae2cc189eb172ded63b78e.html

采用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(sinkStack, 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();
        }
      
    }
}


//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下测试运行通过

相关文章推荐

c#获取客户端IP

/// /// 获取客户端IP地址(无视代理) /// /// 若失败则返回回送地址 public static string GetHostAddress...
  • for12
  • for12
  • 2015年12月02日 15:56
  • 2547

C#获取客户端的IP地址

public class IPNet { public static string GetIP4Address() { string I...

服务器反向代理中用于存放客户端原始 IP 地址

在开发工作中,我们常常需要获取客户端的IP。一般获取客户端的IP地址的方法是:request.getRemoteAddr();但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实...

获取客户端的IP地址的方法

获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是 有效的。但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了...

Remoting获取客户端的外网IP地址

其实在服务端Remoting可以像读取一个变量一样来获取到连接过来的客户端的IP,不过这个IP不是太容易的取到。 网上有一个解决方案就是使用配置文件来吸取IP地址,但是这个方案需要我们使用配置文件来...
  • hbxtlhx
  • hbxtlhx
  • 2012年07月11日 10:40
  • 2797

asp.net 获取客户端ip和mac地址

  • 2012年12月10日 15:20
  • 2KB
  • 下载

.net获取客户端IP,Mac地址

  • 2013年03月28日 15:25
  • 3KB
  • 下载

ASP.NET 获取客户端外网IP地址

我们用 Request.ServerVariables( "REMOTE_ADDR ")   来取得客户端的IP地址。但是,如果客户端是使用代理服务器来访问,那取到的就是代理服务器的IP地址,而不是真...
  • biychen
  • biychen
  • 2014年02月12日 15:52
  • 2197

获取客户端IP地址所需Jar包

  • 2016年02月25日 20:35
  • 2.46MB
  • 下载

根据IP获取客户端所在地址

  • 2012年07月20日 14:35
  • 286B
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Remoting 获取客户端IP地址
举报原因:
原因补充:

(最多只允许输入30个字)