您的位置:首页 > 编程语言

揭开Socket编程的面纱 (四)

2011-07-25 12:29 489 查看

SocketBasicConcepts

首先介绍Socket的一些基本概念

Socket是操作系统提供的一系列网络编程接口。

网络模型分若干层,也有一些协议,比如TCP协议,UDP协议等,这些都是抽象的定义,在硬件以及操作系统级别上有一些对应的实现,Socket可以看做操作系统为开发人员提供的一系列网络编程接口,它封装了一些协议的细节,比如怎么组织数据包,怎么发送数据之类的。

Socket编程的几个基本概念

Endpoint
Endpoin指定要连接到哪里,Endpoint包括两部分内容,IP和Port,IP地址和端口组合起来才能唯一指定远程的通信端。

AddressFamily
怎么寻址,有了IP地址之后就是如何寻址的问题,常用的寻址方案是IPV4和IPV6两种类型,windows操作系统从VISTA和Windows20008起默认支持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数据的编程接口。

SocketsV4、SocketsV5有什么区别?

经常看到一些软件可以设置Sockets4/Sockets5代理,简单说他们是客户端与外网服务器之间通讯的协议,Sockets是位于应用层与传输层之间的中间层。SocketsV4支持TCP,SocketsV5支持TCP/UDP,支持安全认证,支持IPV6。

Socket能够同时接受和发送数据吗?

TCP协议是双工的

Socket如何保证数据按顺序到达?

TCP协议来保证

Socket的基本通信模型模型

客户端:

Socket()
Connect
Send
Close

服务器端:

Socket()
Bind
Listen
Accept
Receive
Send
Close

客户端和服务器端模型是不一样的,两边是非对称的。

.NetSocketAPI

下面是.NetSocket编程最基本的几个类,位于命名空间System.Net.Sockets

SocketSocket接口类
TcpClientTCP客户端类
TcpListenerTCP侦听类
NetworkStream用于网络访问的基础数据流

其他经常用到的辅助类,位于命名空间System.Net

Dns域名解析
EndPoint标识网络地址
IPAddressIP地址。
NetworkCredential基于密码的身份验证方案,不支持基于公钥的身份验证方法(比如ssl)

一个Socket的简单例子

输入网址,获得HTML页面的一段演示代码,只是演示Socket对象的几个主要功能,不具有实用价值。

基本步骤为:建立Socket对象,连接服务器,发送数据,然后接受数据,对应上一章介绍的Socket通信模型。

代码

viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>privatestringDownloadPage(stringpath)
02
{
03
Uriuri=newUri(path);
04
Encodingencoding=Encoding.UTF8;//.GetEncoding("gb2312");
05
06
stringrequestHeader=BuildRequestHeader(uri);
07
byte[]requestBytes=encoding.GetBytes(requestHeader);
08
byte[]receivedBytes=newbyte[1024*100];
09
10
Socketsocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
11
socket.Connect(uri.Host,uri.Port);
12
socket.Send(requestBytes);
13
14
intreceivedBytesLength=socket.Receive(receivedBytes);
15
socket.Shutdown(SocketShutdown.Both);
16
socket.Close();
17
18
stringhtml=string.Empty;
19
if(receivedBytesLength>0)
20
{
21
html=encoding.GetString(receivedBytes,0,receivedBytesLength);
22
}
23
returnhtml;
24
}</
SPAN
>
构造HTTPHeader的代码如下,注意不要忘了Http头模板的最后一行

viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>privatestringBuildRequestHeader(Uriuri)
02
{
03
stringhttpHeaderTemplate=@"GET{url}HTTP/1.1
04
Connection:Close
05
Host:{host}
06
07
";
08
returnhttpHeaderTemplate.Replace("{url}",uri.AbsolutePath)
09
.Replace("{host}",uri.Host);
10
11
}</
SPAN
>
30秒思考题:这段简单代码有什么问题?

我们定义的用来接收数据的数组大小是固定的,如果要接受的数据超过数组大小怎么办?可以定义一个缓冲区,每次接受固定大小的数据,直到接收完成为止。示例代码如下:

viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>MemoryStreamms=newMemoryStream();
02
while(true)
03
{
04
Console.WriteLine("Available:{0}",socket.Available);
05
intreceivedBytesLength=socket.Receive(receivedBytes,0,receivedBytes.Length,SocketFlags.None);
06
if(receivedBytesLength>0)
07
{
08
ms.Write(receivedBytes,0,receivedBytesLength);
09
}
10
else
11
{
12
break;
13
}
14
}
15
16
stringhtml=string.Empty;
17
if(ms.Length>0)
18
{
19
html=encoding.GetString(ms.ToArray(),0,(int)ms.Length);
20
}</
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对象。

部分代码。

viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>privatevoidStartServer()
02
{
03
TcpListenerserver=newTcpListener(IPAddress.Any,10000);
04
server.Start();
05
Debug.WriteLine("Server:start");
06
07
TcpClientclient=server.AcceptTcpClient();
08
Debug.WriteLine("Server:connectionaccept");
09
NetworkStreamstream=client.GetStream();
10
11
Processprocess=newProcess();
12
process.StartInfo.FileName="cmd.exe";
13
process.StartInfo.UseShellExecute=false;
14
process.StartInfo.RedirectStandardInput=true;
15
process.StartInfo.RedirectStandardOutput=true;
16
process.StartInfo.RedirectStandardError=true;
17
process.StartInfo.CreateNoWindow=false;
18
process.Start();
19
20
process.OutputDataReceived+=(Objectsender,DataReceivedEventArgse)=>
21
{
22
byte[]bytes=Encoding.GetEncoding("gb2312").GetBytes(e.Data+"\r\n");
23
stream.Write(bytes,0,bytes.Length);
24
};
25
process.BeginOutputReadLine();
26
27
while(true)
28
{
29
Byte[]buffer=newByte[1024*10];
30
intlength=stream.Read(buffer,0,buffer.Length);
31
if(length==0)
32
{
33
Debug.WriteLine("Server:read0byte");
34
break;
35
}
36
37
stringcommand=Encoding.GetEncoding("gb2312").GetString(buffer,0,length);
38
Debug.WriteLine("Server:receive{0}",command);
39
40
StreamWriterWriter=process.StandardInput;
41
Writer.WriteLine(command);
42
Writer.Flush();
43
}
44
45
server.Stop();
46
process.WaitForExit(1000);
47
process.Close();
48
Debug.WriteLine("Server:close");
49
}
50
51
TcpClientclient;
52
privatevoidConnectButton_Click(objectsender,EventArgse)
53
{
54
client=newTcpClient();
55
client.Connect("localhost",10000);
56
Console.WriteLine("Client:connect");
57
StartButton.Enabled=true;
58
}
59
60
privatevoidStartButton_Click(objectsender,EventArgse)
61
{
62
if(CommandTextBox.Text.Trim().Length==0)
63
{
64
return;
65
}
66
67
stringrequest=CommandTextBox.Text.Trim();
68
byte[]bytes=Encoding.GetEncoding("gb2312").GetBytes(request);
69
NetworkStreamstream=client.GetStream();
70
stream.Write(bytes,0,bytes.Length);
71
72
Console.WriteLine("Client:request{0}",request);
73
MessageTextBox.AppendText("\r\nresponsefromserver:\r\n");
74
75
byte[]buffer=newbyte[1024];
76
do{
77
intreceivedBytesLength=stream.Read(buffer,0,buffer.Length);
78
if(receivedBytesLength>0)
79
{
80
stringtext=Encoding.GetEncoding("gb2312").GetString(buffer,0,receivedBytesLength);
81
MessageTextBox.AppendText(text);
82
MessageTextBox.AppendText("\r\n");
83
}
84
else
85
{
86
break;
87
}
88
}while(stream.DataAvailable);
89
}</
SPAN
>
下一章介绍Socket异步编程模式

.NetSocket编程基础异步编程

同步socket并发性很差,特别是对于服务器端来说,要处理很多客户端连接,同步Socket力不从心,要提高系统的并发处理能力,就要借助.Net异步编程模式。

.NetSocket的异步编程模式和.Net通用的APM编程模式是一致的,调用BeginXXX方法开始异步操作,系统系统处理完成后调用回调函数,在回调函数中调用EndXXX结束操作。

常用的Socket对象的异步API有
接受客户端连接BeginAccept,EndAccept
接收数据BeginReceive,EndReceive
发送数据BeginSend,EndSend

下面是简单的Socket服务端实现的部分,演示Socket异步API的使用。

首先New一个Socket对象,绑定到特定端口,开始监听客户端发来的连接请求。



viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>Socketsocket;
02
socket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
03
socket.Bind(newIPEndPoint(address,port));
04
socket.Listen(backlog);
05
//backlog参数指定队列中最多可容纳的等待接受的传入连接数,不同操作系统的backlog参数的上限是不一样的
06
07
//然后调用BeginAccept开始等待客户端连接。
08
while(true)
09
{
10
acceptDone.Reset();
11
socket.BeginAccept(newAsyncCallback(AcceptCallback),socket);
12
acceptDone.WaitOne();
13
}
14
15
//acceptDone是信号量
16
privateManualResetEventacceptDone=newManualResetEvent(false);
17
18
//AcceptCallback是回调函数,下面是这个函数的部分代码
19
20
privatevoidAcceptCallback(IAsyncResultar)
21
{
22
acceptDone.Set();
23
//EndAccept将会返回Windows自动分配的Socket对象实例
24
Sockethandler=listener.EndAccept(ar);
25
log.InfoFormat("{0}connected",handler.RemoteEndPoint.ToString());
26
27
//可以限制允许的最大连接数连接,保存当前对话的session信息等
28
SocketStatestate=newSocketState(handler);
29
SocketErrorerrorCode;
30
handler.BeginReceive(state.Buffer,0,state.BufferSize,SocketFlags.None,outerrorCode,newAsyncCallback(ReceiveCallback),state);
31
32
if((errorCode!=SocketError.Success)&&(errorCode!=SocketError.IOPending))
33
{
34
ProcessError(handler,errorCode,"BeginReceive");
35
}
36
}
37
</
SPAN
>
BeginXXX的重载列表中一般有一个函数重载允许你传一个SocketError类型的out参数,可以根据这个参数判断是否出错。

SocketState对象是为了方便写代码构造的用来保存当前Socket状态的类,比如上次接受数据是什么时间,一共接受了多少数据等。



viewsourceprint?

1
<
SPAN
style
=
"FONT-SIZE:10pt"
>publicclassSocketState:IDisposable
2
{
3
privateSocketsocket=null;
4
privateconstintbufferSize=1024;
5
privatebyte[]buffer=newbyte[bufferSize];
6
。。。
7
}
8
</
SPAN
>
操作系统接受到数据后会调用回调函数ReceiveCallback

privatevoidReceiveCallback(IAsyncResultar)
{
SocketStatestate=(SocketState)ar.AsyncState;
Sockethandler=state.Socket;

intbytesRead=handler.EndReceive(ar);

if(bytesRead>0)
{
//处理接受的数据
。。。
//如果还有数据,继续接受
if(handler.Available>0||handler.Connected)
{
SocketErrorerrorCode;
handler.BeginReceive(state.Buffer,0,state.BufferSize,SocketFlags.None,outerrorCode,newAsyncCallback(ReceiveCallback),state);
}
else
{
handler.Close();
//其他处理代码,比如重置连接计数器等
}
}
}

上面代码只是简单介绍API的用法,很多地方没有考虑,完整的Socket服务代码比这个要复杂得多。

BeginXXX、EndXXX这种异步模式比同步方式要高效得多,但是每次需要New一个IAsyncResult对象和state对象,并发高的情况下对GC也是不小的压力,频繁的申请、释放小块内存,容易产生很多小的内存碎片,内存碎片多的话内存利用率低,而且可能出现即使内存没有全部用完,但是.NetRuntime还是会报告没有可分配的内存的情况。另一方便,编程模型比较复杂。

为了进一步简化Socket异步编程,.Net3.5引入了新的编程模型。

.Net3.x的Socket异步编程

在.Net3.5中引入了一组增强功能,提供可供专用的高性能套接字应用程序使用的可选异步模式,并且简化了Socket异步编程复杂度。SocketAsyncEventArgs类是这组增强功能中最常用的一个类,专为需要高性能的网络服务器应用程序而设计。它主要的成员如下:

OnCompleted事件:在异步操作完成后,系统会触发OnCompleted事件,在事件处理代码中可以进行后续异步套接字操作的处理。
SetBuffer方法:初始化要用于异步套接字方法的数据缓冲区。从上面的一些例子可以看到,不管同步还是异步,都需要一个缓冲数组来接受数据,使用SetBuffer可以简化缓冲区的设置。
LastOperation属性:获取最近使用此上下文对象执行的套接字操作类型。Accept、Receive、Send都可以用SocketAsyncEventArgs类,需要通过这个属性来判断上次进行的是什么操作。
SocketError:异步套接字操作的结果,SocketError.Success表示操作成功完成

.Net3.X这些Socket增强功能的主要特点是可以避免在异步套接字I/O量非常大时发生重复的对象分配和同步,怎么实现呢?就是利用很通用的对象缓冲池技术。

一般需要对两类对象使用对象池,一个对象池存放SocketAsyncEventArgs对象实例,一个对象池存放缓存byte[]实例,每次需要SocketAsyncEventArgs对象或者缓存数组时,就从对象池中取一个,用完了再放回对象池。对象池方式管理分配的对象属于通用的高效内存使用方式,不是Socket编程特有的模式。

这两个对象池实现的例子可以参考MSDN上的示例代码。

使用此组执行异步套接字操作的模式包含以下步骤:(来自MSDN)
1.分配一个新的SocketAsyncEventArgs上下文对象,或者从应用程序池中获取一个空闲的此类对象。
2.将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。
3.调用适当的套接字方法(xxxAsync)以启动异步操作。
4.如果异步套接字方法(xxxAsync)返回true,则在回调中查询上下文属性来获取完成状态。
5.如果异步套接字方法(xxxAsync)返回false,则说明操作是同步完成的。可以查询上下文属性来获取操作结果。
6.将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。

用代码来说明更直接一些,我们来改写上面的例子



viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>//创建Socket对象,开始监听
02
//创建Accept用的SocketAsyncEventArgs对象实例,指定事件处理函数
03
SocketAsyncEventArgsacceptArgs=newSocketAsyncEventArgs();
04
acceptArgs.Completed+=Process_Accept;
05
06
//开始等待客户端的连接
07
if(!socket.AcceptAsync(e))
08
{
09
Process_Accept(this,e);
10
}
11
12
//在事件处理函数Process_Accept中
13
///。。。
14
15
//继续等待其他客户端连接
16
SocketacceptSocket=e.AcceptSocket;
17
StartAccept(e);
18
19
//开始异步接收数据
20
SocketAsyncEventArgssocketReceiveArgs=newSocketAsyncEventArgs();
21
socketReceiveArgs.Completed+=Process_Receive;
22
socketReceiveArgs.SetBuffer(receiveBuffer,0,bufferSize);
23
if(!socket.ReceiveAsync(socketReceiveArgs))
24
{
25
Process_Receive(this,socketReceiveArgs);
26
}
27
28
//在事件处理函数Process_Receive中处理接收到的数据
29
30
if(e.SocketError==SocketError.Success)
31
{
32
if(e.BytesTransferred>0)
33
{
34
byte[]data=newbyte[e.BytesTransferred];
35
Buffer.BlockCopy(e.Buffer,e.Offset,data,0,e.BytesTransferred);
36
//处理数据
37
。。。
38
//继续接受数据
39
socketReceiveArgs.SetBuffer(receiveBuffer,0,bufferSize);
40
if(!socket.ReceiveAsync(socketReceiveArgs))
41
{
42
Process_Receive(this,socketReceiveArgs);
43
}
44
}
45
else
46
{
47
//关闭连接
48
}
49
}
50
else
51
{
52
//错误处理
53
}
54
</
SPAN
>
同样以上代码仅供观赏,只演示了API的用法,很多情况没考虑,不具有实用价值

下一节介绍Socket的错误处理

Socket错误检测

这里只介绍一些基本的错误检测概念,不涉及一些具体类型的错误处理,比如检测客户端异常断开。

Socket编程常见的错误类型是SocketException,ScoketException是从System.ComponentModel.Win32Exception继承下来的,所以也有ErrorCode属性,NativeErrorCode属性和GetObjectData方法。NativeErrorCode对应Windows的Scoket错误代码,具体含义可以在MSDN上查找。

程序出现了Socket异常也不一定出现了严重的网络错误,需要关闭Socket连接,比如NativeErrorCode为10035,代表操作没法马上完成,比如缓冲区已满,或者缓冲区暂时没有数据可以接受。

另外一个会遇到的异常是ObjectDisposedException,Socket连接断开端口已经释放时会引发这个异常。

在Socket异步编程模型中,有些错误是不能通过TryCatche这样的方式检测的,为了判断是否发生错误,在.Net2.0的异步编程模型里可以在调用BeginXXX或者EndXXX时使用SocketError类型的参数来检测,SocketError是一个枚举类型,表示Socket操作的处理结果,SocketError.Success代表成功处理,除此之外代表可能发生了错误,错误代码的含义应该和上面SocketException异常的NativeErrorCode是一致的。

.Net3.0提供的Socket异步编程可以用SocketAsyncEventArgs类的SocketError属性来检测是否发生错误,SocketError属性是SocketError枚举类型,在OnCompleted的事件处理函数中应该先检测是否有错误发生,如果有错误要先根据错误类型处理错误。

接下来简单介绍Silverlight中的Scoket编程。


Silverlight中的socket编程

如果没有特声明,我们下面所讨论的内容都是基于Silverlight3.0的。

SilverlightSocket编程和前面所述.net的socket编程类似,但是因为web的特殊环境,出于安全等因素的考虑,SilverlightSocket有一些特殊的限制。

首先,SilverlightSocket都是异步的,也就是说,需要用前面讲的.net3.5的异步编程模式开发。

其次,SilverlightSocket只支持Tcp协议,并且SilverlightSocket限制了可使用端口的范围,端口必须在4502-4534范围之内。

另外一个比较重要的限制是Silverlight的安全策略系统。

Silverlight网络安全策略

出于安全方面的原因,Silverlightsocket发起一个新的连接请求时,需要先向远端服务器请求一个策略文件,之后才允许网络连接访问该目标域下的网络资源。

请求策略文件时,Silverlight发送一个字符串<policy-file-request/>到服务器的943端口,服务器程序需要接收该请求,分析是否是策略请求后,发送一个策略文件的字符串给客户端。这个策略文件定义了Silverlight能访问那些资源以及允许使用什么样的方式访问。

Silverlight支持两种网络安全策略文件。

Flash策略文件,此策略文件只可由System.Net命名空间中的WebClient和HTTP类使用。

Silverlight策略文件,既可由System.Net命名空间中的WebClient和HTTP类使用,也可由System.Net.Sockets命名空间中的套接字类使用的Silverlight策略文件。

在连接某个网络资源之前,Silverlight会尝试从目标域下载安全策略文件。具体哪种类型取决于连接请求是来自WebClient或HTTP类,还是来自Socket。客户端请求策略文件的过程是Silverlight自动处理的,不需要你写代码去控制。

如果Silverlight收到了服务器返回的策略文件,在Silverlight应用程序的整个会话期间,该文件将用作Socket针对该目标站点的所有后续请求的策略文件。

如果没有收到策略文件或者策略文件分析失败,Silverlight将拒绝到网络资源的连接,任何连接请求都将失败。

下面的内容使用DTD介绍Silverlight策略文件格式,每个元素的含义请参考msdn
http://msdn.microsoft.com/zh-cn/library/cc645032(VS.95).aspx
<?xmlversion=”1.0″encoding=”ISO-8859-1″?>

<!–ADTDfortheSilverlightPolicyFile–>

<!ELEMENTaccess-policy(cross-domain-access)>

<!ELEMENTcross-domain-access(policy+)>

<!ELEMENTpolicy(allow-from)>

<!ELEMENTpolicy(grant-to)>

<!ELEMENTallow-from(domain+)>

<!ATTLISTallow-fromhttp-request-headersCDATA>

<!ELEMENTdomainEMPTY>

<!ATTLISTdomainuriCDATA#REQUIRED>

<!ELEMENTallow-fromhttp-methodsCDATA>

<!ELEMENTgrant-to(resource+)>

<!ELEMENTgrant-to(socket-resource+)>

<!ELEMENTgrant-toEMPTY>

<!ATTLISTresourcepathCDATA#REQUIRED>

<!ATTLISTresourceinclude-subpaths(true|false)“false”>

<!ATTLISTsocket-resourceportCDATA#REQUIREDprotocol#REQUIRED>

<!–Endoffile.–>

下面是一个Socket策略文件的示例(MSDN的例子)

<?xmlversion=”1.0″encoding=”utf-8″?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domainuri=”file:///”/>
</allow-from>
<grant-to>
<socket-resourceport=”4502-4506″protocol=”tcp”/>
</grant-to>
</policy>

</cross-domain-access>

</access-policy>

Socket策略请求服务器端的实现比较简单,可以参考MSDNSilverlight文档中《Silverlight中的网络安全访问限制》,网上的例子也很多,不再累述。

Web上其他的通信方式

除了SilverlightSocket之外,还有很多种web端和服务器的通信方式。

HTTPPolling

基于AJAX的轮询。比较容易理解,Web端以轮询方式向服务器发请求。这种方式从直观来看,实现起来最简单,性能也有问题,但是简单和复杂,快和慢都是相对的。

为了提高效率,可以减少每次request和reponse传输数据量,比如简化httpheader,简化cookie,采用Restfulwebservice等等。

好像webqq和googlewave都是HTTPPolling机制。

Comet

基于HTTP长连接的”服务器推”技术,大概两种类型:一种是服务器端阻塞异步AJAXhttp请求直到有数据或超时才返回,另外一种是利用Iframe服务器端将数据推到web端,与第一种不同的是,服务器端并不直接返回数据,而是返回对客户端Javascript函数的调用。

Comnet效率比HTTPPolling高,但是架构比较复杂。web客户端角度要注意浏览器对HTTP长连接数的限制,另外客户端的控制请求和数据请求应该使用不同的HTTP连接。服务器端因

为要维护大量的长连接需要注意服务的性能和可扩展性,有些web服务器专门针对comet优化过。

长连接通信的通用模式,服务器端和客户端之间需要实现心跳算法来检测另一方是否在线。

FlashXMLSocket

利用Flash的socket机制与Server通信。

WcfHTTPDuplexServices

Silverlight3.0可以使用WcfHTTPDuplexServices实现Web端和服务器端的双向通信,网上有很多例子,HTTPDuplexServices是基于HTTPPolling的。

WebSocketsAPI

还只是草案,参考地址:http://dev.w3.org/html5/websockets/

现在googlechrome最新的开发版已经支持WebSocketsAPI,测试代码如下

viewsourceprint?

01
<
SPAN
style
=
"FONT-SIZE:10pt"
>if("WebSocket"inwindow){
02
varws=newWebSocket("ws://example.com/service");
03
ws.onopen=function(){
04
ws.send("messagetosend");
05
};
06
ws.onmessage=function(evt){alert(evt.data);};
07
ws.onclose=function(){alert("closed");};
08
}else{
09
alert("sad");
10
}
11
</
SPAN
>
httptunnel

讲到HTTP,顺便讲HTTP隧道(httptunnel)。

因为很多防火墙或者代理服务器只支持http的相关端口,为了能够绕过这些限制,就要想办法利用http(https)协议。

一般防火墙和代理服务器只是简单的检查http头信息,可以要把要传输的数据伪装成符合http协议的文本数据。

另外防火墙和代理服务器一般不会检查https协议传输的内容,所以可以利用这一点来绕开端口限制,具体方法就是先发送一个http头:

CONNECTxxxxx.com:443HTTP/1.0
HOSTxxxxx.com:443
一些http头信息
//空行结束

代理服务器看到是https协议就会放行,返回

HTTP/1.0200ConnectionEstablished
http头信息//空行结束

连接就建立起来了。

通过配置文件来配置System.Net

详细配置元素信息参考

msdn网络设置架构http://msdn.microsoft.com/zh-cn/library/dacty7ed(VS.80).aspxmsdnsystem.Net元素(网络设置)http://msdn.microsoft.com/zh-cn/library/6484zdc1(VS.80).aspx

SocketTrace

要TraceSocket的信息,可以自己写日志文件,也可以利用.Net自带的Trace功能

配置节例子

<?xmlversion="1.0"encoding="UTF-8"?>

<configuration>

<system.diagnostics>

<traceautoflush="true"/>

<sources>

<sourcename="System.Net"maxdatasize="1024">

<listeners>

<addname="MyTraceFile"/>

</listeners>

</source>

<sourcename="System.Net.Sockets">

<listeners>

<addname="MyTraceFile"/>

</listeners>

</source>

<sharedListeners>

<add

name="MyTraceFile"

type="System.Diagnostics.TextWriterTraceListener"

initializeData="System.Net.trace.log"

/>

</sharedListeners>

<switches>

<addname="System.Net"value="Verbose"/>

</switches>

</system.diagnostics>

</configuration>

.Net4.0中Socket编程新功能

DnsEndPoint

silverlight中已经有DnsEndPoint了,.Net4.0也会增加。
IPAddress[]IPs=Dns.GetHostAddresses(“www.contoso.com”);

IPVersionNeutrality

设置Socket的属性为IPv6Only为false就能兼容IPv4,可以不用分别为IPv4和IPv6创建socket对象了。

socket.SetSocketOption(SocketOptionLevel.IPv6,SocketOptionName.IPv6Only,0);

NATTraversal

TcpListener的示例代码

varlistener=newTcpListener(IPAddress.IPv6Any,8000);
listener.AllowNatTraversal(true);
listener.Start();

使用Socket的示例代码

Socket新增了一个方法
publicvoidSetIPProtectionLevel(IPProtectionLevel);

枚举IPProtectionLevel的定义如下

publicenumIPProtectionLevel
{
Unspecified=–1,//platformdefault
Unrestricted=10,//globalwithNATtraversal
EdgeRestricted=20,//globalwithoutNATtraversal
Restricted=30,//sitelocal
}

或者通过配置文件来设置

<system.net>

<settings>

<!–defaultisplatformdefined(Unspecified)–>

<socketipProtectionLevel=”Unrestricted|EdgeRestricted|Restricted|Unspecified”/>

</settings>

</system.net>

详细信息参考End-to-endconnectivitywithNATtraversal

http://blogs.msdn.com/ncl/archive/2009/07/27/end-to-end-connectivity-with-nat-traversal-.aspx

Silverlight4中的网络新功能

UDPMulticast

Silverlight4增加了两个新类:UdpSingleSourceMulticastClient和UdpAnySourceMulticastClient来处理UDPMulticast。

Net.TCPPortSharingService

按照官方的说法,性能和比HTTPPollingDuplex有极大的提高。

吞吐量:对于UI线程来说提升了5.5倍,对于worker线程来说提升了870倍。
客户端数量数:服务器可支持的连接客户端数量是之前的5-6倍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: