.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 &gt; 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 &gt; 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 &gt; 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>


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 >

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 >



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 >



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功能

配置节例子




























.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的定义如下


或者通过配置文件来设置

<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倍。

分类: .Net
标签: .net, socket, silverlight
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用的内容,这段引用中包含了一些Vue.js代码和HTML代码,并讨论了换行符和空格在网页中的使用。其中,引用是关于添加和还原换行符的方法,引用是关于在编辑数据时获取详情和使用textarea的示例代码,引用是关于HTML中空格的处理和字符实体的使用。 问题中提到的&nbsp;是HTML中表示空格的字符实体。HTML通常会自动截取多余的空格,不管你加多少空格,都被看做一个空格。为了在网页中增加空格,可以使用&nbsp;表示空格。此外,还有其他常用的字符实体,如<表示小于号,>表示大于号,&表示&符号,"表示双引号,等等。 需要注意的是,引用的内容中有一些HTML实体被转义了,所以在描述字符实体时需要参考编码转义后的字符。 根据给定的引用内容,我无法直接回答问题,因为问题中的信息不完整。请提供更具体的问题或补充说明,我将尽力为您提供帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [js+v-html+正则表达式去除所有& nbsp和html标签(& nbsp 换行 )](https://blog.csdn.net/xm_w_xm/article/details/91992754)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [html&nbsp;&nbsp;特殊字符(转)](https://blog.csdn.net/feiniu5566/article/details/13624619)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值