C# 實現Remoting雙向通訊

本篇文章主要介紹了C# 實現Remoting雙向通訊,.Net Remoting 是由客戶端經過Remoting,訪問通道以得到服務端對象,再經過代理解析爲客戶端對象來實現通訊的php

 

閒來無事想玩玩雙向通訊,實現相似QQ的互發消息的功能。因而乎開始學習.Net Remoting.html

.Net Remoting 是由客戶端經過Remoting,訪問通道以得到服務端對象,再經過代理解析爲客戶端對象來實現通訊的。也就是說對象是由服務端建立的。服務器

先上代碼tcp

首先是ICommand庫ide

1函數

2性能

3學習

4this

5spa

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

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);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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接口的實質性的數據類

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

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>();

  }

}

該類集成了MarshalByRefObject

因爲Remoting傳遞的對象是以引用的方式,所以所傳遞的遠程對象類必須繼承MarshalByRefObject。MSDN對MarshalByRefObject的說明是:MarshalByRefObject 是那些經過使用代理交換消息來跨越應用程序域邊界進行通訊的對象的基類。不是從 MarshalByRefObject 繼承的對象會以隱式方式按值封送。當遠程應用程序引用一個按值封送的對象時,將跨越遠程處理邊界傳遞該對象的副本。由於您但願使用代理方法而不是副本方法進行通訊,所以須要繼承MarshallByRefObject。

該類主要是定義了一些方法用於客戶端觸發事件,ToServer,ToClient,ToLogin,ToExit以及一些事件,客戶端發向服務端的事件,和服務端發向客戶端的事件。

_nameHash 只是記錄有哪些用戶登陸了。

接下去就是客戶端和服務端了。

首先服務端:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

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;

 

 

  }

}

而後客戶端:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

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;

 

  }

}

服務端實現步驟:

一、註冊通道

要跨越應用程序域進行通訊,必須實現通道。如前所述,Remoting提供了IChannel接口,分別包含TcpChannel和HttpChannel兩種類型的通道。這兩種類型除了性能和序列化數據的格式不一樣外,實現的方式徹底一致,所以下面咱們就以TcpChannel爲例。

註冊TcpChannel,首先要在項目中添加引用「System.Runtime.Remoting」,而後using名字空間:System.Runtime.Remoting.Channel.Tcp。代碼以下:

1

2

TcpChannel channel = new TcpChannel(8022);

ChannelServices.RegisterChannel(channel);

在實例化通道對象時,將端口號做爲參數傳遞。而後再調用靜態方法RegisterChannel()來註冊該通道對象便可。

二、註冊遠程對象

註冊了通道後,要能激活遠程對象,必須在通道中註冊該對象。根據激活模式的不一樣,註冊對象的方法也不一樣。

(1) SingleTon模式

對於WellKnown對象,能夠經過靜態方法RemotingConfiguration.RegisterWellKnownServiceType()來實現:

1

2

3

RemotingConfiguration.RegisterWellKnownServiceType(

        typeof(ServerRemoteObject.ServerObject),

        "ServiceMessage",WellKnownObjectMode.SingleTon);

(2)SingleCall模式

註冊對象的方法基本上和SingleTon模式相同,只須要將枚舉參數WellKnownObjectMode改成SingleCall就能夠了。

 

1

2

3

RemotingConfiguration.RegisterWellKnownServiceType(

        typeof(ServerRemoteObject.ServerObject),

        "ServiceMessage",WellKnownObjectMode.SingleCall);

客戶端實現步驟:

一、註冊通道:

1

2

TcpChannel channel = new TcpChannel();

ChannelServices.RegisterChannel(channel);

注意在客戶端實例化通道時,是調用的默認構造函數,即沒有傳遞端口號。事實上,這個端口號是缺一不可的,只不過它的指定被放在後面做爲了Uri的一部分。

二、得到遠程對象

與服務器端相同,不一樣的激活模式決定了客戶端的實現方式也將不一樣。不過這個區別僅僅是WellKnown激活模式和客戶端激活模式之間的區別,而對於SingleTon和SingleCall模式,客戶端的實現徹底相同。

(1) WellKnown激活模式

要得到服務器端的知名遠程對象,可經過Activator進程的GetObject()方法來得到:

1

2

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屬性的內容。

1

2

3

4

5

6

7

8

9

//設置反序列化級別

        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);

從上述代碼中能夠看到註冊方式有所變化,那是由於客戶端註冊服務端的事件時會報錯「不容許類型反序列化」。

還有一個須要注意的是:

1

2

3

ObjRef objRef = RemotingServices.Marshal(_remotingObject, "SumMessage");

//RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), "SumMessage", WellKnownObjectMode.Singleton);

//調用系統自動建立,致使拿不到_remotingObject對象的實例化,這樣後期綁定事件就沒法操做下去了,固然也能夠直接靜態事件綁定,這樣就不須要手動實例化對象了

經過該方法手動建立_remotingObject這個對象的實例化。

而後以前講到了一個SwapObject這個類,這個類的做用是事件交換。

1

2

3

4

5

6

_remotingObject.ServerToClient +=方法();

//這樣由於這個方法是客戶端的,服務端沒法調用,因此須要一箇中間轉換的

 SwapObject swap = new SwapObject();//先建立一個Swap對象

 _remotingObject.ServerToClient += swap.ToClient;

 //而後服務端事件發信息給swap,而後swap再經過事件發消息給客戶端,swap是客戶端建立的因此能夠發送,而swap是服務端的類,因此服務端也能識別,swap起到了中間過渡的做用

 swap.SwapServerToClient +=方法();

以上就是C#NetRemoting實現雙向通訊的示例代碼分享的詳細內容,更多請關注php中文網其它相關文章!

相關文章
相關標籤/搜索