- 浏览: 203117 次
- 性别:
- 来自: 天津
最新评论
.Net Socket编程基础 -1
.Net Socket编程基础 -1
Socket Basic Concepts<wbr></wbr>
首先介绍Socket的一些基本概念
Socket是操作系统提供的一系列网络编程接口。
网络模型分若干层,也有一些协议,比如TCP协议,UDP协议等,这些都是抽象的定义,在硬件以及操作系统级别上有一些对应的实现,Socket可以看做操作系统为开发人员提供的一系列网络编程接口,它封装了一些协议的细节,比如怎么组织数据包,怎么发送数据之类的。
Socket编程的几个基本概念<wbr></wbr>
Endpoint
Endpoin指定要连接到哪里,Endpoint包括两部分内容,IP和Port,IP地址和端口组合起来才能唯一指定远程的通信端。
AddressFamily
怎么寻址,有了IP地址之后就是如何寻址的问题,常用的寻址方案是IP V4和IP
V6两种类型,windows操作系统从VISTA和Windows 20008起默认支持IPV6。
Protocol
使用什么协议进行通信,比如TCP协议或者UDP协议,下面介绍Socket类型的时候还会涉及TCP和UDP等协议的介绍。
Socket类型
Socket有三种常用类型:Stream, Dgram, Raw
Stream流类型,支持可靠、双向、基于连接的字节流,使用TCP协议。
Dgram数据报类型,支持数据报,即最大长度固定的无连接、不可靠消息。消息可能会丢失或重复并可能在到达时不按顺序排列,使用UDP协议。
Raw类型支持对基础传输协议的访问,需要自己生成数据包。网上有一些RAW的例子,比如D.O.S攻击,ARP攻击,网络监控之类的。
本文只讨论Stream类型的Socket编程,RAW和Dgram不在讨论之列,也就是只讨论基于TCP协议的编程。
一些常见的概念问题
Socket和TCP/IP有什么关系?
Socket和TCP/IP不是一个层面的概念,Socket是操作系统提供的操作TCP数据的编程接口。
Sockets V4、Sockets V5有什么区别?
经常看到一些软件可以设置Sockets4/Sockets5代理,简单说他们是客户端与外网服务器之间通讯的协议,Sockets是位于应用层与传输层之间的中间层。 Sockets V4支持TCP, Sockets V5支持TCP/UDP,支持安全认证,支持IPV6。
Socket能够同时接受和发送数据吗?
TCP协议是双工的
Socket如何保证数据按顺序到达?
TCP协议来保证
Socket的基本通信模型模型
客户端:
Socket()
Connect
Send
Close
服务器端:
Socket()
Bind
Listen
Accept
Receive
Send
Close
客户端和服务器端模型是不一样的,两边是非对称的。
<wbr></wbr>.Net Socket API
下面是.Net
Socket编程最基本的几个类,位于命名空间System.Net.Sockets
Socket Socket接口类
TcpClient TCP客户端类
TcpListener TCP侦听类
NetworkStream 用于网络访问的基础数据流
其他经常用到的辅助类,位于命名空间System.Net
Dns 域名解析
EndPoint<wbr> 标识网络地址<br>
IPAddress<wbr> IP地址。<br>
NetworkCredential 基于密码的身份验证方案,不支持基于公钥的身份验证方法(比如ssl)<br><br></wbr></wbr>一个Socket的简单例子
输入网址,获得HTML页面的一段演示代码,只是演示Socket对象的几个主要功能,不具有实用价值。
基本步骤为:建立Socket对象,连接服务器,发送数据,然后接受数据,对应上一章介绍的Socket通信模型。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
< SPAN
style = "FONT-SIZE:
10pt" >private string
DownloadPage(string path)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Uri
uri = new Uri(path);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Encoding
encoding = Encoding.UTF8;// .GetEncoding("gb2312");
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
requestHeader = BuildRequestHeader(uri);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
requestBytes = encoding.GetBytes(requestHeader);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
receivedBytes = new byte[1024 * 100];
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> socket.Connect(uri.Host,uri.Port);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> socket.Send(requestBytes);
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> int
receivedBytesLength = socket.Receive(receivedBytes);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> socket.Shutdown(SocketShutdown.Both);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> socket.Close();
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
html = string.Empty;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(receivedBytesLength > 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> html
= encoding.GetString(receivedBytes, 0,
receivedBytesLength);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> return
html;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }</ SPAN >
|
构造HTTP
Header的代码如下,注意不要忘了Http头模板的最后一行
1
2
3
4
5
6
7
8
9
10
11
|
< SPAN
style = "FONT-SIZE:
10pt" >private string
BuildRequestHeader(Uri uri)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
httpHeaderTemplate = @"GET {url} HTTP/1.1
Connection: Close Host: {host} <wbr></wbr> <wbr></wbr>"; <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> return
httpHeaderTemplate.Replace("{url}", uri.AbsolutePath)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> .Replace("{host}",
uri.Host);
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }</ SPAN >
|
30秒思考题:这段简单代码有什么问题?
我们定义的用来接收数据的数组大小是固定的,如果要接受的数据超过数组大小怎么办?
可以定义一个缓冲区,每次接受固定大小的数据,直到接收完成为止。示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< SPAN
style = "FONT-SIZE:
10pt" >MemoryStream ms = new
MemoryStream();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> while
(true)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Console.WriteLine("Available
:{0}", socket.Available);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> int
receivedBytesLength = socket.Receive(receivedBytes, 0,
receivedBytes.Length, SocketFlags.None);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(receivedBytesLength > 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> ms.Write(receivedBytes,
0, receivedBytesLength);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> else
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> break;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
html = string.Empty;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(ms.Length > 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> html
= encoding.GetString(ms.ToArray(), 0, (int)ms.Length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }</ SPAN >
|
Socket的缓冲区
Socket接收数据时,操作系统先把数据接收到缓冲区,然后通知程序,socket.Available
是从已经从网络接收的、可供读取的数据的字节数,这个值是指缓冲区中已接收数据的字节数,不是实际的数据大小。而且如果网络有延迟,Send之后马上读取Available属性不一定能读到正确的值,所以不能利用socket.Available来判断总共要接受的字节数。
在上面的方法中,如果没有可读取的数据,则 Receive
方法将一直处于阻止状态,直到有数据可用,如果Server端也没有正确关闭连接,程序很容易死在这里,可以通过Socket.ReceiveTimeout来设置Socket对象接受数据的超时时间。
30秒思考题:为什么这样下载的页面有时候和浏览器下载的页面不一样?
>>gzip,chunked编码,重定向等
NetworkStream的例子
前面讲过基于TCP协议的Socket是Steam类型的,在操作系统中,为了简化编程,把设备、文件等都看作流对象,统一编程接口。NetworkStream类提供了在阻止模式下通过Socket套接字发送和接收数据的方法,.Net还提供了TcpClient和TcpListener类,用于简化同步阻止模式下通过TCP协议连接、发送和接收流数据。下面的例子是这几个对象的简单介绍,省略了一些细节,也不具有实用价值。
这个例子模拟计算机远程控制,先新起一个线程模拟服务进程,在这个线程中创建一个TcpListener对象,等待客户端连接。用户在客户端界面点了“连接”按钮后,UI线程创建TcpClient对象,等待用户输入dos命令,用户输入dos命令,按执行按钮,这时TcpClient对象把用户输入的命令发送给TcpListener对象,服务进程执行完命令后,将执行结果反馈给TcpClient对象。
部分代码。
<wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>
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
|
< SPAN
style = "FONT-SIZE:
10pt" >private void
StartServer()
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> TcpListener
server = new TcpListener(IPAddress.Any, 10000);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> server.Start();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Debug.WriteLine("Server:
start");
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> TcpClient
client = server.AcceptTcpClient();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Debug.WriteLine("Server
: connection accept");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> NetworkStream
stream = client.GetStream();
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Process
process = new Process();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.FileName
= "cmd.exe";
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.UseShellExecute
= false;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.RedirectStandardInput
= true;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.RedirectStandardOutput
= true;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.RedirectStandardError
= true;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.StartInfo.CreateNoWindow
= false;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.Start();
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.OutputDataReceived
+= (Object sender, DataReceivedEventArgs e)
=>
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
bytes = Encoding.GetEncoding("gb2312").GetBytes(e.Data +
"\r\n");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> stream.Write(bytes,
0, bytes.Length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> };
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.BeginOutputReadLine();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> while
(true)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Byte[]
buffer = new Byte[1024 * 10];
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> int
length = stream.Read(buffer, 0, buffer.Length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(length == 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Debug.WriteLine("Server:
read 0 byte");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> break;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
command = Encoding.GetEncoding("gb2312").GetString(buffer, 0,
length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Debug.WriteLine("Server:
receive {0} ", command);
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> StreamWriter
Writer = process.StandardInput;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Writer.WriteLine(command);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Writer.Flush();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> server.Stop();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.WaitForExit(1000);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> process.Close();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Debug.WriteLine("Server:
close");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> TcpClient
client;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> private
void ConnectButton_Click(object sender, EventArgs e)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> client
= new TcpClient();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> client.Connect("localhost",
10000);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Console.WriteLine("Client:
connect");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> StartButton.Enabled
= true;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> private
void StartButton_Click(object sender, EventArgs e)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(CommandTextBox.Text.Trim().Length == 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> return;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
request = CommandTextBox.Text.Trim();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
bytes =
Encoding.GetEncoding("gb2312").GetBytes(request);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> NetworkStream
stream = client.GetStream();
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> stream.Write(bytes,
0, bytes.Length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Console.WriteLine("Client:
request {0}", request);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> MessageTextBox.AppendText("\r\nresponse
from server:\r\n");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
buffer = new byte[1024];
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> do{
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> int
receivedBytesLength = stream.Read(buffer, 0,
buffer.Length);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if(receivedBytesLength
> 0)
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> string
text = Encoding.GetEncoding("gb2312").GetString(buffer, 0,
receivedBytesLength);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> MessageTextBox.AppendText(text);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> MessageTextBox.AppendText("\r\n");
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> else
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> break;
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }while(stream.DataAvailable);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }</ SPAN >
|
下一章介绍Socket异步编程模式
<wbr></wbr>
.Net Socket编程基础 异步编程
同步socket并发性很差,特别是对于服务器端来说,要处理很多客户端连接,同步Socket力不从心,要提高系统的并发处理能力,就要借助.Net异步编程模式。
.Net Socket的异步编程模式和.Net 通用的APM编程模式是一致的,调用BeginXXX方法开始异步操作,系统系统处理完成后调用回调函数,在回调函数中调用EndXXX结束操作。
常用的Socket对象的异步API有
接受客户端连接 BeginAccept, EndAccept
接收数据 BeginReceive, EndReceive
发送数据 BeginSend, EndSend
下面是简单的Socket服务端实现的部分,演示Socket异步API的使用。
首先New一个Socket对象,绑定到特定端口,开始监听客户端发来的连接请求。
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
|
< SPAN
style = "FONT-SIZE:
10pt" >Socket socket;
socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(address,
port)); socket.Listen(backlog);<wbr></wbr> // backlog
参数指定队列中最多可容纳的等待接受的传入连接数,不同操作系统的backlog参数的上限是不一样的 <wbr></wbr> <wbr></wbr>//然后调用BeginAccept开始等待客户端连接。 while (true) { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> acceptDone.Reset();
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> socket.BeginAccept(new
AsyncCallback(AcceptCallback), socket);
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> acceptDone.WaitOne();
} <wbr></wbr> <wbr></wbr>//acceptDone是信号量 private ManualResetEvent acceptDone = new
ManualResetEvent(false); <wbr></wbr> <wbr></wbr>//AcceptCallback是回调函数,下面是这个函数的部分代码 <wbr></wbr> <wbr></wbr>private void AcceptCallback(IAsyncResult
ar) { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> acceptDone.Set();
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> //
EndAccept 将会返回Windows自动分配的 Socket 对象实例
<wbr><wbr><wbr></wbr></wbr></wbr> Socket
handler = listener.EndAccept(ar);
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> log.InfoFormat("{0}
connected", handler.RemoteEndPoint.ToString());
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> //可以限制允许的最大连接数连接,保存当前对话的session信息等
<wbr><wbr><wbr></wbr></wbr></wbr> SocketState
state = new SocketState(handler);
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> SocketError
errorCode;
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> handler.BeginReceive(state.Buffer,
0, state.BufferSize, SocketFlags.None, out errorCode, new
AsyncCallback(ReceiveCallback), state);
<wbr></wbr> <wbr></wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> if
((errorCode != SocketError.Success)
&& (errorCode !=
SocketError.IOPending))
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> ProcessError(handler,
errorCode, "BeginReceive");
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> }
} </ SPAN >
|
BeginXXX的重载列表中一般有一个函数重载允许你传一个SocketError 类型的out参数,可以根据这个参数判断是否出错。
SocketState对象是为了方便写代码构造的用来保存当前Socket状态的类,比如上次接受数据是什么时间,一共接受了多少数据等。
1
2
3
4
5
6
7
8
|
< SPAN
style = "FONT-SIZE:
10pt" >public class SocketState :
IDisposable
{ private Socket socket = null; private const int bufferSize =
1024; private byte[] buffer = new
byte[bufferSize]; <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> 。。。
} </ SPAN >
|
操作系统接受到数据后会调用回调函数ReceiveCallback
private void ReceiveCallback(IAsyncResult ar)
{
SocketState state = (SocketState)ar.AsyncState;
Socket handler = state.Socket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
//处理接受的数据
。。。
//如果还有数据,继续接受
if (handler.Available > 0 || handler.Connected )
{
SocketError errorCode;
handler.BeginReceive(state.Buffer, 0, state.BufferSize, SocketFlags.None, out errorCode, new AsyncCallback(ReceiveCallback), state);
}
else
{
handler.Close();
//其他处理代码,比如重置连接计数器等
}
}
}
上面代码只是简单介绍API的用法,很多地方没有考虑,完整的Socket服务代码比这个要复杂得多。
BeginXXX、EndXXX这种异步模式比同步方式要高效得多,但是每次需要New一个IAsyncResult对象和state对象,并发高的情况下对GC也是不小的压力,频繁的申请、释放小块内存,容易产生很多小的内存碎片,内存碎片多的话内存利用率低,而且可能出现即使内存没有全部用完,但是.Net Runtime还是会报告没有可分配的内存的情况。另一方便,编程模型比较复杂。
为了进一步简化Socket异步编程,.Net3.5引入了新的编程模型。
<wbr></wbr>.Net 3.x 的Socket异步编程 在.Net 3.5中引入了一组增强功能,提供可供专用的高性能套接字应用程序使用的可选异步模式,并且简化了Socket异步编程复杂度。SocketAsyncEventArgs类是这组增强功能中最常用的一个类,专为需要高性能的网络服务器应用程序而设计。它主要的成员如下: OnCompleted事件:在异步操作完成后,系统会触发OnCompleted事件,在事件处理代码中可以进行后续异步套接字操作的处理。 SetBuffer方法:初始化要用于异步套接字方法的数据缓冲区。从上面的一些例子可以看到,不管同步还是异步,都需要一个缓冲数组来接受数据,使用SetBuffer可以简化缓冲区的设置。 LastOperation属性:获取最近使用此上下文对象执行的套接字操作类型。Accept、Receive、Send都可以用SocketAsyncEventArgs类,需要通过这个属性来判断上次进行的是什么操作。 SocketError:异步套接字操作的结果, SocketError.Success 表示操作成功完成 .Net 3.X这些Socket增强功能的主要特点是可以避免在异步套接字 I/O 量非常大时发生重复的对象分配和同步,怎么实现呢?就是利用很通用的对象缓冲池技术。 一般需要对两类对象使用对象池,一个对象池存放SocketAsyncEventArgs对象实例,一个对象池存放缓存byte[]实例,每次需要SocketAsyncEventArgs对象或者缓存数组时,就从对象池中取一个,用完了再放回对象池。对象池方式管理分配的对象属于通用的高效内存使用方式,不是Socket编程特有的模式。 这两个对象池实现的例子可以参考MSDN上的示例代码。 使用此组执行异步套接字操作的模式包含以下步骤:(来自MSDN) 1.分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象。 2.将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。 3.调用适当的套接字方法 (xxxAsync) 以启动异步操作。 4.如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。 5.如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。可以查询上下文属性来获取操作结果。 6.将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。 用代码来说明更直接一些,我们来改写上面的例子
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
|
< SPAN
style = "FONT-SIZE:
10pt" >//创建Socket对象,开始监听
//创建Accept用的SocketAsyncEventArgs对象实例,指定事件处理函数 SocketAsyncEventArgs<wbr>
acceptArgs = new SocketAsyncEventArgs();</wbr> acceptArgs.Completed +=
Process_Accept;<wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> <wbr></wbr> <wbr></wbr>//开始等待客户端的连接<wbr></wbr> if (!socket.AcceptAsync(e)) { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> Process_Accept(this,
e);
} <wbr></wbr> <wbr></wbr>//在事件处理函数Process_Accept中 ///。。。 <wbr></wbr> <wbr></wbr>//继续等待其他客户端连接 Socket acceptSocket =
e.AcceptSocket; StartAccept(e); <wbr></wbr> <wbr></wbr>//开始异步接收数据 SocketAsyncEventArgs<wbr>
socketReceiveArgs = new SocketAsyncEventArgs();</wbr> socketReceiveArgs.Completed +=
Process_Receive; socketReceiveArgs.SetBuffer(receiveBuffer, 0,
bufferSize); if
(!socket.ReceiveAsync(socketReceiveArgs)) { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> Process_Receive(this,
socketReceiveArgs);
} <wbr></wbr> <wbr></wbr>//在事件处理函数Process_Receive中处理接收到的数据 <wbr></wbr> <wbr></wbr>if (e.SocketError ==
SocketError.Success) { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> if
(e.BytesTransferred > 0)
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> byte[]
data = new byte[e.BytesTransferred];
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Buffer.BlockCopy(e.Buffer,
e.Offset, data, 0, e.BytesTransferred);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> //处理数据
<wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr> 。。。
<wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr> //继续接受数据
<wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr> socketReceiveArgs.SetBuffer(receiveBuffer,
0, bufferSize);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> if
(!socket.ReceiveAsync(socketReceiveArgs))
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> Process_Receive(this,
socketReceiveArgs);
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> }
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> else
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> {
<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> //关闭连接
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> }
} else { <wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> //错误处理
} </ SPAN >
|
同样以上代码仅供观赏,只演示了API的用法,很多情况没考虑,不具有实用价值
下一节介绍Socket的错误处理
Socket错误检测
这里只介绍一些基本的错误检测概念,不涉及一些具体类型的错误处理,比如检测客户端异常断开。
Socket编程常见的错误类型是SocketException,ScoketException是从System.ComponentModel.Win32Exception继承下来的,所以也有ErrorCode属性,NativeErrorCode属性和GetObjectData方法。NativeErrorCode对应Windows的Scoket错误代码,具体含义可以在MSDN上查找。
程序出现了Socket异常也不一定出现了严重的网络错误,需要关闭Socket连接,比如NativeErrorCode为10035,代表操作没法马上完成,比如缓冲区已满,或者缓冲区暂时没有数据可以接受。
另外一个会遇到的异常是ObjectDisposedException,Socket连接断开端口已经释放时会引发这个异常。
在Socket异步编程模型中,有些错误是不能通过Try Catche这样的方式检测的,为了判断是否发生错误,在.Net 2.0的异步编程模型里可以在调用BeginXXX或者EndXXX时使用SocketError类型的参数来检测,SocketError是一个枚举类型,表示Socket操作的处理结果,SocketError.Success代表成功处理,除此之外代表可能发生了错误,错误代码的含义应该和上面SocketException异常的NativeErrorCode是一致的。
.Net 3.0提供的Socket异步编程可以用SocketAsyncEventArgs类的SocketError属性来检测是否发生错误,SocketError属性是SocketError枚举类型,在OnCompleted的事件处理函数中应该先检测是否有错误发生,如果有错误要先根据错误类型处理错误。
接下来简单介绍Silverlight中的Scoket编程。
<wbr></wbr>
Silverlight中的socket编程
<wbr></wbr>
如果没有特声明,我们下面所讨论的内容都是基于Silverlight 3.0的。
Silverlight Socket编程和前面所述.net的socket编程类似,但是因为web的特殊环境,出于安全等因素的考虑,Silverlight Socket有一些特殊的限制。
首先,Silverlight Socket都是异步的,也就是说,需要用前面讲的.net 3.5的异步编程模式开发。
其次,Silverlight Socket只支持Tcp协议,并且Silverlight Socket限制了可使用端口的范围,端口必须在4502-4534范围之内。
另外一个比较重要的限制是Silverlight的安全策略系统。
<wbr></wbr>
Silverlight网络安全策略
<wbr></wbr>
出于安全方面的原因,Silverlight socket发起一个新的连接请求时,需要先向远端服务器请求一个策略文件,之后才允许网络连接访问该目标域下的网络资源。
请求策略文件时,Silverlight发送一个字符串<policy-file-request/>到服务器的943端口,服务器程序需要接收该请求,分析是否是策略请求后,发送一个策略文件的字符串给客户端。这个策略文件定义了Silverlight能访问那些资源以及允许使用什么样的方式访问。
<wbr></wbr>
Silverlight支持两种网络安全策略文件。
<wbr></wbr>
Flash 策略文件,此策略文件只可由 System.Net 命名空间中的 WebClient 和 HTTP 类使用。
Silverlight 策略文件,既可由 System.Net 命名空间中的 WebClient 和 HTTP 类使用,也可由 System.Net.Sockets 命名空间中的套接字类使用的 Silverlight 策略文件。
在连接某个网络资源之前,Silverlight会尝试从目标域下载安全策略文件。具体哪种类型取决于连接请求是来自WebClient或HTTP 类,还是来自Socket。客户端请求策略文件的过程是Silverlight自动处理的,不需要你写代码去控制。
<wbr></wbr>
如果Silverlight收到了服务器返回的策略文件,在Silverlight应用程序的整个会话期间,该文件将用作Socket针对该目标站点的所有后续请求的策略文件。
<wbr></wbr>
如果没有收到策略文件或者策略文件分析失败,Silverlight将拒绝到网络资源的连接,任何连接请求都将失败。
<wbr></wbr>
下面的内容使用 DTD 介绍 Silverlight 策略文件格式, 每个元素的含义请参考msdn
http://msdn.microsoft.com/zh-cn/library/cc645032(VS.95).aspx
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!– A DTD for the Silverlight Policy File –>
<!ELEMENT access-policy (cross-domain-access)>
<!ELEMENT cross-domain-access (policy+)>
<!ELEMENT policy (allow-from)>
<!ELEMENT policy (grant-to)>
<!ELEMENT allow-from (domain+)>
<!ATTLIST allow-from http-request-headers CDATA>
<!ELEMENT domain EMPTY >
<!ATTLIST domain uri CDATA #REQUIRED>
<!ELEMENT allow-from http-methods CDATA>
<!ELEMENT grant-to (resource+)>
<!ELEMENT grant-to (socket-resource+)>
<!ELEMENT grant-to EMPTY>
<!ATTLIST resource path CDATA #REQUIRED>
<!ATTLIST resource include-subpaths (true|false) “false”>
<!ATTLIST socket-resource port CDATA #REQUIRED protocol #REQUIRED>
<!– End of file. –>
<wbr></wbr>
下面是一个Socket策略文件的示例(MSDN的例子)
<?xml version=”1.0″
encoding =”utf-8″?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri=”file:///” />
</allow-from>
<grant-to>
<socket-resource port=”4502-4506″ protocol=”tcp”
/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
Socket策略请求服务器端的实现比较简单,可以参考MSDN Silverlight文档中《Silverlight中的网络安全访问限制》,网上的例子也很多,不再累述。
<wbr></wbr>
Web上其他的通信方式
<wbr></wbr>
除了Silverlight Socket之外,还有很多种web端和服务器的通信方式。
<wbr></wbr>
HTTP Polling
基于AJAX的轮询。比较容易理解,Web端以轮询方式向服务器发请求。这种方式从直观来看,实现起来最简单,性能也有问题,但是简单和复杂,快和慢都是相对的。
为了提高效率,可以减少每次request和reponse传输数据量,比如简化http header,简化cookie,采用Restful web service等等。
好像web qq和google wave都是HTTP Polling机制。
<wbr></wbr>
Comet
基于HTTP长连接的”服务器推”技术,大概两种类型:一种是服务器端阻塞异步AJAX http请求直到有数据或超时才返回,另外一种是利用Iframe服务器端将数据推到web端,与第一种不同的是,服务器端并不直接返回数据,而是返回对客户端Javascript函数的调用。
Comnet效率比HTTP Polling高,但是架构比较复杂。web客户端角度要注意浏览器对HTTP长连接数的限制,另外客户端的控制请求和数据请求应该使用不同的HTTP连接。服务器端因
为要维护大量的长连接需要注意服务的性能和可扩展性,有些web服务器专门针对comet优化过。
长连接通信的通用模式,服务器端和客户端之间需要实现心跳算法来检测另一方是否在线。
<wbr></wbr>
Flash XMLSocket
利用Flash的socket机制与Server通信。
<wbr></wbr>
Wcf HTTP Duplex Services
Silverlight 3.0可以使用Wcf HTTP Duplex Services实现Web端和服务器端的双向通信,网上有很多例子,HTTP Duplex Services是基于HTTP Polling的。
<wbr></wbr>
Web Sockets API
还只是草案,参考地址:http://dev.w3.org/html5/websockets/
现在google chrome最新的开发版已经支持Web Sockets API,测试代码如下
1
2
3
4
5
6
7
8
9
10
11
|
< SPAN
style = "FONT-SIZE:
10pt" >if ("WebSocket" in window)
{<wbr><wbr></wbr></wbr>
<wbr><wbr></wbr></wbr> var
ws = new WebSocket("ws://example.com/service");
<wbr><wbr></wbr></wbr> ws.onopen
= function() {
<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr> ws.send("message
to send");<wbr></wbr>
<wbr><wbr></wbr></wbr> };
<wbr><wbr></wbr></wbr> ws.onmessage
= function (evt) {alert(evt.data); };
<wbr><wbr></wbr></wbr> ws.onclose
= function() { alert("closed");};
} else { <wbr><wbr></wbr></wbr> alert("sad");
} </ SPAN >
|
http tunnel
讲到HTTP,顺便讲HTTP 隧道(http tunnel)。
因为很多防火墙或者代理服务器只支持http的相关端口,为了能够绕过这些限制,就要想办法利用http(https)协议。
一般防火墙和代理服务器只是简单的检查http头信息,可以要把要传输的数据伪装成符合http协议的文本数据。
另外防火墙和代理服务器一般不会检查https协议传输的内容,所以可以利用这一点来绕开端口限制,具体方法就是先发送一个http头:
CONNECT xxxxx.com:443
HTTP/1.0
HOST xxxxx.com:443
一些http头信息
//空行结束
代理服务器看到是https协议就会放行,返回
HTTP/1.0 200 Connection
Established
http 头信息//空行结束
连接就建立起来了。
<wbr></wbr>
通过配置文件来配置System.Net
<wbr></wbr>
详细配置元素信息参考
msdn 网络设置架构
http://msdn.microsoft.com/zh-cn/library/dacty7ed(VS.80).aspx
msdn system.Net 元素(网络设置)http://msdn.microsoft.com/zh-cn/library/6484zdc1(VS.80).aspx
<wbr></wbr>
Socket Trace
要Trace Socket的信息,可以自己写日志文件,也可以利用.Net自带的Trace功能
配置节例子
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.diagnostics>
<trace />
<sources>
<source name="System.Net" maxdatasize="1024">
<listeners>
<add name="MyTraceFile"/>
</listeners>
</source>
<source name="System.Net.Sockets">
<listeners>
<add name="MyTraceFile"/>
</listeners>
</source>
<sharedListeners>
<add
name="MyTraceFile"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="System.Net.trace.log"
/>
</sharedListeners>
<switches>
<add name="System.Net" value="Verbose" />
</switches>
</system.diagnostics>
</configuration>
.Net 4.0中Socket编程新功能
<wbr></wbr>
DnsEndPoint
silverlight中已经有DnsEndPoint了,.Net
4.0也会增加。
IPAddress[] IPs =
Dns.GetHostAddresses(“www.contoso.com”);
<wbr></wbr>
IP Version Neutrality
设置Socket的属性为IPv6Only为false就能兼容IPv4,可以不用分别为IPv4和IPv6创建socket对象了。
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
<wbr></wbr>
NAT Traversal
<wbr></wbr>
TcpListener的示例代码
var listener = new
TcpListener(IPAddress.IPv6Any, 8000);
listener.AllowNatTraversal(true);
listener.Start();
<wbr></wbr>
使用Socket的示例代码
Socket新增了一个方法
public void SetIPProtectionLevel (IPProtectionLevel);
枚举IPProtectionLevel的定义如下
public enum IPProtectionLevel
{
Unspecified = –1, // platform default
Unrestricted = 10, // global with NAT traversal
EdgeRestricted = 20, // global without NAT traversal
Restricted = 30, // site local
}
或者通过配置文件来设置
<system.net>
<settings>
<!– default is platform defined (Unspecified) –>
<socket ipProtectionLevel=”Unrestricted | EdgeRestricted | Restricted | Unspecified”/>
</settings>
</system.net>
详细信息参考End-to-end connectivity with NAT traversal
http://blogs.msdn.com/ncl/archive/2009/07/27/end-to-end-connectivity-with-nat-traversal-.aspx
<wbr></wbr>
Silverlight 4中的网络新功能
UDP Multicast
Silverlight 4增加了两个新类:UdpSingleSourceMulticast<wbr>Client和UdpAnySourceMulticastCli<wbr>ent来处理UDP Multicast。</wbr></wbr>
<wbr></wbr>
Net.TCP Port Sharing Service
按照官方的说法,性能和比HTTP Polling Duplex有极大的提高。
吞吐量:对于UI线程来说提升了5.5倍,对于worker线程来说提升了870倍。
客户端数量数:服务器可支持的连接客户端数量是之前的5-6倍。
相关推荐
论本土资源与法治的矛盾冲突 - - --兼谈法治的渊源与理念.docx
一个为RadioButton控件,(带源程序)可以定制漂亮的图形界面,提供...我才发现VS.net正式版也适用,.net beta2下编写的控件,vs.net 正式版下重新编译一下即可,注意测试时多放几个此控件。
PowerTCP Telnet For .NET ;;; ;;; 技术特性 应用场景 适用于.NET环境下各种通讯应用系统的建立 运用优势 开发环境 Microsoft Visual Studio .NET Microsoft Visual Basic .NET ...
1.可以观察一元,二元函数图形和分形图形绘制 2.字符计算功能,支持各种灵活的表达方式 如: exp(12.3+8*sin(6E-10)) ...
文件上传程序
1.修改了新闻内容不能打入空格的bug; 2.可以修改,删除新闻类别,并可以自定义新闻显示的数量; 3.可以删除已添加的新闻,修改新闻功能很快就能编好; 4.可以添加新闻标题链接到其他网页的新闻。 5.可编辑新闻; 6...
Javascript学习第一季--Javascript DOM 总结
毕业设计软件部分的一个课题,因为有别的部分,所以这个软件做的比较简单。 本软件是通过ADO连接ACCESS数据库。...对于任何疑问或探讨,请联系:hezhiqun@ynto.net
-- 企业先进性训练工作总结,报告总结,工作总结 .docx
数据通信与计算机网 第 3 章 数据链路层.ppt
将HTML标签外的空格替换为
开发环境 Microsoft Visual Studio .NET ; ; ; ;Microsoft Visual Basic .NET ; ; ; ;Microsoft Visual C++ .NET ; ; ; ;Microsoft Visual ...
A finite-time convergent Zhang neural network and its application to real-time matrix square root finding
A finite-time recurrent neural network for solving online time-varying Sylvester matrix equation based on a new ...
某着名顾问公司SPC教材-SPC 在制程中的应用.pptx
SRPBoard 繁体升级包 V1.2.1 -> V2.0
-新目标 [收集].pdf
ASP.NET页面优化器