Socket Basic Concepts 

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

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

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

Socket编程的几个基本概念 

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

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

 .Net Socket API

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

Socket Socket接口类
TcpClient TCP客户端类
TcpListener TCP侦听类
NetworkStream 用于网络访问的基础数据流

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

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

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

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

代码

private string DownloadPage(string path)
        {
            Uri uri = new Uri(path);
            Encoding encoding = Encoding.UTF8;// .GetEncoding("gb2312");

            string requestHeader = BuildRequestHeader(uri);
            byte[] requestBytes = encoding.GetBytes(requestHeader);
            byte[] receivedBytes = new byte[1024 * 100];

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(uri.Host,uri.Port);
            socket.Send(requestBytes);

            int receivedBytesLength = socket.Receive(receivedBytes);
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();

            string html = string.Empty;
            if (receivedBytesLength > 0)
            {
                html = encoding.GetString(receivedBytes, 0, receivedBytesLength);
            }
            return html;
        }

构造HTTP Header的代码如下,注意不要忘了Http头模板的最后一行

private string BuildRequestHeader(Uri uri)
        {
            string httpHeaderTemplate = @"GET {url} HTTP/1.1
Connection: Close
Host: {host}

";
            return httpHeaderTemplate.Replace("{url}", uri.AbsolutePath)
                .Replace("{host}", uri.Host);

        }

30秒思考题:这段简单代码有什么问题?

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

MemoryStream ms = new MemoryStream();
            while (true)
            {
                Console.WriteLine("Available :{0}", socket.Available);
                int receivedBytesLength = socket.Receive(receivedBytes, 0, receivedBytes.Length, SocketFlags.None);
                if (receivedBytesLength > 0)
                {
                    ms.Write(receivedBytes, 0, receivedBytesLength);
                }
                else
                {
                    break;
                }
            }

            string html = string.Empty;
            if (ms.Length > 0)
            {
                html = encoding.GetString(ms.ToArray(), 0, (int)ms.Length);
            }

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对象。

部分代码。
       

private void StartServer()
        {
            TcpListener server = new TcpListener(IPAddress.Any, 10000);
            server.Start();
            Debug.WriteLine("Server: start");

            TcpClient client = server.AcceptTcpClient();
            Debug.WriteLine("Server : connection accept");
            NetworkStream stream = client.GetStream();

            Process process = new Process();
            process.StartInfo.FileName = "cmd.exe";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = false;
            process.Start();

            process.OutputDataReceived += (Object sender, DataReceivedEventArgs e) =>
            {
                byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(e.Data + "\r\n");
                stream.Write(bytes, 0, bytes.Length);
            };
            process.BeginOutputReadLine();
            
            while (true)
            {
                Byte[] buffer = new Byte[1024 * 10];
                int length = stream.Read(buffer, 0, buffer.Length);
                if (length == 0)
                {
                    Debug.WriteLine("Server: read 0 byte");
                    break;
                }

                string command = Encoding.GetEncoding("gb2312").GetString(buffer, 0, length);
                Debug.WriteLine("Server: receive {0} ", command);

                StreamWriter Writer = process.StandardInput;
                Writer.WriteLine(command);
                Writer.Flush();
            }

            server.Stop();
            process.WaitForExit(1000);
            process.Close();
            Debug.WriteLine("Server: close");
        }

        TcpClient client;
        private void ConnectButton_Click(object sender, EventArgs e)
        {
            client = new TcpClient();
            client.Connect("localhost", 10000);
            Console.WriteLine("Client: connect");
            StartButton.Enabled = true;
        }

        private void StartButton_Click(object sender, EventArgs e)
        {
            if (CommandTextBox.Text.Trim().Length == 0)
            {
                return;
            }

            string request = CommandTextBox.Text.Trim();
            byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(request);
            NetworkStream stream = client.GetStream();
            stream.Write(bytes, 0, bytes.Length);
            
            Console.WriteLine("Client: request {0}", request);
            MessageTextBox.AppendText("\r\nresponse from server:\r\n");
            
            byte[] buffer = new byte[1024];
            do{
                int receivedBytesLength = stream.Read(buffer, 0, buffer.Length);
                if(receivedBytesLength > 0)
                {
                    string text = Encoding.GetEncoding("gb2312").GetString(buffer, 0, receivedBytesLength);
                    MessageTextBox.AppendText(text);
                    MessageTextBox.AppendText("\r\n");
                }
                else
                {
                    break;
                }
            }while(stream.DataAvailable);
        }


下一章介绍Socket异步编程模式

 

.Net Socket编程基础 异步编程

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

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

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

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

首先New一个Socket对象,绑定到特定端口,开始监听客户端发来的连接请求。
Socket socket;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(address, port));
socket.Listen(backlog); 
// backlog 参数指定队列中最多可容纳的等待接受的传入连接数,不同操作系统的backlog参数的上限是不一样的

//然后调用BeginAccept开始等待客户端连接。
while (true)
{
    acceptDone.Reset();
    socket.BeginAccept(new AsyncCallback(AcceptCallback), socket);
    acceptDone.WaitOne();
}

//acceptDone是信号量
private ManualResetEvent acceptDone = new ManualResetEvent(false);

//AcceptCallback是回调函数,下面是这个函数的部分代码

private void AcceptCallback(IAsyncResult ar)
{
    acceptDone.Set();
    // EndAccept 将会返回Windows自动分配的 Socket 对象实例
   Socket handler = listener.EndAccept(ar);
    log.InfoFormat("{0} connected", handler.RemoteEndPoint.ToString());

    //可以限制允许的最大连接数连接,保存当前对话的session信息等
   SocketState state = new SocketState(handler);
    SocketError errorCode;
    handler.BeginReceive(state.Buffer, 0, state.BufferSize, SocketFlags.None, out errorCode, new AsyncCallback(ReceiveCallback), state);

    if ((errorCode != SocketError.Success) && (errorCode != SocketError.IOPending))
    {
        ProcessError(handler, errorCode, "BeginReceive");
    }
}
BeginXXX的重载列表中一般有一个函数重载允许你传一个SocketError 类型的out参数,可以根据这个参数判断是否出错。

SocketState对象是为了方便写代码构造的用来保存当前Socket状态的类,比如上次接受数据是什么时间,一共接受了多少数据等。
public class SocketState : IDisposable
{
private Socket socket = null;
private const int bufferSize = 1024;
private byte[] buffer = new byte[bufferSize];
	。。。
}
操作系统接受到数据后会调用回调函数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引入了新的编程模型。
.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.将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。

用代码来说明更直接一些,我们来改写上面的例子
//创建Socket对象,开始监听
//创建Accept用的SocketAsyncEventArgs对象实例,指定事件处理函数
SocketAsyncEventArgs  acceptArgs = new SocketAsyncEventArgs();
acceptArgs.Completed += Process_Accept;       

//开始等待客户端的连接 
if (!socket.AcceptAsync(e))
{
    Process_Accept(this, e);
}

//在事件处理函数Process_Accept中
///。。。

//继续等待其他客户端连接
Socket acceptSocket = e.AcceptSocket;
StartAccept(e);

//开始异步接收数据
SocketAsyncEventArgs  socketReceiveArgs = new SocketAsyncEventArgs();
socketReceiveArgs.Completed += Process_Receive;
socketReceiveArgs.SetBuffer(receiveBuffer, 0, bufferSize);
if (!socket.ReceiveAsync(socketReceiveArgs))
{
    Process_Receive(this, socketReceiveArgs);
}

//在事件处理函数Process_Receive中处理接收到的数据

if (e.SocketError == SocketError.Success)
{
    if (e.BytesTransferred > 0)
    {
        byte[] data = new byte[e.BytesTransferred];
        Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
        //处理数据
      。。。
      //继续接受数据
      socketReceiveArgs.SetBuffer(receiveBuffer, 0, bufferSize);
        if (!socket.ReceiveAsync(socketReceiveArgs))
        {
             Process_Receive(this, socketReceiveArgs);
        }
    }
    else
    {
        //关闭连接
    }
}
else
{
    //错误处理
}
同样以上代码仅供观赏,只演示了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编程。

Silverlight中的socket编程

 

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

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

首先,Silverlight Socket都是异步的,也就是说,需要用前面讲的.net 3.5的异步编程模式开发。

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

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

 

Silverlight网络安全策略

 

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

请求策略文件时,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 
<?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. –>

 

下面是一个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中的网络安全访问限制》,网上的例子也很多,不再累述。

 

Web上其他的通信方式

 

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

 

HTTP Polling

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

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

好像web qq和google wave都是HTTP Polling机制。

 

Comet

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

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

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

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

 

Flash XMLSocket

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

 

Wcf HTTP Duplex Services

Silverlight 3.0可以使用Wcf HTTP Duplex Services实现Web端和服务器端的双向通信,网上有很多例子,HTTP Duplex Services是基于HTTP Polling的。

 

Web Sockets API

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

现在google chrome最新的开发版已经支持Web Sockets API,测试代码如下

if ("WebSocket" in window) {	
  var ws = new WebSocket("ws://example.com/service");
  ws.onopen = function() {
    ws.send("message to send"); 
  };
  ws.onmessage = function (evt) {alert(evt.data); };
  ws.onclose = function() { alert("closed");};
} else {
  alert("sad");
}


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 头信息//空行结束

连接就建立起来了。

 

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

 

详细配置元素信息参考

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

 

Socket Trace

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

配置节例子

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" />
<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编程新功能

 

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

IP Version Neutrality 
设置Socket的属性为IPv6Only为false就能兼容IPv4,可以不用分别为IPv4和IPv6创建socket对象了。 
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);

NAT Traversal

 

TcpListener的示例代码

var listener = new TcpListener(IPAddress.IPv6Any, 8000);
listener.AllowNatTraversal(true);
listener.Start();

使用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

 

Silverlight 4中的网络新功能

UDP Multicast

Silverlight 4增加了两个新类:UdpSingleSourceMulticastClient和UdpAnySourceMulticastClient来处理UDP Multicast。

 

Net.TCP Port Sharing Service

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

吞吐量:对于UI线程来说提升了5.5倍,对于worker线程来说提升了870倍。
客户端数量数:服务器可支持的连接客户端数量是之前的5-6倍。