美文网首页通往成功之路
用socket来写一个聊天软件吧(三)实现发送不同的消息

用socket来写一个聊天软件吧(三)实现发送不同的消息

作者: 此十八 | 来源:发表于2018-01-30 12:46 被阅读6次

通过模拟请求或序列化来发送不同的消息

思路:要发送不同的消息(文字,图片,文件,震动等等),我想可不可以对不同类型的消息作一个标识呢,在哪里标志呢?
因为在传输的时候服务器与客户端只以字节流的方式来完成数据的传输,那么我们就可以在不同的数据数里加一个标志。

image
              图为httpwatch捕获的http报文

这种思想来源自HTTP请求与响应报文,模拟HTTP请求与响应,我自己规定字节流里第一个字节{0:文字,1:文件,2:震动}。
但是这个时候有一个坏处,如果标识后期又要加一个功能,比如要求用户可能控制不同的文字大小,不同的文字与颜色。那样标志就越来越多了,传输的报文也越来越难封装和解析了(中间多了定义标识,合并与拆封字符串的过程)。
这个时候我们就会用面象对象的思想来想这个问题了。能不能把一个对象用来传输呢?因为此时的网络是以字节数组的形式在不同电脑中传输的,那怎么样把一个对象转换成一个字节数组在不同电脑中来进行发送呢?
这个时候我们就应该想到了序列化。

image
       图为我的文件结构

由于我的服务器与客户端在不同的程序集,所以我就定义一个common的类库在不同程序集中来进行传输。

MyCommon类:

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

|

[Serializable]

public class MyCommon

{

//定义标志

private int flag;

public int Flag

{

get { ``return flag; }

set { flag = value; }

}

//传输的字节数组

private byte``[] buffer;

public byte``[] Buffer

{

get { ``return buffer; }

set { buffer = value; }

}

}

|

服务器端:

|

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

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

|

using System.Runtime.Serialization.Formatters.Binary;

namespace MyChatServer

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

//不检测跨线程操作的合法性

Control.CheckForIllegalCrossThreadCalls = ``false``;

}

//当多用户连接到同一个服务器的时候,记录通信点与对应通信的socket,以字典的键值对来保存,方便调用,这样服务器就可以选择的来给不同的用户发送消息了

Dictionary<``string``, Socket> dicSocket = ``new Dictionary<``string``, Socket>();

Dictionary<``string``, Thread> dicThread = ``new Dictionary<``string``, Thread>();

private void btnStart_Click_1(``object sender, EventArgs e)

{

//ip地址

IPAddress ip = IPAddress.Parse(txtServer.Text);

//当前计算机上任何可用的ip地址

//IPAddress ip = IPAddress.Any;

IPEndPoint point = ``new IPEndPoint(ip, ``int``.Parse(txtPort.Text));

//创建负责监听的socket

Socket socket = ``new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

try

{

//绑定ip和端口号

socket.Bind(point);

//监听

socket.Listen(10);

ShowMsg(``"开始监听"``);

}

catch (Exception ex)

{

ShowMsg(ex.Message);

return``;

}

Thread th = ``new Thread(Listen);

th.IsBackground = ``true``;

th.Start(socket);

}

int count = 0;

void Listen(``object o)

{

Socket socket = o ``as Socket;

while (count<100)

{

try

{

count++;

//创建一个负责通信用的socket 阻塞窗体的运行

Socket connSocket = socket.Accept();

string s = connSocket.RemoteEndPoint.ToString();

ShowMsg(s + ``":连接成功"``);

//记录通信用的socket

dicSocket.Add(s, connSocket);

cboUsers.Items.Add(s);

//接收消息

Thread th = ``new Thread(RecMsg);

th.IsBackground = ``true``;

th.Start(connSocket);

dicThread.Add(s, th);

}

catch (Exception ex)

{

ShowMsg(ex.Message);

break``;

}

}

}

void RecMsg(``object o)

{

Socket connSocket = o ``as Socket;

while (``true``)

{

try

{

//接收客户端发送过来的消息

byte``[] buffer = ``new byte``[1024 * 1024];

//num 接收到的实际有效的字节个数

int num = connSocket.Receive(buffer);

string s = Encoding.UTF8.GetString(buffer, 0, num);

ShowMsg(connSocket.RemoteEndPoint.ToString() + ``":" + s);

}

catch (Exception ex)

{

ShowMsg(ex.Message);

break``;

}

}

}

void ShowMsg(``string msg)

{

txtLog.AppendText(msg + ``"\r\n"``);

}

//服务器发送消息

private void btnSend_Click(``object sender, EventArgs e)

{

//获取文本框内容 转化成字节数组

byte``[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);

try

{

//List<byte> list = new List<byte>();

////标志 0 文字

//list.Add(0);

//list.AddRange(buffer);

Common.MyCommon my = ``new Common.MyCommon();

my.Flag = 0;

my.Buffer = buffer;

//客户端的通信用的socket

//获取当前选中的客户端的ip地址

string s = cboUsers.Text;

dicSocket[s].Send(Serialize(my));

}

catch (Exception ex)

{

ShowMsg(ex.Message);

}

}

private void Form1_Load(``object sender, EventArgs e)

{

}

//选择文件

private void btnSelect_Click(``object sender, EventArgs e)

{

OpenFileDialog ofd = ``new OpenFileDialog();

if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)

{

txtPath.Text = ofd.FileName;

}

}

//发送文件

private void btnSendFile_Click(``object sender, EventArgs e)

{

string key = cboUsers.Text;

using (FileStream fs = ``new FileStream(txtPath.Text,FileMode.Open))

{

byte``[] buffer = ``new byte``[fs.Length];

fs.Read(buffer,0,buffer.Length);

//List<byte> list = new List<byte>();

////标志 1 文件

//list.Add(1);

//list.AddRange(buffer);

Common.MyCommon my = ``new Common.MyCommon();

my.Flag = 1;

my.Buffer = buffer;

dicSocket[key].Send(Serialize(my));

}

}

//震动

private void btnZD_Click(``object sender, EventArgs e)

{

string key = cboUsers.Text;

//byte[] buffer = new byte[1];

////标志 震动

//buffer[0] = 2;

Common.MyCommon my = ``new Common.MyCommon();

my.Flag = 2;

dicSocket[key].Send(Serialize(my));

}

//序列化,我想把这个对象转换成一个数组,那就传进去对象给我返回数组吧

byte``[] Serialize(Common.MyCommon my)

{

//先把这个对象转换成一个流,再把这个流转换成一个字节数组,这个流只是一个中转流,用内存流就可以了

using (MemoryStream stream = ``new MemoryStream())

{

BinaryFormatter bf = ``new BinaryFormatter();

bf.Serialize(stream,my);

//把序列化好的流转换成数组

byte``[] buffer = stream.ToArray();

//返回出字节数组

return buffer;

}

}

}

}

|

客户端:

|

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

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

|

using System.Runtime.Serialization.Formatters.Binary;

namespace MyChatClient

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

Control.CheckForIllegalCrossThreadCalls = ``false``;

}

Socket socket;

private void btnStart_Click(``object sender, EventArgs e)

{

//服务器的ip地址与端口

IPAddress ip = IPAddress.Parse(txtServer.Text);

IPEndPoint point = ``new IPEndPoint(ip,``int``.Parse(txtPort.Text));

socket = ``new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

try

{

//连接服务器

socket.Connect(point);

ShowMsg(``"连接成功"``);

//接收服务器发送的消息

Thread th = ``new Thread(RecMsg);

th.IsBackground = ``true``;

th.Start();

}

catch (Exception ex)

{

ShowMsg(ex.Message);

}

}

//接收服务器发送过来的消息

void RecMsg()

{

while (``true``)

{

try

{

byte``[] buffer = ``new byte``[1024 * 1024];

int num = socket.Receive(buffer);

Common.MyCommon my = Deserialize(buffer, num);

//当是文字

if``(my.Flag == 0)

{

string s = Encoding.UTF8.GetString(my.Buffer, 0,my.Buffer.Length);

ShowMsg(s);

}

//接收文件

else if (my.Flag == 1)

{

//提示是否保存文件

DialogResult dr = MessageBox.Show(``"是否保存文件?"``,``"提示"``,MessageBoxButtons.YesNo);

if (dr == System.Windows.Forms.DialogResult.Yes)

{

SaveFileDialog sfd = ``new SaveFileDialog();

//保存文件 win7下使用的时候注意,必须加this

if (sfd.ShowDialog(``this``) == System.Windows.Forms.DialogResult.OK)

{

using (FileStream fs = ``new FileStream(sfd.FileName,FileMode.Create))

{

fs.Write(my.Buffer,0,my.Buffer.Length);

}

}

}

}

else if (my.Flag == 2)

{

//窗口置于顶层

this``.TopMost = ``true``;

//震动

ZD();

}

}

catch (Exception ex)

{

ShowMsg(ex.Message);

break``;

}

}

}

void ShowMsg(``string msg)

{

txtLog.AppendText(msg + ``"\r\n"``);

}

//发送消息

private void btnSend_Click(``object sender, EventArgs e)

{

if (socket != ``null``)

{

try

{

byte``[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);

socket.Send(buffer);

}

catch (Exception ex)

{

ShowMsg(ex.Message);

}

}

}

private void Form1_Load(``object sender, EventArgs e)

{

}

//震动

string dir = ``"top"``;

int count = 10;

void ZD()

{

for (``int i = 0; i < count; i++)

{

if (dir == ``"top"``)

{

dir = ``"bottom"``;

this``.Location = ``new Point(``this``.Location.X - 5, ``this``.Location.Y - 5);

}

else

{

dir = ``"top"``;

this``.Location = ``new Point(``this``.Location.X + 5, ``this``.Location.Y + 5);

}

System.Threading.Thread.Sleep(50);

}

}

//反序列化,把一个字节数组转换成一个对象,

Common.MyCommon Deserialize(``byte``[] buffer,``int num)

{

using (MemoryStream ms = ``new MemoryStream(buffer, 0, num))

{

BinaryFormatter bf = ``new BinaryFormatter();

Common.MyCommon my = bf.Deserialize(ms) ``as Common.MyCommon;

return my;

}

}

}

}

|

成功了,来看看结果吧

image image

这只是一个实验,客户端对客户端只是服务器的转发,如果想做成像QQ那样的,可以与ADO.NET结合起来,其实核心的就是这些了,如果想尽善尽美的话,还要考虑的问题还有很多。还是那句话,说起来简单,做起来难。

源码已上传,下载地址:http://files.cnblogs.com/inline/MyChat.zip

相关文章

  • 用socket来写一个聊天软件吧(三)实现发送不同的消息

    通过模拟请求或序列化来发送不同的消息 思路:要发送不同的消息(文字,图片,文件,震动等等),我想可不可以对不同类型...

  • 用Socket.io打造你的聊天和消息推送

    你还在用第三方的框架实现聊天和消息推送?快快试试吧 Socket.IO(官网)介绍 是一个跨平台的聊天框架,可以实...

  • Java Socket通信

    目标 Demo是通过Java ServerSocket 和 Socket 通信实现客户端发送消息和发送文件到服务器...

  • runtime的消息机制

    任何方法调用本质:发送一个消息,用runtime发送消息,OC底层实现通过runtime实现; 我们平时书写的代码...

  • runtime介绍

    前言:任何方法调用的本质:发送一个消息,用runtime来发送消息,OC底层实现通过runtime来实现。 run...

  • Flutter Socket+Protobuf

    近来在家撸码想撸一下公司产品现有业务用Flutter去实现下。主要有一个核心功能就是Socket进行发送和接收消息...

  • Runtime的底层实现

    任何方法调用的本质: 其实是发送了一个消息, 用runtime发送消息, OC底层实现通过runtime实现最终生...

  • Netty笔记之三:Netty实现Socket编程

    netty实现Tcp Socket编程。 demo实现功能客户端向服务端发送消息,服务器接收到消息后向客户端响应。...

  • 01-网络编程

    socket 在python中,使用socket模块的函数socket就可以完成. 发送消息 接收消息 套接字在同...

  • udp知识求教

    想利用socket 编写程序 ,虚拟机编写一个程序接收数据,windows编写一个程序发送数据。类似与聊天软件,不...

网友评论

    本文标题:用socket来写一个聊天软件吧(三)实现发送不同的消息

    本文链接:https://www.haomeiwen.com/subject/vxxczxtx.html