1. 程式人生 > >C# 實現Remoting雙向通訊

C# 實現Remoting雙向通訊

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

閒來無事想玩玩雙向通訊,實現類似QQ的互發訊息的功能。於是乎開始學習.Net Remoting.

.Net Remoting 是由客戶端通過Remoting,訪問通道以獲得服務端物件,再通過代理解析為客戶端物件來實現通訊的。也就是說物件是由服務端建立的。

先上程式碼

首先是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

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