通过模拟请求或序列化来发送不同的消息
思路:要发送不同的消息(文字,图片,文件,震动等等),我想可不可以对不同类型的消息作一个标识呢,在哪里标志呢?
因为在传输的时候服务器与客户端只以字节流的方式来完成数据的传输,那么我们就可以在不同的数据数里加一个标志。
图为httpwatch捕获的http报文
这种思想来源自HTTP请求与响应报文,模拟HTTP请求与响应,我自己规定字节流里第一个字节{0:文字,1:文件,2:震动}。
但是这个时候有一个坏处,如果标识后期又要加一个功能,比如要求用户可能控制不同的文字大小,不同的文字与颜色。那样标志就越来越多了,传输的报文也越来越难封装和解析了(中间多了定义标识,合并与拆封字符串的过程)。
这个时候我们就会用面象对象的思想来想这个问题了。能不能把一个对象用来传输呢?因为此时的网络是以字节数组的形式在不同电脑中传输的,那怎么样把一个对象转换成一个字节数组在不同电脑中来进行发送呢?
这个时候我们就应该想到了序列化。
图为我的文件结构
由于我的服务器与客户端在不同的程序集,所以我就定义一个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
网友评论