Microsoft .NET 框架提供 Internet 服务的分层的、可扩展的和托管的实现,您可以将这些 Internet 服务快速而轻松地集成到您的应用程序中。您的应用程序可建立在可插接式协议的基础之上以便自动利用新的 Internet 协议,或者它们可以使用 Windows 套接字接口的托管实现来使用套接字级别上的网络。
介绍可插接式协议
Microsoft .NET 框架提供分层的、可扩展的和托管的 Internet 服务实现,您可以将它们快速而轻松地集成到您的应用程序中。System.Net 和 System.Net.Sockets 命名空间中的 Internet 访问类可用于实现基于 Web 和基于 Internet 的应用程序。
Internet 应用程序
Internet 应用程序大体上分为两类:客户端应用程序(请求信息)和服务器应用程序(响应来自客户端的信息请求)。典型的 Internet 客户端-服务器应用程序是万维网 (World Wide Web),在万维网中,人们使用浏览器来访问世界各地的 Web 服务器上存储的文档和其他数据。
应用程序并不限于仅充当其中的一个角色;例如,大家所熟悉的中间层应用程序服务器通过请求其他服务器的数据来响应客户端的请求,在这种情况中,它既作为服务器,也作为客户端。
客户端应用程序通过标识所请求的 Internet 资源以及用于该请求和响应的通讯协议来发出请求。如有必要,客户端还提供完成请求所需的任何附加数据,例如代理位置或身份验证信息(用户名、密码等)。只要构成了请求,就可以将该请求发送到服务器。
标识资源
.NET 框架使用统一资源标识符 (URI) 来标识所请求的 Internet 资源和通讯协议。URI 至少由三个(也可能是四个)片段组成:方案标识符(标识用于请求和响应的通讯协议)、服务器标识符(由域名系统 (DNS) 主机名或 TCP 地址组成,用于唯一标识 Internet 上的服务器)、路径标识符(定位服务器上请求的信息)以及可选的查询字符串(将信息从客户端传送到服务器)。例如,URI“http://www.contoso.com/whatsnew.aspx?date=today”就是由方案标识符“http”、服务器标识符“www.contoso.com”、路径“whatsnew.aspx”和查询字符串“?date=today”组成的。
只要服务器接收到请求并进行了响应,它就将该响应返回到客户端应用程序。响应包括补充信息,例如内容的类型(如原始文本或 XML 数据)。
.NET 框架中的请求和响应
.NET 框架使用特定类来提供通过请求/响应模型访问 Internet 所需的三部分信息:Uri 类(包含您要查找的 Internet 资源的 URI)、WebRequest 类(包含对该资源的请求)以及 WebResponse 类(为传入的响应提供容器)。
客户端应用程序通过将网络资源的 URI 传递到 WebRequest.Create 方法来创建 WebRequest 实例。此静态方法创建特定协议(例如 HTTP)的 WebRequest 实例。返回的 WebRequest 实例提供对属性的访问,这些属性既控制对服务器的请求,又控制对建立请求时发送的数据流的访问。WebRequest 实例上的 GetResponse 方法将来自客户端应用程序的请求发送到在 URI 中标识的服务器。在响应可能被延迟的情况下,可以使用 WebRequest 实例上的 BeginGetResponse 方法异步建立请求,并且可以使用 EndGetResponse 方法在以后返回响应。
GetResponse 和 EndGetResponse 方法返回一个 WebResponse 实例,该实例提供对服务器返回的数据的访问。因为此数据由 GetResponseStream 方法作为流提供给发出请求的应用程序,所以它可以在应用程序中的使用数据流的任何地方使用。
WebRequest 类和 WebResponse 类是可插接式协议的基础——这是一种网络服务的实现,该实现使您可以在开发使用 Internet 资源的应用程序时,无需担心每一资源使用的协议的特定详细信息。用 WebRequest 类注册 WebRequest 的子代类可以管理有关建立与 Internet 资源的实际连接的详细信息。
举例来说,HttpWebRequest 类管理有关使用 HTTP 连接到 Internet 资源的详细信息。默认情况下,当 WebRequest.Create 方法遇到以“http:”或“https:”(HTTP 和安全 HTTP 的协议标识符)开头的 URI 时,返回的 WebRequest 实例可以按照原样使用,或者可以被类型转换为 HttpWebRequest 以访问协议特定的属性。在大多数情况下,WebRequest 实例提供建立请求所必需的所有信息。
可被表示为请求/响应事务的所有协议都可以在 WebRequest 中使用。您可以从 WebRequest 和 WebResponse 导出协议特定的类,然后通过静态 WebRequest.RegisterPrefix 方法注册它们以供应用程序使用。
当要求 Internet 请求的客户端验证时,WebRequest 的 Credentials 属性提供必需的凭据。这些凭据可以是用于基本 HTTP 或简要身份验证的简单名称/密码对,或者是用于 NTLM 或 Kerberos 身份验证的名称/密码/域组。一组凭据可以存储在 NetworkCredentials 实例中,或者多组凭据可以同时存储在 CredentialCache 实例中。CredentialCache 使用该请求的 URI 和服务器支持的身份验证方案来确定哪些凭据将发送到服务器。
通过 WebClient 进行简单请求
对于需要建立用于 Internet 资源的简单请求的应用程序而言,WebClient 类提供将数据上载到 Internet 服务器或从 Internet 服务器下载数据的公共方法。WebClient 依赖 WebRequest 类来提供对 Internet 资源的访问;因此,WebClient 类可以使用任何注册的可插接式协议。
对于不能使用请求/响应模型的应用程序而言,或者对于需要侦听网络并发送请求的应用程序而言,System.Net.Sockets 命名空间提供 TCPClient 类、TCPListener 类和 UDPClient 类。这些类处理使用不同的传输协议建立连接的详细信息,并且作为流向应用程序公开网络连接。
对于熟悉 Windows 套接字接口的开发人员或者需要通过在套接字级别编程所提供的控制的开发人员,他们将发现 System.Net.Sockets 类可以满足他们的需要。System.Net.Sockets 类是从托管到 System.Net 类中本机代码的转换点。在大多数情况下,System.Net.Sockets 类将数据封送到其 Windows 32 位副本中,以及处理任何必需的安全检查。
请求数据
开发在当今的 Internet 分布式操作环境中运行的应用程序要求采用高效、易用的方法检索所有类型资源的数据。可插接式协议令您可以开发使用单个接口检索来自多个 Internet 协议的数据的应用程序。
对于简单请求和响应事务,WebClient 类提供将数据上载到 Internet 服务器或从 Internet 服务器下载数据的最简便的方法。WebClient 提供上载和下载文件、发送和接收流以及将数据缓冲区发送到服务器并接收响应的方法。WebClient 使用 WebRequest 类和 WebResponse 类来建立与 Internet 资源的实际连接,以便注册的所有可插接式协议都可供使用。以下示例请求 Web 页并在流中返回结果。
[C#]
WebClient myClient = new WebClient();
Stream response = myClient.OpenRead("http://www.contoso.com/index.htm");
// The stream data is used here.
response.Close();
需要进行更复杂的事务处理的客户端应用程序使用 WebRequest 类及其子代来请求服务器中的数据。WebRequest 封装连接到服务器、发送请求并接收响应的详细信息。WebRequest 是抽象类,定义可用于使用可插接式协议的所有应用程序的一组属性和方法。WebRequest 的子代(例如 HttpWebRequest)实现由 WebRequest 定义的属性和方法,所采取的方式与基础协议一致。
WebRequest 类通过使用传递到其 Create 方法的 URI 的值来确定要创建的特定派生类实例,创建 WebRequest 子代的协议特定的实例。应用程序通过使用 WebRequest.RegisterPrefix 方法注册子代的构造函数,指示哪一 WebRequest 子代应被用于处理请求。
通过调用 WebRequest 实例上的 GetResponse 方法来建立对 Internet 资源的请求。GetResponse 方法构造来自于 WebRequest 实例的属性的协议特定的请求,建立与服务器的 TCP 或 UDP 套接字连接,并发送该请求。对于将数据发送到服务器的请求,例如 HTTP Post 或 FTP Put 请求,WebRequest.GetRequestStream 方法提供要将数据发送到其中的网络流。
GetResponse 方法返回与 WebRequest 匹配的协议特定 WebResponse,如以下示例所示。
[C#]
WebRequest req = WebRequest.Create("http://www.contoso.com/");
WebResponse resp = req.GetResponse();
WebResponse 类也是抽象类,定义可用于使用可插接式协议的所有应用程序的属性和方法。WebResponse 子代实现这些用于基础协议的属性和方法。例如,HttpWebResponse 类实现用于 HTTP 的 WebResponse 类。
由服务器返回的数据被提供给由 WebResponse.GetResponseStream 返回的流中的应用程序。您可以像使用任何其他流一样使用此流,如以下示例所示。
[C#]
StreamReader sr =
new StreamReader(resp.GetResponseStream(), Encoding.ASCII);
创建 Internet 请求
应用程序通过 WebRequest.Create 方法创建 WebRequest 实例。这是创建子代 WebRequest 实例的静态方法(基于传递到该实例的 URI 方案)。
.NET 框架提供继承自 WebRequest 的 HttpWebRequest 类,以处理对 Internet 的 HTTP 和 HTTPS 请求。在大多数情况下,WebRequest 提供您建立请求所需的所有属性;但是,如有必要,您可以将通过 WebRequest.Create 方法创建的 WebRequest 对象类型转换为 HttpWebRequest,以访问请求的 HTTP 特定的属性。
HttpWebResponse 处理对 Internet 的 HTTP 和 HTTPS 请求的响应。为访问 HttpWebResponse 的 HTTP 特定属性,您需要将 WebResponse 类型转换为 HttpWebResponse。
为处理使用其他 Internet 协议(例如 FTP)的请求,您需要导出 WebRequest 和 WebResponse 的协议特定子代。
使用 Internet 请求和响应类
若要从 Internet 请求数据并读取响应,请使用以下步骤。
如果要访问像 Web 页这样的资源,请通过用要使用的资源的 URI 调用 WebRequest.Create 来创建 WebRequest 实例,如下例所示。
[C#]
WebRequest wReq = WebRequest.Create("http://www.contoso.com/");
注意 .NET 框架为以“http:”、“https:”和“file:”开头的 URI 提供协议特定的 WebRequest 和 WebResponse 子代。若要访问其他协议,必须实现协议特定的 WebRequest 和 WebResponse 子代。
在 WebRequest 实例中设置任何所需的属性值。例如,若要支持身份验证,请将 Credentials 属性设置为 NetworkCredential 类的实例,如下例所示。
[C#]
wReq.Credentials =
new NetworkCredential("username","password");
大多数情况下,WebRequest 实例本身就已足够发送和接收数据。但是,如果需要设置协议特定的属性,则将 WebRequest 实例转换为协议特定的实例。仅当处理 WebRequest 的 WebRequest 子代正确时,此类型转换才有效。例如,若要访问 HttpWebRequest 的 HTTP 特定的属性,请将 WebRequest 转换为 HttpWebRequest。 下面的代码实例显示如何设置 HTTP 特定的 UserAgent 属性。
[C#]
if (wReq is HttpWebRequest)
{
((HttpWebRequest)wReq).UserAgent = ".NET Framework Example Client";
}
若要从 Internet 下载资源,请调用 WebRequest 的 GetResponse 方法。
若要将数据发送或上载到资源,请调用 WebRequest 的 GetRequestStream 方法,并使用结果 Stream 对象编写数据。完成上载后,必须使用 Stream.Close 方法关闭请求流。关闭流后,可以调用 GetResponse 以确保服务器已正确接收到数据。为 WebResponse 实例返回的实际类由所请求的 URI 方案决定。下面的代码实例显示如何使用 GetResponse 方法创建 WebResponse 实例。
[C#]
WebResponse wResp = wReq.GetResponse();
注意 调用 WebResponse 后,必须使用 WebResponse.Close 或 Stream.Close 关闭响应。如果不关闭每个响应,应用程序将用完与服务器的连接,而无法处理其他请求。
使用 WebResponse 实例的 GetResponseStream 方法从网络资源中获取包含响应数据的流。还可以访问 WebResponse 实例的属性或将 WebResponse 实例转换为协议特定的实例以读取协议特定的属性。例如,若要访问 HttpWebResponse 的 HTTP 特定的属性,请将 WebResponse 转换为 HttpWebResponse。下面的代码示例显示如何访问 HTTP 特定的属性和读取响应流。
[C#]
// Read an HTTP-specific property.
if (wResp is HttpWebResponse)
{
DateTime Updated = ((HttpWebResponse)wResp).LastModified;
}
// Get the response stream.
Stream respStream = wResp.GetResponseStream();
// This example uses a StreamReader to read the entire response
// into a string and then writes the string to the console.
StreamReader reader = new StreamReader(respStream, Encoding.ASCII);
String respHTML = reader.ReadToEnd();
Console.WriteLine(respHTML);
// Close the response stream.
respStream.Close();
如果应用程序只需要 WebResponse 中返回的标头信息并忽略任何返回的数据,则不必获取响应流。下面的代码示例显示如何从 Internet 主机返回服务器标头信息。
[C#]
WebRequest wReq = WebRequest.Create("http://www.contoso.com");
WebResponse wResp = wReq.GetResponse();
string server = wResp.Headers["Server"];
读取响应中的数据后,必须使用 Stream.Close 方法关闭所有打开的流或使用 WebResponse.Close 方法关闭响应。
[C#]
wResp.Close();
不必在响应流和 WebResponse 实例上都调用 Close 方法,但这样做并没有什么害处。WebResponse.Close 在关闭响应时调用 Stream.Close。
下面的示例应用程序说明 WebRequest 和 WebResponse 类的用法。
[C#]
using System;
using System.Net;
using System.Text;
using System.IO;
class ClientGet {
public static void Main(string[] args)
{
if (args.Length < 1)
{
showusage();
return;
}
// Get the URI from the command line.
Uri site = new Uri(args[0]);
// Create the request instance.
WebRequest wReq = WebRequest.Create(site);
// Set the HTTP-specific UserAgent property
if (wReq is HttpWebRequest)
{
((HttpWebRequest)wReq).UserAgent =
".NET Framework Example Client";
}
// Get the response instance
WebResponse wResp = wReq.GetResponse();
// Read an HTTP-specific property.
if (wResp is HttpWebResponse)
{
DateTime updated = ((HttpWebResponse)wResp).LastModified;
}
// Get the response stream.
Stream respStream = wResp.GetResponseStream();
// This example uses a StreamReader to read the entire response
// into a string and then writes the string to the console.
StreamReader reader =
new StreamReader(respStream, Encoding.ASCII);
String respHTML = reader.ReadToEnd();
Console.WriteLine(respHTML);
// Close the response and response stream.
wResp.Close();
}
public static void showusage()
{
Console.WriteLine("Attempts to GET a URI.");
Console.WriteLine("\r\nUsage:");
Console.WriteLine(" ClientGet URI");
Console.WriteLine("Example:");
Console.WriteLine(" ClientGet http://www.contoso.com/");
}
}
在网络上使用流
网络资源在 .NET 框架中表示为流。通过对流进行一般处理,.NET 框架提供下列功能:
- 发送和接收 Web 数据的通用方法。无论文件的实际内容是什么(HTML、XML 或其他任何内容),应用程序都将使用 Stream.Write 和 Stream.Read 发送和接收数据。
- 整个框架中流的兼容性。流用于整个 .NET 框架中,此框架具有丰富的结构来处理流。例如,通过只修改初始化流的几行代码,可以修改从 FileStream 中读取 XML 数据的应用程序,使其改为从 NetworkStream 中读取数据。NetworkStream 类和其他流之间的主要区别是:NetworkStream 是不可查找的,CanSeek 属性始终返回 false,而 Seek 和 Position 方法引发 NotSupportedException。
- 当数据到达时处理数据。流在数据从 Internet 下载的过程中提供对数据的访问,而不是强制应用程序等待下载完整个数据集。
System.Net.Sockets 命名空间包含一个 NetworkStream 类,该类实现专门用于 Internet 的 Stream 类。System.Net.Sockets 命名空间中的类使用 NetworkStream 类表示流。
若要使用返回的流向网络发送数据,请在 WebRequest 实例上调用 GetRequestStream。WebRequest 将请求标头发送到服务器,然后您可以通过在返回的流上调用 BeginWrite、EndWrite 或 Write 方法,将数据发送到 Internet 资源。某些协议(如 HTTP)可能要求在发送数据之前设置协议特定的属性。下面的代码示例显示如何为发送数据设置 HTTP 特定的属性。该示例假定变量 sendData 包含要发送的数据,而变量 sendLength 是要发送的数据字节数。
[C#]
HttpWebRequest request =
(HttpWebRequest) WebRequest.Create("http://www.contoso.com/");
request.Method = "POST";
request.ContentLength = sendLength;
try
{
Stream sendStream = request.GetRequestStream();
sendStream.Write(sendData,0,sendLength);
sendStream.Close();
}
catch
{
// Handle errors . . .
}
若要从网络接收数据,请在 WebResponse 实例上调用 GetResponseStream。然后,您可以通过在返回的流上调用 BeginRead、EndRead 或 Read 方法,从 Internet 资源读取数据。
使用来自网络资源的流时,请记住以下几点。
- 由于 NetworkStream 类无法更改流中的位置,因此 CanSeek 属性总是返回 false。Seek 和 Position 方法引发 NotSupportedException。
- 当使用 WebRequest 和 WebResponse 时,通过调用 GetResponseStream 创建的流实例是只读的,通过调用 GetRequestStream 创建的流实例是只写的。
- 使用 StreamReader 类使编码更容易。下面的代码实例使用 StreamReader 从 WebResponse 实例读取用 ASCII 编码的流(此示例不显示如何创建请求)。
- 如果网络资源不可用,则对 GetResponse 的调用可能会阻塞。应考虑利用 BeginGetResponse 和 EndGetResponse 方法使用异步请求。
- 创建对服务器的连接时,对 GetRequestStream 的调用可能会阻塞。应考虑利用 BeginGetRequestStream 和 EndGetRequestStream 方法对流使用异步请求。
[C#]
// Create a response object.
WebResponse response = request.GetResponse();
// Get a readable stream from the server.
StreamReader sr =
new StreamReader(response.GetResponseStream(), Encoding.ASCII);
// Use the stream. Remember when you are through with the stream to close
s