> 백엔드 개발 > C#.Net 튜토리얼 > C#NetRemoting은 양방향 통신을 위한 샘플 코드 공유를 구현합니다.

C#NetRemoting은 양방향 통신을 위한 샘플 코드 공유를 구현합니다.

黄舟
풀어 주다: 2017-03-25 11:14:50
원래의
2458명이 탐색했습니다.

이 글에서는 주로 C# NetRemoting을 소개합니다. .Net Remoting은 클라이언트가 Remoting을 통해 채널에 액세스하고 객체, 프록시를 통해 클라이언트 객체로 해결하여 통신

할 일도 없고 원하는 것도 없습니다 양방향 통신을 하고 QQ의 서로 메시지를 보내는 기능 같은 것을 구현하기 위해 .Net Remoting을 배우기 시작했습니다.

.Net Remoting은 클라이언트가 채널에 액세스하여 Remoting을 통해 서버 개체를 생성한 다음 프록시 통신을 통해 클라이언트 개체로 구문 분석합니다. 즉, 개체는 서버에 의해 생성됩니다.

첫 번째는 ICommand 라이브러리

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ICommand
{
  public interface IRemotingObject
  {
    event SendHandler ClientToServer;
    event ReceiveHandler ServerToClient;
    event UserChangedHandler Login;
    event UserChangedHandler Exit;
    /// <summary>
    /// 加法运算
    /// </summary>
    /// <param name="x1">参数1</param>
    /// <param name="x2">参数2</param>
    /// <returns></returns>
    string SUM(int x1, int x2);
    /// <summary>
    /// 获取服务端事件列表
    /// </summary>
    Delegate[] GetServerEventList();
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    void ToServer(object info, string toName);
    /// <summary>
    /// 接受信息
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    void ToClient(object info, string toName);
    void ToLogin(string name);
    void ToExit(string name);
  }
  /// <summary>
  /// 客户端发送消息
  /// </summary>
  /// <param name="info">信息</param>
  /// <param name="toName">发送给谁,""表示所有人,null表示没有接收服务器自己接收,其他表示指定某人</param>
  public delegate void SendHandler(object info, string toName);
  /// <summary>
  /// 客户端接收消息
  /// </summary>
  /// <param name="info">信息</param>
  /// <param name="toName">发送给谁,""表示所有人,null表示没有接收服务器自己接收,其他表示指定某人</param>
  public delegate void ReceiveHandler(object info, string toName);
  /// <summary>
  /// 用户信息事件
  /// </summary>
  /// <param name="name">用户名</param>
  public delegate void UserChangedHandler(string name);
}
로그인 후 복사
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ICommand
{
  public class SwapObject : MarshalByRefObject
  {

    public event ReceiveHandler SwapServerToClient 
    {
      add { _receive += value; }
      remove { _receive -= value; }
    }
    /// <summary>
    /// 接受信息
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    public void ToClient(object info, string toName)
    {
      if (_receive != null)
        _receive(info, toName);
    }
    //无限生命周期 
    public override object InitializeLifetimeService()
    {
      return null;
    }

    private ReceiveHandler _receive;
  } 
}
로그인 후 복사

첫 번째 클래스는

인터페이스

와 일부 대리자에는 실질적인 내용이 없습니다. 두 번째 클래스는 이전 인터페이스 클래스의 ToClient 이벤트 및 메서드를 정의하며 해당 기능은 다음과 같습니다. 🎜>다음에는 ICommand 인터페이스를 통합하는 실질적인 데이터 클래스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICommand;

namespace NetRemoting
{
  public class RemotingObject : MarshalByRefObject, IRemotingObject
  {
    /// <summary>
    /// 发送事件
    /// </summary>
    public event SendHandler ClientToServer
    {
      add { _send += value; }
      remove { _send -= value; }
    }
    /// <summary>
    /// 接收消息事件
    /// </summary>
    public event ReceiveHandler ServerToClient;
    /// <summary>
    /// 发送事件
    /// </summary>
    public event UserChangedHandler Login
    {
      add { _login += value; }
      remove { _login -= value; }
    }
    /// <summary>
    /// 发送事件
    /// </summary>
    public event UserChangedHandler Exit
    {
      add { _exit += value; }
      remove { _exit -= value; }
    }
    /// <summary>
    /// 加法运算
    /// </summary>
    /// <param name="x1">参数1</param>
    /// <param name="x2">参数2</param>
    /// <returns></returns>
    public string SUM(int x1, int x2)
    {
      return x1 + "+" + x2 + "=" + (x1 + x2);
    }
    /// <summary>
    /// 绑定服务端向客户端发送消息的事件方法
    /// </summary>
    /// <param name="receive">接收事件</param>
    public Delegate[] GetServerEventList()
    {
      return this.ServerToClient.GetInvocationList();
    }
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    public void ToServer(object info, string toName)
    {
      if (_send != null)
        _send(info, toName);
    }
    /// <summary>
    /// 接收消息
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    public void ToClient(object info, string toName)
    {
      if (_receive != null)
        _receive(info, toName);
    }
    /// <summary>
    /// 登录
    /// </summary>
    /// <param name="name">用户名</param>
    public void ToLogin(string name)
    {
      if (!_nameHash.Contains(name))
      {
        _nameHash.Add(name);
        if (_login != null)
          _login(name);
      }
      else
      { throw new Exception("用户已存在"); }
    }
    /// <summary>
    /// 退出
    /// </summary>
    /// <param name="name">用户名</param>
    public void ToExit(string name)
    {
      if (_nameHash.Contains(name))
      {
        _nameHash.Remove(name);
        if (_exit != null)
          _exit(name);
      }
    }

    private SendHandler _send;
    private ReceiveHandler _receive;
    private UserChangedHandler _login;
    private UserChangedHandler _exit;
    private HashSet<string> _nameHash = new HashSet<string>();
  }
}
로그인 후 복사

가 있습니다. 이 클래스는 Remoting에서 전달하는 개체가

참조 형식이므로 MarshalByRefObject

를 통합합니다.

이므로 전달된 원격 개체 클래스는

MarshalByRefObject를 상속해야 합니다. MarshalByRefObject에 대한 MSDN의 설명은 다음과 같습니다. MarshalByRefObject는 메시지를 교환하기 위해 프록시를 사용하여 응용 프로그램 도메인 경계를 넘어 통신하는 개체의 기본 클래스입니다. 값으로 마샬링하는 것은 암시적입니다. 원격 애플리케이션이 값으로 마샬링되는 개체를 참조하는 경우 개체의 복사본이 원격 경계를 넘어 전달됩니다. 이는 복사 메서드 대신 프록시 메서드를 사용하여 통신하려고 하기 때문에 필요합니다. MarshallByRefObject를 상속합니다. 이 클래스는 주로 클라이언트가 이벤트를 트리거하는 일부 메소드, ToServer, ToClient, ToLogin, ToExit 및 일부 이벤트, 클라이언트가 서버로 보낸 이벤트, 서버가 서버로 보낸 이벤트를 정의합니다.

_nameHash는

사용자가 로그인한
것을 기록합니다. 다음으로 클라이언트와 서버는

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using NetRemoting;
using System.Collections;
using System.Runtime.Serialization.Formatters;
using ICommand;

namespace NetRemotingServer
{
  public partial class Server : Form
  {
    public Server()
    {
      InitializeComponent();
      Initialize();
    }
    /// <summary>
    /// 注册通道
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Server_Load(object sender, EventArgs e)
    {

      ChannelServices.RegisterChannel(_channel, false);
      //RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton); //a方案
      /*将给定的 System.MarshalByRefObject 转换为具有指定 URI 的 System.Runtime.Remoting.ObjRef 类的实例。
       ObjRef :存储生成代理以与远程对象通信所需要的所有信息。*/
      ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage");//b方案
      _remotingObject.ClientToServer += (info, toName) =>
      {
        rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(info.ToString() + "\r\n"); }));
        SendToClient(info, toName);
      };
      _remotingObject.Login += (name) =>
      {
        rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(name + " 登录" + "\r\n"); }));
      };
      _remotingObject.Exit += (name) =>
      {
        rxtInfo.Invoke((MethodInvoker)(() => { rxtInfo.AppendText(name + " 退出" + "\r\n"); }));
      };
    }
    /// <summary>
    /// 注销通道
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Server_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (_channel != null)
      {
        _channel.StopListening(null);
        ChannelServices.UnregisterChannel(_channel);
      }
    }
    /// <summary>
    /// 广播消息
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnSend_Click(object sender, EventArgs e)
    {
      SendToClient(txtSend.Text, txtName.Text);
    }
    /// <summary>
    /// 发送消息到客户端
    /// </summary>
    /// <param name="info"></param>
    /// <param name="toName"></param>
    private void SendToClient(object info, string toName)
    {
      //foreach (var v in _remotingObject.GetServerEventList())
      //{
      //  try
      //  {
      //    ReceiveHandler receive = (ReceiveHandler)v;
      //    receive.BeginInvoke(info, toName, null, null);
      //  }
      //  catch
      //  { }
      // }
      _remotingObject.ToClient(txtSend.Text, txtName.Text);
    }
    /// <summary>
    /// 初始化
    /// </summary>
    private void Initialize()
    {
      //设置反序列化级别 
      BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
      BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
      serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支持所有类型的反序列化,级别很高 
      IDictionary idic = new Dictionary<string, string>();
      idic["name"] = "serverHttp";
      idic["port"] = "8022";
      _channel = new HttpChannel(idic, clientProvider, serverProvider);
      _remotingObject = new RemotingObject();
    }

    HttpChannel _channel;
    private RemotingObject _remotingObject;


  }
}
로그인 후 복사

를 처리합니다.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ICommand;
using System.Runtime.Serialization.Formatters;
using System.Collections;

namespace NetRemotingClient
{
  public partial class Client : Form
  {
    public Client()
    {
      InitializeComponent();
    }
    /// <summary>
    /// 注册通道
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Client_Load(object sender, EventArgs e)
    {
      try
      {
        //设置反序列化级别 
        BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
        BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支持所有类型的反序列化,级别很高 
        //信道端口 
        IDictionary idic = new Dictionary<string, string>();
        idic["name"] = "clientHttp";
        idic["port"] = "0";
        HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider);
        ChannelServices.RegisterChannel(channel, false);
        _remotingObject = (IRemotingObject)Activator.GetObject(typeof(IRemotingObject), "http://localhost:8022/SumMessage");
        //_remotingObject.ServerToClient += (info, toName) => { rtxMessage.AppendText(info + "\r\n"); };
        SwapObject swap = new SwapObject();
        _remotingObject.ServerToClient += swap.ToClient;
        swap.SwapServerToClient += (info, toName) =>
        {
          rtxMessage.Invoke((MethodInvoker)(() =>
        {
          if (toName == txtLogin.Text || toName == "")
            rtxMessage.AppendText(info + "\r\n");
        }));
        };
      }
      catch (Exception ex)
      { MessageBox.Show(ex.Message); }
    }
    /// <summary>
    /// 登录
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnLogin_Click(object sender, EventArgs e)
    {
      try
      {
        if (txtLogin.Text == "")
          throw new Exception("用户名不得为空");
        _remotingObject.ToLogin(txtLogin.Text);
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }
    /// <summary>
    /// 退出
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Client_FormClosing(object sender, FormClosingEventArgs e)
    {
      try
      {
        _remotingObject.ToExit(txtLogin.Text);
      }
      catch
      { }
    }
    /// <summary>
    /// 发送
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnSend_Click(object sender, EventArgs e)
    {
      //rtxMessage.AppendText(_remotingObject.SUM(2, 4) + "\r\n");
      _remotingObject.ToServer(txtSend.Text, txtName.Text);
    }


    private IRemotingObject _remotingObject;

  }
}
로그인 후 복사
서버 구현 단계:

1. 등록 채널

애플리케이션 도메인 간에 통신하려면 채널을 구현해야 합니다. 앞서 언급했듯이 Remoting은 TcpChannel과 HttpChannel이라는 두 가지 유형의 채널을 포함하는 IChannel 인터페이스를 제공합니다. 직렬화된 데이터의 성능과 형식이 다르다는 점을 제외하면 이 두 유형은 완전히 동일한 방식으로 구현되므로 아래에서는 TcpChannel을 예로 들어 보겠습니다.

TcpChannel을 등록하려면 먼저 프로젝트에 "System.Runtime.Remoting" 참조를 추가한 다음 System.Runtime.Remoting.Channel.Tcp 네임스페이스를 사용하세요. 코드는 다음과 같습니다.

TcpChannel channel = new TcpChannel(8022);
ChannelServices.RegisterChannel(channel);
로그인 후 복사

채널 객체를 인스턴스화할 때 포트 번호를 매개변수로 전달합니다. 그런 다음

정적 메서드 RegisterChannel()을 호출하여 채널 개체를 등록합니다.

2. 원격 개체 등록

채널 등록 후 원격 개체를 활성화하려면 해당 개체를 채널에 등록해야 합니다. 활성화 모드에 따라 개체를 등록하는 방법이 다릅니다.

(1) SingleTon 모드

WellKnown 개체의 경우 정적 메서드 RemotingConfiguration.RegisterWellKnownServiceType()을 통해 달성할 수 있습니다.

RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(ServerRemoteObject.ServerObject),
        "ServiceMessage",WellKnownObjectMode.SingleTon);
로그인 후 복사

( 2) SingleCall 모드

객체 등록 방법은 기본적으로 SingleTon 모드와 동일하며, 열거 매개변수 WellKnownObjectMode만 SingleCall로 변경하면 됩니다.

RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(ServerRemoteObject.ServerObject),
        "ServiceMessage",WellKnownObjectMode.SingleCall);
로그인 후 복사

클라이언트 구현

단계:


등록 채널:

TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel);
로그인 후 복사
클라이언트가 채널을 인스턴스화할 때 기본 생성자가 호출됩니다. 즉, 포트 번호가 전달되지 않습니다. 실제로 이 포트 번호는 필수이지만 해당 사양은 나중에 Uri의 일부로 배치됩니다.

2. 원격 객체를 획득합니다.

서버 측과 마찬가지로 다양한 활성화 모드에 따라 클라이언트 구현 방식이 결정됩니다. 그러나 이러한 차이점은 WellKnown 활성화 모드와 클라이언트 활성화 모드 사이에서만 발생합니다. SingleTon 및 SingleCall 모드의 경우 클라이언트 구현은 완전히 동일합니다.

(1) WellKnown 활성화 모드

서버 측에서 Well-Known 원격 개체를 얻으려면 Activator의 GetObject() 메서드를 통해 얻을 수 있습니다. 프로세스:

ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(
       typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");
로그인 후 복사

首先以WellKnown模式激活,客户端获得对象的方法是使用GetObject()。其中参数第一个是远程对象的类型。第二个参数就是服务器端的uri。如果是http通道,自然是用http://localhost:8022/ServiceMessage了。因为我是用本地机,所以这里是localhost,你可以用具体的服务器IP地址来代替它。端口必须和服务器端的端口一致。后面则是服务器定义的远程对象服务名,即ApplicationName属性的内容。

//设置反序列化级别 
        BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
        BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;//支持所有类型的反序列化,级别很高 
        //信道端口 
        IDictionary idic = new Dictionary<string, string>();
        idic["name"] = "clientHttp";
        idic["port"] = "0";
        HttpChannel channel = new HttpChannel(idic, clientProvider, serverProvider);
로그인 후 복사

从上述代码中可以看到注册方式有所变化,那是因为客户端注册服务端的事件时会报错“不允许类型反序列化”。

还有一个需要注意的是:

ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage");
//RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton);
//调用系统自动创建,导致拿不到_remotingObject对象的实例化,这样后期绑定事件就无法操作下去了,当然也可以直接静态事件绑定,这样就不需要手动实例化对象了
로그인 후 복사

通过该方法手动创建_remotingObject这个对象的实例化。

然后之前讲到了一个SwapObject这个类,这个类的作用是事件交换。

_remotingObject.ServerToClient +=方法();
//这样因为这个方法是客户端的,服务端无法调用,所以需要一个中间转换的
 SwapObject swap = new SwapObject();//先创建一个Swap对象
 _remotingObject.ServerToClient += swap.ToClient;
 //然后服务端事件发信息给swap,然后swap再通过事件发消息给客户端,swap是客户端创建的所以可以发送,而swap是服务端的类,所以服务端也能识别,swap起到了中间过渡的作用
 swap.SwapServerToClient +=方法();
로그인 후 복사

위 내용은 C#NetRemoting은 양방향 통신을 위한 샘플 코드 공유를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿