saptechnique

Better late than never. - 郭富

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

五一在家看了一会儿<重构手册>, 想拿以前写的代码尝试着改进改进, 想起去年暑假写的局域网聊天小工具, 现在看自己那时写的代码已经不堪入目, 最不可思议的当属用了"多线程"处理网络请求, 现在觉得应该使用异步方法.

主要设计

简要说明

上图左边部分表示的是客户端的过程, 右边部分表示的是服务端的过程. 客户端相比服务端在建立连接之前步骤稍微少一些, 成功建立连接后客户端和服务端都有一个CommunicateSocket负责与对方通信, 如发送消息, 接收消息, 发送文件, 接收文件等.

服务端, 声明ServerSocket, 绑定(Bind)一个IP并指定这个IP的通信端口, 比如是127.0.0.1:9050, ServerSocket可以监听来自多个IP发送的连接请求, 监听(Listen)方法的参数可以设置允许的最多连接请求个数. 然后调用异步接受请求的方法(BeginAccept), 如果接受到某个客户端发来连接请求, 这时定义一个新的CommunicateSocket专门负责与这个客户端通信. 然后可以通过CommunicateSocket.BeginSend()方法给客户端发送数据, CommunicateSocket.BeginReceive()可以接收客户端发来的数据.

客户端, 有一个CommunicateSocket, 并绑定一个IP以及一个未被占用的端口, 定义IPEndPoint serverIP表示服务端Socket的IP和端口, 这样才可以进行端口对端口之间的通信, 接下来就可以尝试CommunicateSocket.BeginConnect(serverIP), 连接成功之后就可以发送和接收数据了, CommunicateSocket.BeginSend(), CommunicateSocket.BeginReceive().

有些异步方法有两种实现方式, 如BeginAccept()和AcceptAsync(), 这两个方法有什么区别呢? 以 Begin 和 End 开头的方法是以 APM(Asynchronous Programming Model)设计方法实现的异步操作, 以 Async 结尾的方法是利用称为 EAP (Event-based Asynchronous Pattern) 的设计方法实现的异步操作.

代码部分

1. SocketFunc类

SocketFunc是一个抽象类, 服务端和客户端只有建立连接的方法不同, 其它都相同, 所以把相同的部分放到这个类中.

 
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
public abstract class SocketFunc
{
//不管是服务端还是客户端, 建立连接后用这个Socket进行通信
public Socket communicateSocket = null;
 
//服务端和客户端建立连接的方式稍有不同, 子类会重载
public abstract void Access(string IP, System.Action AccessAciton);
 
//发送消息的函数
public void Send(string message)
{
if (communicateSocket.Connected == false)
{
throw new Exception("还没有建立连接, 不能发送消息");
}
Byte[] msg = Encoding.UTF8.GetBytes(message);
communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None,
ar => {
 
}, null);
}
 
//接受消息的函数
public void Receive(System.Action<string> ReceiveAction)
{
//如果消息超过1024个字节, 收到的消息会分为(总字节长度/1024 +1)条显示
Byte[] msg = new byte[1024];
//异步的接受消息
communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None,
ar => {
//对方断开连接时, 这里抛出Socket Exception
//An existing connection was forcibly closed by the remote host
communicateSocket.EndReceive(ar);
ReceiveAction(Encoding.UTF8.GetString(msg).Trim('\0',' '));
Receive(ReceiveAction);
}, null);
}
}

2. ServerSocket:SocketFunc类

继承自SocketFunc类, 类中重载了Access方法.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ServerSocket:SocketFunc
{
//服务端重载Access函数
public override void Access(string IP, System.Action AccessAciton)
{
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//本机预使用的IP和端口
IPEndPoint serverIP = new IPEndPoint(IPAddress.Any, 9050);
//绑定服务端设置的IP
serverSocket.Bind(serverIP);
//设置监听个数
serverSocket.Listen(1);
//异步接收连接请求
serverSocket.BeginAccept(ar =>
{
base.communicateSocket = serverSocket.EndAccept(ar);
AccessAciton();
}, null);
}
}

3. ClientSocket:SocketFunc类

继承自SocketFunc类, 类中重载了Access方法.

 
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
public class ClientSocket:SocketFunc
{
//客户端重载Access函数
public override void Access(string IP, System.Action AccessAciton)
{
base.communicateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
base.communicateSocket.Bind(new IPEndPoint(IPAddress.Any, 9051));
 
//服务器的IP和端口
IPEndPoint serverIP;
try
{
serverIP = new IPEndPoint(IPAddress.Parse(IP), 9050);
}
catch
{
throw new Exception(String.Format("{0}不是一个有效的IP地址!", IP));
}
 
//客户端只用来向指定的服务器发送信息,不需要绑定本机的IP和端口,不需要监听
try
{
base.communicateSocket.BeginConnect(serverIP, ar =>
{
AccessAciton();
}, null);
}
catch
{
throw new Exception(string.Format("尝试连接{0}不成功!", IP));
}
}
}

程序截图

源码下载

http://www.cnblogs.com/technology/archive/2011/05/03/2035410.html

posted on 2011-12-21 15:01  guofu  阅读(322)  评论(0编辑  收藏  举报