认清事物的本质-简单

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

原文链接:http://msdn.microsoft.com/en-us/magazine/cc300760.aspx

仅拣关键地方翻译。

sockets是编写高性能服务程序里最经常用到的传输机制。在Win32® Windows® Sockets library (Winsock)里面包含了在使用sockets编写的程序里面提高性能的机制,并且在微软.NET框架里面提供了一个位于Winsock之上的层,使得我们可以在托管程序里面利用sockets通信(如图所示)。

Socket 基础

socket为通常是网络的通信定义了端点。每个socket可以承载任意数量的协议。但现今最经常用到的协议时UDP(User Datagram Protocol )协议和TCP(Transmission Control Protocol )协议。

UDP socket是无连接的,主要用在广播和多播方式的通信方面。它并不提供可靠的传输和消息队列,因此需要终端程序本身去检测和处理数据包的丢失和排列问题。

TCP socket是面向连接的,它提供一种端点之间的持续可靠连接,因此它最大的优点就是能够保证消息的准确传递和适合的消息队列。

TCP socket 可以作为客户端或者服务端。它们的不同之处在于服务端监听连接,而客户端socket 发起连接。socket 之间建立起连接之后,无论客户端还是服务器端都可以发送和接受数据以及关闭其间的通讯。

要在托管代码里建立一个基于TCP的服务器端socket ,第一步是实例化一个socket 类的对象。socket 的构造函数接收三个参数:AddressFamily类参数,SocketType类参数和ProtocolType类参数。其中,AddressFamily 指明该socket 使用的地址模式,其中最经常用到的两个AddressFamily 值是IPV4地址类的InterNetwork 和IPV6 地址类的InterNetworkV6 。SocketType 指明socket 要使用的通讯类型,最经常用的两个值是Stream (面向连接模式时采用)和Dgram (采用无连接模式时采用)。ProtocolType 指定使用的通讯协议,这个有很多值可以使用,例如Tcp, Udp, Idp, Ggp等等。举例说明,建立一个基于TCP协议的通讯,可以以以下方式创建socket :
Socket s = new Socket(
     AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

socket 建立以后,需要被绑定到一个地址之上。客户端的socket 可以不经过此过程,但对服务器端socket 来说,该过程是必须的。此时可以使用socket 的Bind 方法来绑定地址。绑定时需要得到要和socket 绑定的地址和端口,因此Bind方法接收一个继承自EndPoint类的实例作为参数。通常情况下是一个IPEndPoint 类的实例。

IPEndPoint 是网络通信的端点逻辑上的代表,它包含了一个IP地址和一个端口。IPEndPoint 类的构造函数使用该IP地址和端口号构造实例。端口号是一个代表要使用的端口的整数,而IP地址则可以是一个IPAddress类实例,也可以是一个长整数。有许多预先定义的通过IPAddress类的静态属性就可以适用的IPAddress类,其中基于TCP的socket 最经常用到的两个是IPAddress.Loopback 和IPAddress.Any。

IPAddress.Loopback 类是有名的地址(127.0.0.1) ,该地址指明使用当前机器的地址,这是一个不依赖与硬件和任何网络连接的假地址,使用它无需考虑实际的网络连接或者网络硬件就可以进行本地测试。

IPAddress.Any (0.0.0.0)指示服务器端监听所有网络接口的通信请求,而不是仅仅限定于指定的IP地址。在两种情况下并不使用该地址:一,当需要控制连接服务器端的连接时不使用。如果在服务器端有多个网卡,经常遇到的情况是双宿主服务器,你就可以通过设置每个网卡需要想要监听的地址,达到监听其中一个接口的所有连接(比如企业内部网)的同时监听另一个接口的所有连接(例如因特网)的目的。二,为了安全。如果构造的socket能够复用一个地址(也就是说这个socket 被绑定到一个已经在使用中的地址),那么,当别人连接你的服务器端程序使用 的同样的端口时,那么底层的socket子系统就必须决定哪一个绑定到该地址的socket接收传输近来的数据包。通常与该地址绑定更紧密的服务器端程序接收数据。System.Net.NetworkInformation 命名空间对系统中的每一个网络接口进行了详细说明。下面的代码打印出每个网卡的每个单播IP地址:

foreach (NetworkInterface nic in 
    NetworkInterface.GetAllNetworkInterfaces())
{
    Console.WriteLine(nic.Name);
    foreach (UnicastIPAddressInformation addrInfo in 
        nic.GetIPProperties().UnicastAddresses)
    {
        Console.WriteLine("\t" + addrInfo.Address);
    }
}  

连接

程序在得到服务器端的地址以后才能进行连接或者与其绑定。获得该地址的一个途径是通过System.Net.Dns 类。Dns 类为IP地址提供了查找服务,为主机名称提供了反查找服务。它的许多静态成员能够进行网络地址和名称的转换。其中,Dns.GetHostEntry 方法返回的是IPHostEntry ,其中包含了一台电脑的所有相关信息的清单。它列出了这台电脑发布到DNS服务器的所有IP地址。

Bind方法调用以后,Socket.Listen 方法为socket建立一个内部的连接队列。当客户端试图进行连接时,连接请求就会首先被放到这个队列里。Listen 方法接收一个参数,该参数设定该队列里面的最大连接数。Socket.Accept 方法将队列里面的第一个请求拉出,并返回一个新socket对象,该对象就可以用作与客户端进行通信。

对于客户端,在实例化完成之后一般要调用Connect 方法。在此之前,如果需要将该客户端与特定端口绑定,也可以调用Bind 方法。如果不绑定该socket的话,Connect 方法会自动选择一个端口。调用Connect 方法会试图与服务器建立一个连接。Connect 方法也有一个EndPoint参数,该参数指定要连接的目标主机。连接建立以后,无论是客户端还是服务器端都可以发送数据到对方或者从对方接收数据。

连接建立之后,如果程序终止了通信,就必须关闭socket。Shutdown 方法用来正常的关闭socket。该方法将会发送尚未发送的数据,接收尚未接受的数据。如果是异步从一个socket里面读取数据,并且接收的数据为0比特,那么该点就是已经关闭了改socket。

下表总结了创建服务器端socket和客户端socket所需要的步骤。可以看出socket基础实际上是相当简单的,难点在于如何让socket程序运行高效。

当socket遇到网络错误的时候,SocketException 就会被抛出。该异常包含了从Win32传递出来的错误代码。对于熟悉Win32 Winsock 开发的人来说,SocketException.ErrorCode 与调用Winsock 库里的WSAGetLastError 函数返回的值相同。其中几个关键应该非常熟悉的错误代码如下表所示。在.NET2.0框架里,SocketException.SocketErrorCode 通过SocketError 枚举值提供了相同的错误信息。

posted on 2009-09-15 19:15  萧冲  阅读(991)  评论(0编辑  收藏  举报