上一节给大家演示了建立连接的关键代码,连接建立好后,就可以进行数据传输了。数据传输包含从服务器端到客户端和从客户端到服务器端,两者差别不大。
数据的传输,TcpClient的GetNetworkStream是关键,通过它我们可以得到NetworkStream网络流,客户端和服务器主要的工作就是对其读出和写入。关于如何构造稳定且性能好的网络应用,如何进行复杂的封包和解包,这里我们不考虑,我们使用StreamReader和StreamWriter来封装NetStream,读取和写入的代码如下
1
TcpClient client;
2
StreamReader sr;
3
StreamWriter sw;
4
this
.userName
=
""
;
5
NetworkStream netStream
=
client.GetStream();
6
sr
=
new
StreamReader(netStream, System.Text.Encoding.UTF8);
7
sw
=
new
StreamWriter(netStream, System.Text.Encoding.UTF8);
8
//
读取数据
9
string
output
=
sr.ReadLine();
10
//
写入数据
11
string
input
=
""
;
12
sw.WriteLine();
13
sw.Flush();
客户端和服务器的通信一般是由客户端向服务器端发请求,服务器端接受请求并处理,再将结果重新发回客户端
发送请求和应答的协议格式可以自己定义,游戏中使用的协议格式为 命令,参数1,参数2......
先看一下客户端请求登录
1
service.SendToServer(
"
Login,
"
+
textBoxName.Text.Trim());
2
class
Service
3
{
4
ListBox listbox;
5
StreamWriter sw;
6
public
Service(ListBox listbox, StreamWriter sw)
7
{
8
this
.listbox
=
listbox;
9
this
.sw
=
sw;
10
}
11
public
void
SendToServer(
string
str)
12
{
13
sw.WriteLine(str);
14
sw.Flush();
15
}
16
}
服务器接受请求并处理,一般来说是根据不同的请求创建不同的线程进行处理,见本节后半部分
1
private
void
ReceiveData(
object
obj)
2
{
3
//
User类封装了TcpClient,StreamReader和StreamWriter
4
User user
=
(User)obj;
5
TcpClient client
=
user.client;
6
string
receiveString
=
null
;
7
receiveString
=
sr.ReadLine();
8
service.SetListBox(
string
.Format(
"
来自{0}:{1}
"
, user.userName, receiveString));
9
string
[] splitString
=
receiveString.Split(
'
,
'
);
10
switch
(splitString[
0
])
11
{
12
case
"
Login
"
:
13
user.userName
=
string
.Format(
"
[{0}--{1}]
"
, splitString[
1
], client.Client.RemoteEndPoint);
14
service.SendToOne(user,
"
Welcome
"
);
15
break
;
16
default
:
17
break
;
18
}
19
}
20
1
class
Service
2
{
3
private
ListBox listbox;
4
private
delegate
void
SetListBoxCallback(
string
str);
5
private
SetListBoxCallback setListBoxCallback;
6
public
Service(ListBox listbox)
7
{
8
this
.listbox
=
listbox;
9
setListBoxCallback
=
new
SetListBoxCallback(SetListBox);
10
}
11
public
void
SetListBox(
string
str)
12
{
13
if
(listbox.InvokeRequired)
14
{
15
listbox.Invoke(setListBoxCallback, str);
16
}
17
else
18
{
19
listbox.Items.Add(str);
20
listbox.SelectedIndex
=
listbox.Items.Count
-
1
;
21
listbox.ClearSelected();
22
}
23
}
24
public
void
SendToOne(User user,
string
str)
25
{
26
try
27
{
28
user.sw.WriteLine(str);
29
user.sw.Flush();
30
SetListBox(
string
.Format(
"
向{0}发送{1}
"
, user.userName, str));
31
}
32
catch
33
{
34
SetListBox(
string
.Format(
"
向{0}发送信息失败
"
, user.userName));
35
}
36
}
37
}
服务器负责监听并根据不同的用户启动不同的线程
1
IPAddress localAddress;
2
int
port
=
51888
;
3
TcpListener myListener;
4
Service service
=
new
Service(listbox);
5
IPAddress[] addrIP
=
Dns.GetHostAddresses(Dns.GetHostName());
6
localAddress
=
addrIP[
0
];
7
myListener
=
new
TcpListener(localAddress, port);
8
myListener.Start();
9
service.SetListBox(
string
.Format(
"
开始在{0}:{1}监听客户连接
"
, localAddress, port));
10
ThreadStart ts
=
new
ThreadStart(ListenClientConnect);
11
Thread myThread
=
new
Thread(ts);
12
myThread.Start();
ListenClientConnect方法
1
while
(
true
)
2
{
3
TcpClient newClient
=
null
;
4
try
5
{
6
newClient
=
myListener.AcceptTcpClient();
7
}
8
catch
9
{
10
break
;
11
}
12
User user
=
new
User(newClient);
13
userList.Add(user);
14
service.SetListBox(
string
.Format(
"
{0}进入
"
, newClient.Client.RemoteEndPoint));
15
service.SetListBox(
string
.Format(
"
当前连接用户数:{0}
"
,userList.Count));
16
//
basilwang 2008-09-06 to receive data from client
17
ParameterizedThreadStart pts
=
new
ParameterizedThreadStart(ReceiveData);
18
Thread threadReceive
=
new
Thread(pts);
19
threadReceive.Start(user);
20
}
此处的启用了ParameterizedThreadStart 线程处理带参数的ReceiveData方法,主要是防止同客户端传输数据的过程出现的异常不能正确处理导致线程崩溃,每一个客户端分配一个线程可以保证客户端各自的独立性;同时ParameterizedThreadStart允许传递参数。
至此我们完成了一个简单的服务器客户端应答,希望能对初学者有所帮助。
文件下载