《C#+30网络编程》由会员分享,可在线阅读,更多相关《C#+30网络编程(14页珍藏版)》请在金锄头文库上搜索。
1、第第 11 章章 网络编程网络编程 学习目标: 网络无疑是当前计算机程序中最重要的应用平台。微软推出的这一套.NET 框架,顾名思义就是要在网络上建立一个统一的操作平台。 本章将介绍网络编程的概念和 TCP/IP 协议; 介绍了如何使用.NET Framework 进行网络通信;本章还将介绍一些可用于网络编程的控件的使用方法。 11.1 网络基础网络基础 在介绍 c#网络编程之前,首先介绍一下有关网络的基础知识。 11.1.1 网络概述网络概述 计算机网络是整个计算机业发展最为迅速的一个领域, 网络应用程序的需求也在日益扩大。 所谓计算机网络就是通过通信线路互连起来的计算机的集合。 首先它是计
2、算机的一个群体,是由多台计算机组成的。其次这些计算机是通过一定的通信介质互连在一起的。计算机之间的互连是指它们彼此之间能够交换信息。互连通常有两种方法:计算机间通过双绞线、同轴电缆、电话线、光纤等有形通信介质互相连接,或通过激光、微波、地球卫星通信信道等无形介质互连。 在计算机网络产生之初, 每个计算机厂商都有一套自己的网络体系结构的概念, 它们之间互不相容。为此,国际标准化组织(ISO)在 1979 年建立了一个分委会来专门研究一种用于开放系统互联的体系结构(Open Systems Interconnection,简称 OSI) 。 OSI 参考模型分为七层,分别是:物理层,数据链路层,网
3、络层,传输层,会话层,表示层和应用层。 但是实际上, 现在最普及的网络分层方法是TCP/IP协议中规定的五层方法, 整个Internet就是以 TCP/IP 协议为基础建立起来的,下面就来介绍 TCP/IP 协议。 11.1.2 TCP/IP 网络协议网络协议 协议时对等的网络实体间的通信的规则, 可以简单理解为网络上各计算机彼此交流的一种“语言” 。 TCP/IP 参考模型是因特网(Internet)的基础,它分为五层,分别是物理层,数据链路层,网络层、传输层和应用程。通常说的 TCP/IP 是一组协议的总称,TCP/IP 实际上一个协议族,包括 100 多个相互关联的协议,其中 IP(In
4、ternet Protocol,网际协议)是网络层最主要的协议; TCP(Transmission Control Protocol, 传输控制协议)和 UDP (User Datagram Protocol,用户数据报协议)是传输层中最主要的协议。一般认为 IP,TCP,UDP 是最根本的三种协议,是其他协议的基础。 IP 定义了数据按照数据报传输的格式和规则;TCP 提供了可靠的、面向连接的协议;UDP 是不可靠、无连接的协议。 TCP 建立在 IP 之上(这也是 TCP/IP 的由来) ,定义了网络上程序到程序的数据传输格式和规则,提供了数据报传输确认、丢失重发、以及数据报按照发送次序重
5、新装配的机制。TCP 类似于打电话,在开始传输数据之前,必须要建立明确的连接。 UPD 也建立在 IP 之上,但它是一种无连接的协议,两台计算机之间的传输类似于发送短信,消息从一台电脑发送到另一台电脑,两者之间没有明确的连接,UDP 并不保证数据传输过程的完整性和正确性,所以说它是一种不可靠的协议,但相对于 TCP 来说,UDP 有更好的传输效率。 TCP/IP 使用协议栈来工作,栈是所有用来在两台机器间完成一个传输所需要的所有协议的一个集合。栈分为五个层次,每一层都能从相邻的层中接收或者发送数据,每一层都与许多协议相关联。TCP/IP 协议族的最主要协议如表 11-1 所示: 表 11-1
6、TCP/IP 协议族 层次 主要协议 应用层 HTTP、FTP、SMTP、DNS、Telnet 传输层 TCP、UDP、DVP. 网络层 IP、ICMP、ARP、RARP 链路层 Enternet、Arpanet、PDN 物理层 允许任何协议 下面介绍一些常用的协议。 网络层协议管理离散的计算机间的数据传输,用户一般注意不到,如 IP 协议为用户和远程计算机提提供了信息包的传输方法,确保信息包能正确到达目的地;ARP 协议(地址解析协议)的作用是将 IP 地址映射为物理地址;ICMP 协议(Internet 控制消息协议)是用来在两台计算机之间传输时处理错误和控制消息的,比如 ping 就是常
7、用的由 ICMP 实现的网络工具,可以用来判断网络是否通畅。 应用层协议是建立在网络层协议之上,是专门为用户提供应用服务的,一般是可见的。如 FTP 协议 (文件传输协议) , 可以实现从一个系统向另一个系统传输文件; HTTP 协议 (超文本传输协议)可以在 Internet 上进行信息传输时使用。SMTP 协议(简单邮件传输协议)可以实现邮件传输的可靠性和高效性。 11.2 .NET 网络编程组网络编程组件件 11.2.1 .NET 中的网络组件中的网络组件 .NET 类库为我们提供了很多网络组件以方便网络程序的编写,其中最重要的网络组件主要分布在以下几个命名空间中: (1) System
8、.Net System.Net 命名空间为当今很多流行的网络协议提供了一个统一的简单的接口,其中WebClient 提供了简单访问 Internet 资源的方法;WebRequest 和 WebResponse 可以让你使用网络上的资源而不用考虑各种协议内部的细节,此外,这个命名空间里还包含IPAddress 等用户描述网络地址信息的类。 (2)System.Net.Socket 这个命名空间包含的类通常与较低级别的操作有关。其中 Socket 类是一个非常基础且强大的类,可以完成几乎所有的 TCP/IP 的操作,当然我们也可以在 Socket 的基础上构建 TCPClinent、TCPLis
9、tener 等 TCP、UDP 实现。 (3)System.Web System.Web 包含大部分的实现浏览器/服务器通讯结构的类和接口。它的功能主要集中在于 HTTP 协议和 Web 有关的操作上。 (4)System.Web.Service 其中的组件将用于 web 服务, 以 HTTP 协议和 XML 的格式为其他应用程序提供一定的基于 Web 的信息服务。 11.2.2 网络通信中的流网络通信中的流 流的概念已经存在很长时间了。 流是一个用于传输数据的对象, 数据传输一般有两个方向:如果数据从外部源传输到程序中,就是读取流;如果数据从程序传输到外部源,就是写入流。外部源常常是一个文件
10、,但它还可以是网络上资源或者一个指定的管道,也可以是内存中的一片区域。 在这些情况下,.NET 提供了一个 System.IO.Stream 处理对于所有流数据的操作,另外为 了 使 用 方便 , 又 提供 了 System.IO.MemoryStream 来 读 取 内 存 中 的数 据 , 使用System.Net.Socket.NetworkStream 处理网络数据。 对于流的基本操作如表 11-2 所示: 名称 说明 CanRead 是否可读 CanSeek 是否可以直接访问流中的某个位置 CanWrite 是否可写 Length 流的长度 Position 当前访问的位置 Begi
11、nRead 异步读 BeginWrite 异步写 Close 关闭流 EndRead 结束异步读 EndWrite 结束异步写 Flush 把缓冲区的操作一次完成 Read 读 ReadByte 读字节 Seek 定位 Write 写 WriteByte 写字节 NetworkStream 实现了 System.IO.Stream 的机制, 可以直接使用它进行网络数据的读写,它提供以下功能: (1) 一个统一的从网络中读取数据的方法。 (2) 与其他的.NET 流兼容,这样可以很方便移植程序。例如你有一个从 FileStream中读取 XML 数据的程序, 只要稍加修改就可以变为从网络中读取
12、XML 数据的程序。 (3) 提供两种操作方式,同步处理和异步处理,其中异步处理提供了在数据到达时处理的功能,避免在程序中等待数据的传输。 但是 NetworkStream 的操作对象是网络中的资源,所以它不支持随机数据访问,也就是它的 CanSeek 永远是 False,使用它的 Position 属性或者调用 Seek 方法都会返回异常。 11.2.3 网络中的编码和网络中的编码和解码解码 网络中的数据传输经常会遇到编码和解码的操作。 因为网络是一个公共的混合体, 多种平台、多种语言开发的程序都在网络上传输数据,所以网络上存在很多的编码方式:ASCII,UTF7 和 UTF8 等。 这种现
13、状就需要一个可以进行编码方式转换的工具让我们方便的将内码转换为我们系统可以接收的方式, 以正确的获取数据。 .NET 中的 System.Text.Encoding 类提供类这样的功能。 Encoding 类提供类字符串、Unicode 字符集和相应编码的字节数组之间的相互转换。有了这些 Encoding 的方法我们就可以进行内码转换并读取数据了,Encoding 类的主要成员如表 11-3 所示。 表 11-3 Encoding 的主要成员 名称 说明 ASCII 获取 ASCII(7 位)字符集的编码 BigEndianUnicode 获取采用 Big-Endian 字节顺序的 Unico
14、de 格式的编码 CodePage 获取此编码的代码页标识符 Default 获取系统的当前 ANSI 代码页的编码 EncodingName 获取编码的可读说明 Unicode 获取采用 Little-Endian 字节顺序的 Unicode 格式的编码 UFT7 获取 UTF-7 格式的编码 UTF8 获取 UTF-8 格式的编码 Convert 将字节数组从一种编码转换为另一种 GetEncoding 获得一种特殊的编码 GetBytes 将指定的 string 或字符数组的全部或部分内容编码为字节数组。 GetChars 将字节数组解码为字符数组 GetEncoder 为此编码返回一个
15、编码器 GetDecoder 为此编码返回一个解码器 GetString 将指定字节数组解码为字符串 在网络间传输的数据都是以字节数组的形式存在, 如果要把从网络中获得的字节数组转化为 ASCII 字符串,代码如下: 假定 Buffer 为保存网络间传输的数据的字节数组 String content=System.Text.Encoding.ASCII.GetString(Buffer); 如果要将某个 ASCII 字符串在网络间传送,则需要一个相反的过程,要将该字符串转化为 ASCII 字节数组,代码如下: String content=”123456”; Byte sendData=Sys
16、tem.Text.Encoding.ASCII.GetByte(content); 11.3 访问访问 Internet 在网络环境下,我们最感兴趣的两个命名空间是 System.Net 和 System.Net.Sockets。System.Net 通常与较高层的操作有关,比如下载文件,使用 HTTP 协议进行 Web 请求等,而 System.Net.Sockets 通常与较底层的操作有关, 如果要直接使用套接字或者 TCP/IP 一类的协议,这个命名空间是非常有用的。本节我们将介绍使用.NET 访问 Internet 的一些方法。 11.3.1 URI URI 一般是用来描述 Inter
17、net 中的网页或者资源的位置。注意术语 URL(统一资源定位符)在新的技术规范中已不再使用,现在使用的是 URI(统一资源标识符) 。URI 与 URL 的含义大致相同,但 URI 更通用。 在 System 命名空间下,Uri 和 UriBuilder 两个类都用于表示 URI。UriBulider 允许把给定的字符串当做 URI 的组成部分, 从而建立一个 URI, 而 Uri 类允许分析、 组合和比较 URI。 11.3.2 文件下载文件下载 在这里,我们使用 System.Net 下包含的 HttpWebRequest 和 HttpWebResponse 对象来实现文件的下载功能。
18、HttpWebRequest 对象是从 WebRequest 对象继承而来,负责对基于 HTTP 协议的网络资源的请求和访问,HttpWebRequest 类对 WebRequest 中定义的属性和方法提供支持,也对使用户能够直接与使用 HTTP 的服务器交互的附加属性和方法提供支持。 不要使用 HttpWebRequest 构造函数。使用 WebRequest.Create 方法初始化新的 HttpWebRequest 对象。如果统一资源标识符 (URI) 的方案是 http:/ 或 https:/,则 Create 返回 HttpWebRequest 对象。 如果 HttpWenReque
19、st 对象请求的资源可用,则返回 HttpWebResponse 对象。HttpWebResponse 里包含请求资源的相关属性,并可用 HttpWebResponse 对象对请求资源进行操作(下载) 。 下表显示可以通过 HttpWebResponse 类的属性使用的公共 HTTP 标头。 标头标头 属性属性 Content-Encoding 编码方式 Content-Length 资源长度 Content-Type 资源类型 此外, Systen.Net 命名空间下也提供了 WebClient 对象用来专属下载 Internet 资源, 该对象提供了 OpenRead()方法直接以流的形式
20、返回所请求资源。 下面是使用以上几个对象下载网络中文件的示例: 11.4C#套接字套接字 11.4.1 接字基本概念接字基本概念 套接字是通信的基石,是支持 TCP/IP 协议的网络通信的基本操作单元。可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。 套接字存在于通信域中, 通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序) 。各种进程使用这个相同的域互相之间用 Internet 协议簇来进行通信。 套接字可以根据通信性质分类, 这种性质对
21、于用户是可见的。 应用程序一般仅在同一类的套接字间进行通信。 不过只要底层的通信协议允许, 不同类型的套接字间也照样可以通信。String uri=”;/网络资源uri地址 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); /创建HttpWebRequest对象 HttpWebResponse response=(HttpWebResponse) request.GetResponse(); /返回请求资源的相应 int totlesize=(int)response.ContentLength; /获取下载文件总
22、长度 string path = “”;/文件存储路径 FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite); WebClient client = new WebClient(); Stream str = client.OpenRead(uri); /使用WebClient对象返回请求文件的数据流 byte temp = new byte2000; int startbyte = 0; while (true) int m = str.Read(temp, 0, 2000); if
23、 (m = 0) break; fs.Write(temp, 0, m); startbyte += m; fs.Close();/下载完毕,关闭文件流 套接字有两种不同的类型:流套接字和数据报套接字。 11.4.2 套接字套接字字工作原理字工作原理 要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为 ClientSocket,另一个运行于服务器端,我们称之为 ServerSocket。 根据连接启动的方式以及本地套接字要连接的目标, 套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。 所谓服务器监听, 是服务器端套接字并不定位具体的客户端套接
24、字, 而是处于等待连接的状态,实时监控网络状态。 所谓客户端请求, 是指由客户端的套接字提出连接请求, 要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。 所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。 11.4.3 Scket 类类 .NetFrameWork 为 S
25、ocket 通讯提供了 System.Net.Socket 命名空间, 在这个命名空间里面有以下几个常用的重要类分别是: Socket 类 这个低层的类用于管理连接,WebRequest,TcpClient 和 UdpClient 在内部使用这个类。 NetworkStream 类 这个类是从 Stream 派生出来的,它表示来自网络的数据流 TcpClient 类 允许创建和使用 TCP 连接 TcpListener 类 允许监听传入的 TCP 连接请求 UdpClient 类 用于 UDP 客户创建连接(UDP 是另外一种 TCP 协议,但没有得到广泛的使用,主要用于本地网络)。 其中 S
26、ocket 类是包含在 System.Net.Sockets 名字空间中的一个非常重要的类。一个Socket 实例包含了一个本地以及一个远程的终结点,该终结点包含了该 Socket 实例的一些相关信息。 Socket 类支持两种基本模式:同步和异步。其区别在于:在同步模式中,按块传输,对执行网络操作的函数(如 Send 和 Receive)的调用一直等到所有内容传送操作完成后才将控制返回给调用程序。在异步模式中,是按位传输,需要指定发送的开始和结束。同步模式是最常用的模式。 下面我们重点讨论同步模式的 Socket 编程。首先,同步模式的 Socket 编程的基本过程如下: 1 创建一个 So
27、cket 实例对象。 2 将上述实例对象连接到一个具体的终结点(EndPoint) 。 3 连接完毕,就可以和服务器进行通讯:接收并发送信息。 4 通讯完毕,用 ShutDown()方法来禁用 Socket。 5 最后用 Close()方法来关闭 Socket。 知道了以上基本过程,我们就开始进一步实现连接并通讯了。在使用之前,需要首先创建 Socket 对象的实例,这可以通过 Socket 类的构造方法来实现: public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);
28、 其中: addressFamily 参数指定 Socket 使用的寻址方案,比如 AddressFamily.InterNetwork表明为 IP 版本 4 的地址; socketType 参数指定 Socket 的类型,比如 SocketType.Stream 表明连接是基于流套接字的,而 SocketType.Dgram 表示连接是基于数据报套接字的。 protocolType 参数指定 Socket 使用的协议,比如 ProtocolType.Tcp 表明连接协议是运用 TCP 协议的,而 Protocol.Udp 则表明连接协议是运用 UDP 协议的。 在创建了 Socket 实例后
29、,我们就可以通过一个远程主机的终结点和它取得连接,运用的方法就是 Connect()方法: public Connect (EndPoint ep); 该方法只可以被运用在客户端。进行连接后,我们可以运用套接字的 Connected 属性来验证连接是否成功。如果返回的值为 true,则表示连接成功,否则就是失败。 下面的代码就显示了如何创建 Socket 实例并通过终结点与之取得连接的过程: IPHostEntry IPHost = Dns.Resolve(http:/ string aliases = IPHost.Aliases; IPAddress addr = IPHost.Addre
30、ssList; EndPoint ep = new IPEndPoint(addr0,80); Socket sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); sock.Connect(ep); if(sock.Connected) Console.WriteLine(OK); 一旦连接成功,我们就可以运用 Send()和 Receive()方法来进行通讯。 Send()方法的函数原型如下: public int Send (byte buffer, int size, Socke
31、tFlags flags); 其中: 参数 buffer 包含了要发送的数据, 参数 size 表示要发送数据的大小, 而参数 flags 则可以是以下一些值:SocketFlags.None、SocketFlags.DontRoute、SocketFlags.OutOfBnd。 该方法返回的是一个 System.Int32 类型的值,它表明了已发送数据的大小。同时,该方法还有以下几种已被重载了的函数实现: public int Send (byte buffer); public int Send (byte buffer, SocketFlags flags); public int Se
32、nd (byte buffer,int offset, int size, SocketFlags flags); 介绍完 Send()方法,下面是 Receive()方法,其函数原型如下: public int Receive(byte buffer, int size, SocketFlags flags); 其中的参数和 Send()方法的参数类似,在这里就不再赘述。 同样,该方法还有以下一些已被重载了的函数实现: public int Receive (byte buffer); public int Receive (byte buffer, SocketFlags flags);
33、public int Receive (byte buffer,int offset, int size, SocketFlags flags); 在通讯完成后,我们就通过 ShutDown()方法来禁用 Socket,函数原型如下: public void ShutDown(SocketShutdown how); 其中参数 how 表明了禁用的类型,SoketShutdown.Send 表明关闭用于发送的套接字;SoketShutdown.Receive 表明关闭用于接收的套接字;而 SoketShutdown.Both 则表明发送和接收的套接字同时被关闭。 应该注意的是在调用 Close
34、()方法以前必须调用 ShutDown()方法以确保在 Socket关闭之前已发送或接收所有挂起的数据。一旦 ShutDown()调用完毕,就调用 Close()方法来关闭 Socket,其函数原型如下: public void Close(); 该方法强制关闭一个 Socket 连接并释放所有托管资源和非托管资源。该方法在内部其实是调用了方法 Dispose() ,该函数是受保护类型的,其函数原型如下: protected virtual void Dispose(bool disposing); 其中,参数 disposing 为 true 或是 false,如果为 true,则同时释放托
35、管资源和非托管资源;如果为 false,则仅释放非托管资源。因为 Close()方法调用 Dispose()方法时的参数是 true,所以它释放了所有托管资源和非托管资源。 如果以上操作中出现异常, 我们可以使用 SocketException 来抛出 Socket 相关出错信息;当网络发生错误时, Socket 和 Dns 引发 SocketException; 使用 SocketException.ErrorCode 可以获取特定的错误代码, Message 可以获得错误消息。 主要代码如下: try /执行代码 catch(SocketException e) Console.Write
36、Line(“0 error code : 1” , e.Message , e.ErrorCode); return (e.ErrorCode); return 0; 这样,一个 Socket 从创建到连接到通讯最后的关闭的过程就完成了。 【例 11.6】下面看一个完整的实例,client(客户端)向 server(服务器端)发送一段测试字符串,server 接收并显示出来,给予 client 成功响应。 客户端主要代码: static void Main() try int port = 2000; string host = 127.0.0.1; IPAddress ip = IPAddr
37、ess.Parse(host); IPEndPoint ipe = new IPEndPoint(ip, port); /把 ip 和端口转化为 IPEndPoint 实例 Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); /创建一个 Socket 类 Console.WriteLine(Conneting.); c.Connect(ipe); /连接到服务器 string sendStr = hello!This is a socket test; byte bs =
38、 Encoding.ASCII.GetBytes(sendStr); Console.WriteLine(Send Message); c.Send(bs, bs.Length, 0);/发送测试信息 string recvStr = ; byte recvBytes = new byte1024; int bytes; bytes = c.Receive(recvBytes, recvBytes.Length, 0); /从服务器端接受返回信息 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); Console.WriteLin
39、e(Client Get Message:0, recvStr); /显示服务器返回信息 c.Close(); catch (ArgumentNullException e) Console.WriteLine(ArgumentNullException: 0, e); catch (SocketException e) Console.WriteLine(SocketException: 0, e); Console.WriteLine(Press Enter to Exit); Console.ReadLine(); 服务器端主要代码: static void Main() try int
40、 port = 2000; string host = 127.0.0.1; IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = new IPEndPoint(ip, port); Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); /创建一个 Socket 类 s.Bind(ipe);/绑定端口 s.Listen(0);/开始监听 Console.WriteLine(Wait for connect); Sock
41、et temp = s.Accept();/为新建连接创建新的 Socket。 Console.WriteLine(Get a connect); string recvStr = ; byte recvBytes = new byte1024; int bytes; bytes = temp.Receive(recvBytes, recvBytes.Length, 0);/从客户端接受信息 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); Console.WriteLine(Server Get Message:0, recv
42、Str); /把客户端传来的信息显示出来 string sendStr = Ok!Client Send Message Sucessful!; byte bs = Encoding.ASCII.GetBytes(sendStr); temp.Send(bs, bs.Length, 0);/返回客户端成功信息 temp.Close(); s.Close(); catch (ArgumentNullException e) Console.WriteLine(ArgumentNullException: 0, e); catch (SocketException e) Console.Write
43、Line(SocketException: 0, e); Console.WriteLine(Press Enter to Exit); Console.ReadLine(); 11.4.4 Socket 的的同步编程和同步编程和异步编程异步编程 上一节中的 Accept,Receive,Send 方法都有相应的 BeginAccept,EndAccept 等方法与之对应。这是因为 Socket 允许同步或异步的方式进行操作,对于 Socket 得每一个操作也就有同步和异步两个版本。 所谓同步方式,就是发送方发送数据包以后,不等待接受方相应,就这届发送下一个数据包;异步方式就是当发送方发送一个
44、数据包以后,一直等到接收方相应后,才接着发送下一个数据包。 所谓同步 Socket 是指客户机或服务器上执行 Socket 连接,接收或发送操作时,客户机或服务器会中止工作,处于暂停状态。这种方式编程比较简单,对于简单的网络程序来说也确实够用, 但是对于网络操作比较繁重的时候同步操作这种机制显然就不何时了。 这是就需要使用异步通信的机制。 对于服务器端,在同步操作的过程中,当一个 Socket 处于 Listen 状态时,它可以使用Accept 方法来接收远方的连接请求。而在连接请求到来之前,程序是一直处于中止状态的,当请求到来后,Accept 方法将会返回一个与客户端连接的 Socket。
45、同步 Socket 在监听时处于暂停状态,这样无疑减小了网络的负载能力,因为程序中有一个同步 Socket 在监听时,程序就处于暂停,无法再建立一个同步的监听 Socket 或者进行其他操作。对于网络操作负荷比较大的程序来说,使用同步得 Socket 无法满足要求,这时就要使用异步的 Socket 操作了。异步的 Socket 可以在监听的同时进行其他的操作。 这里我们以服务器端的异步 Socket 为例,演示使用异步 Socket 在服务器端建立连接的操作,异步数据发送和接收这里不再累述。 对于服务器端的异步 Socket 来说,它需要在一个方法开始接受网络连接请求;一个回调函数处理连接请求
46、建立连接。 Socket 中使用 BeginAccept 方法接收新的连接请求。BeginAccept 方法有两个参数,第一个是 AsyncCallback 类型的委托, 第二个是 object 类型用来传递状态, 一般用来传递 Socket变量 例如: 11.11.5 5 小结小结 static void Main() int port = 2000; string host = 127.0.0.1; IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = new IPEndPoint(ip, port); Socket s = new
47、Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); /创建一个 Socket 类 s.Bind(ipe);/绑定端口 s.Listen(0);/开始监听 Console.WriteLine(Wait for connect); AsyncCallback callback=new AsyncCallback(GetClient);/为回调函数创建委托 s.BeginAccept(callback,s);/开始异步接收请求 Static GetClient(IAyncResult ar) /回调函数 Socket s=(Socket)ar.AsyncState; Socket client=s.EndRequest(ar);/获取代表客户端的 Socket 本章重点讲述了使用 C#进行网络编程的相关类库、Socket 以及电子邮件的概念。 11.7 11.7 习题与练习习题与练习 1. 思考确定一个 IP 地址属于哪类 IP 地址。 2. 辨析 TCP 和 UDP 协议的区别。 3. 编程和上机调试。 1) 编写一个能够扫描一个网段所有 IP 地址的程序。 2) 使用 TCP/IP 协议编写一个聊天程序,两个机器可以相互发送接收消息。