Ansel's Blog

It would probably render the application more complex

博客园 首页 新随笔 联系 订阅 管理
  81 Posts :: 1 Stories :: 114 Comments :: 2 Trackbacks



.net Remoting的一个具体实现例子

这里.net remoting 的想实现一个银行转帐的过程
把用户输入的金额从用户A转入到用户B

数据库:
使用SQL SERVER里面的那个Pubs数据库,在这个数据库里面创建一个数据表叫Budget
只包含两个字段,Name、Money
CREATE TABLE Budget
(
   NAME         VARCHAR(8)      NOT NULL, 
   MONEY      FLOAT
)
GO

服务器端:
只需要创建一个普通的Win32窗体即可



用于启动服务器的服务
这段代码只是在宿主服务器上启动一个Remoting服务端口或者是在Tcp或者是在Http上

        private void bttnStart_Click(object sender, System.EventArgs e)
        
{
            ChannelServices.RegisterChannel(
new HttpChannel(7608));
            ChannelServices.RegisterChannel(
new TcpChannel(7711));

            RemotingConfiguration.ApplicationName
="TranserRemotingService"
            
//如果这个宿主程序是注册在IIS上的话,那么这个ApplicationName就相当于IIS上的虚拟目录
            WellKnownServiceTypeEntry WKSTE = new WellKnownServiceTypeEntry(typeof(TransferMoney),"URLAssemblyComponent",WellKnownObjectMode.Singleton);
            RemotingConfiguration.RegisterWellKnownServiceType(WKSTE);
            bttnStart.Enabled
=false;
        }


        
private void button2_Click(object sender, System.EventArgs e)
        
{
            Close();
        }


下面这段代码就是Remoting服务的最重要的部分,具体实现都是在这里实施
其中“TransMoney”是一个存储过程,它做了把钱从A的帐户转到B的实际操作

        public class TransferMoney:MarshalByRefObject,ITransfer
        
{
            
public string getTransferResult(double money)
            
{
                
try
                
{
                    transferMoneyFromAToB(money);
                    
return "Transfer Money Succeed!";
                }

                
catch(Exception Ex)
                
{
                    
return "Transfer Money Failed! "+Ex.Message;
                }
        
            }

            
private static void transferMoneyFromAToB(double money)
            
{
                SqlCommand sqlcom 
=new SqlCommand("Exec TransMoney "+money.ToString()+",'A','B'");
                databaseAccess obj
=new databaseAccess();
                sqlcom.Connection
=obj.getConnection();            
                sqlcom.Connection.Open();
                sqlcom.ExecuteNonQuery();
                sqlcom.Connection.Close();
                
//throw new Exception("Operation failed when transfer money into A!");
            }


        }


这就是那个存储过程,考过去就可以用了
CREATE Proc TransMoney(
@Money float,
@FromUser varchar(8),
@ToUser varchar(8)
)
as
begin
  begin tran
  update budget set Money=Money-@Money where Name =@FromUser 
  if @@Error
<>0
  begin
    rollback tran
    RaisError ('妈的给钱时银行柜员机出错了!:( ', 16, 1) with Log  --记录日志
    return -1
  end
  update budget set Money=Money+@Money where Name =@ToUser 
  if @@Error
<>0
  begin
    rollback tran
    RaisError ('妈的收钱时银行柜员机出错了!:( ', 16, 1) with Log  --记录日志
    return -1
  end
  commit tran
  return 1  
end

这里还要注意一个类“databaseAccess”需要加入到项目中去,代码如下:

using System;
using System.Data.SqlClient;

namespace RemotingConsole
{
 /// 
<summary>
 ///   The databaseAccess sets up the connection to the data repository.
 /// 
</summary>
  /// 
<remarks>
  ///   Author: Ansel Du; arkstars@hotmail.com; Mar. 2005
  /// 
</remarks>
 public class databaseAccess 
 {
 
       private static string sqlConnectionString = "server=61.129.112.20;database=Pubs;User ID=sa;Password=";
       private SqlConnection sqlConnection;

     /// 
<summary>
     ///   Public class constructor.
     /// 
</summary>
     protected internal databaseAccess() 
  {
   sqlConnection = new SqlConnection(databaseAccess.sqlConnectionString);
  }

     /// 
<summary>
  ///   The getConnection method sets up the database connection
     /// 
</summary>
  /// 
<returns>the (closed) SQL connection object</returns>
  protected internal SqlConnection getConnection() //组合体内子类也可见
  {
      return sqlConnection;
  }
 }
}



但是大家可能注意到了在Remoting的服务那一段代码里面有一个接口"ITransfer"
public class TransferMoney:MarshalByRefObject,ITransfer
...
对,我为了让客户端容易而且安全地调用宿主服务器的服务,故意设了一个接口,这个接口需要你单独在创建一个项目来完成它:

using System;


namespace AssemblyComponent
{

    /// 
<summary>
    /// 定义一个接口
    /// 
</summary>
    public interface ITransfer 
    {
        string getTransferResult(double money);
    }
}

这个接口你创建了以后,生成一个Assembly(组合体)的DLL形式的东东
这个东东首先需要Remoting进行引用,而且服务器需要实现这个接口,具体实现上面的代码已经写了,你可以对照着看看就明白了。相信大家一定比我聪明!
 
然后生成,服务器端的服务这就完成了,这时候生成了一个Exe的组合文件,用户只需要那个Assembly文件和这个可执行的组合文件。那么就可以启动一个服务器了!
Ok!下面我们编写一个客户端来调用这个服务器的服务。

这个就是客户端的界面,很简单吧?这时候用户只需输入宿主服务器的IP和要转帐的金额数,选择一个传输通道TCP通道或者HTTP通道即可完成操作。

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using AssemblyComponent;

namespace remotingClient
{

            .......
        private void button1_Click(object sender, System.EventArgs e)
        {
            string msg;
            try
            {
                double atemp=Convert.ToDouble(tBMoney.Text);
            }
            catch(Exception exx)
            {
                msg="输入的并非数字类型-〉 "+ exx.Message;
            }

            try
            {                                
                //通过Activator.GetObject得到代理类的实例
                if(rbtHttp.Checked==true)//Http信道
                {
                    ITransfer ProxyObj=(ITransfer) Activator.GetObject(typeof(ITransfer),"http://"+tbHostServer.Text+":7608/TranserRemotingService/URLAssemblyComponent");
                    double atemp=Convert.ToDouble(tBMoney.Text);
                    msg=ProxyObj.getTransferResult(Convert.ToDouble(atemp));
                }
                else//Tcp信道
                {
                    ITransfer ProxyObj=(ITransfer) Activator.GetObject(typeof(ITransfer),"tcp://"+tbHostServer.Text+":7711/TranserRemotingService/URLAssemblyComponent");
                    double atemp=Convert.ToDouble(tBMoney.Text);
                    msg=ProxyObj.getTransferResult(Convert.ToDouble(atemp));
                }
                //msg=ProxyObj.a(2);
                
            }
            catch(Exception Ex)
            {
                msg="转帐失败-〉 "+ Ex.Message;                
            }
            MessageBox.Show(msg);            
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            rbtTcp.Checked=true;
            rbtHttp.Checked=false;
            
        }

        private void button2_Click(object sender, System.EventArgs e)
        {
          Close();
        }

        private void rbtTcp_CheckedChanged(object sender, System.EventArgs e)
        {
            rbtHttp.Checked=!rbtTcp.Checked;                        
        }

        private void rbtHttp_CheckedChanged(object sender, System.EventArgs e)
        {
            rbtTcp.Checked=!rbtHttp.Checked;    
        }
    }
}

Ok,好像不缺少什么东西了,慢慢看吧,是不是很简单哦

posted on 2005-08-17 15:31 Ansel 阅读(1931) 评论(9)  编辑 收藏 网摘 所属分类: 关于.Net Remoting

Feedback

#1楼  2005-08-17 17:40 ξσ Dicky σξ      
厉害~~
  回复  引用  查看    

看看
  回复  引用    

#3楼  2005-08-17 22:47 啊啊 [未注册用户]
你采用的是 WellKnownObjectMode.Singleton模式,服务器端还要对线程等进行处理吧。
  回复  引用    

#4楼 [楼主] 2005-08-18 09:02 Ansel      
这个应该是使用Singlecall模式
对于不同的业务方式应该使用不同的激活模式
这里只是举个例子

  回复  引用  查看    

#5楼 [楼主] 2005-08-18 09:38 Ansel      
创建一个宿主应用程序服务器的关键点在于:
1、需要注册通道HTTP或者TCP通道
这个是由ChannelServices这个类对象来完成的
(ChannelServices是来自于名字空间 System.Runtime.Remoting.Channels)
ChnnelServices.RegisterChannel(new HttpChannel(XX));
ChnnelServices.RegisterChannel(new TcpChannel(YY));
当你注册了这个通道以后,服务器启用的时候就会自动在你注册的通信通道上进行数据交互,这一点你大可不要关心。

2、需要创建一个WellKnow服务类型入口,这个服务类型入口即是基于你创建的那个继承于MarshellByRefObject父类的类。
WellKnowServiceTypeEntry WSTE=WellKnowServiceTypeEntry(typeof(你所创建的那个Remoting类),"服务别名",WellKnowObjectMode.Singlecall);

3、注册这个WellKnowServiceTypeEntry
RemotingConfiguration.RegisterWellKnowServiceType(WSTE);

这个服务类型入口对象创建好以后,只要注册以后即可启动


  回复  引用  查看    

#6楼 [楼主] 2005-08-18 09:55 Ansel      
创建一个Remoting客户端来调用这个Remoting服务的关键点在于:
使用Activator.GetObject来创建一个指向接口的一个代理类对象

ITransfer ProxyObj=(ITransfer) Activator.GetObject(typeof(ITransfer),"tcp://"+tbHostServer.Text+":7711/TranserRemotingService/URLAssemblyComponent");

这句话中Activator.GetObject的第一个参数是识别类型的,这里的类型就是那个自定的接口类型,第二个参数是访问服务的一个具体的路径中的那个“TranserRemotingService”其实就是我们在服务器的一个应用名称(参看宿主服务器)
RemotingConfiguration.ApplicationName="TranserRemotingService";
最后面那个URLAssemblyComponent就是我们创建服务器的时候得那个WellKnow服务类型入口的服务别名。

这样客户端就可以明白无误地根据这个指针-->根据通信通道以及端口-->再到那个服务的应用名称(类似于IIS里面的虚拟服务器名称)-->再到我们定义的那个我们定义并且注册了的WellKnow服务类型入口(WellKnownServiceTypeEntry )的别名,然后就在客户端生成了一个代理类,我们使用这个代理类就可以调用远程服务器上的那些Remoting服务方法。

  回复  引用  查看    

#7楼  2006-05-13 18:05 Jackfly [未注册用户]
如何在互联网上运行?
  回复  引用    

#8楼  2006-05-17 09:22 qin [未注册用户]
问题:执行删除操作时候,出现异常但是“由于安全限制,无法访问类型System.Runtime.Remoting.ObjRef”,请问错误出在哪?如何解决?
详细的情况如下:
1、先定义了一个接口
public interface IDataBaseOperator
{
int Update( object sender );

int Add( object sender );

int Delete( object sender );

DataSet Select( string selectCondition, bool selMode );
}
用于对数据库执行4种操作
2、然后在定义一远程对象
public class Customers: MarshalByRefObject,IDataBaseOperator
3、构建宿主程序,使用配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="SingleCall"
type="CustomersLibrary.Customers, CustomersLibrary"
objectUri="Customers" />
</service>
<channels>
<channel port="8086" ref="tcp"/>
</channels>
<!--leaseManagerPollTime="7S"-->
<lifetime
leaseTime="7M"
sponsorshipTimeout="7M"
renewOnCallTime="7M"
/>
</application>
</system.runtime.remoting>
</configuration>

4、构建客户端,也使用配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown type="Customers, CustomersLibrary" url="tcp://192.168.1.18:8086/Customers" />
</client>
<channels>
<channel ref="tcp" port="0"></channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
执行操作
{
RemotingConfiguration.Configure( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile );

Customers obj2 = new Customers();

if (obj2 == null)
{
System.Console.WriteLine(
"Could not locate TCP server");
}

try
{
obj2.ID = "123456-123456";
obj2.Delete(obj2);
}
catch ( Exception ex )
{
MessageBox.Show(ex.Message);
}
}
但是系统提示“由于安全限制,无法访问类型System.Runtime.Remoting.ObjRef”,请问错误出在哪?如何解决?谢谢!

  回复  引用    

#9楼  2008-09-16 15:21 sd [未注册用户]
写入FormatterSinkProvider节点

  回复  引用    





标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-08-18 09:43 编辑过
Google站内搜索

China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!
开发者征途系统新作:《设计模式——基于C#的工程化实现及扩展》

相关文章:

相关链接: