.NET Framework 4.0_网络编程——请求数据
http://msdn.microsoft.com/en-us/library/1t38832a(v=vs.100).aspx
本文内容
- 请求数据——Internet 服务上传和下载数据
- 创建 Internet 请求——Web、File 和 FTP 请求
- 请求一个 Web 页面并在流中检索
- 如何使用 WebRequest 类发送数据
- 如何检索与 WebRequest 匹配的具体协议的 WebResponse
- 使用网络流
- 作出异步请求
请求数据——Internet 服务上传和下载数据
开发运行在今天网络的分布式操作环境中的应用程序,需要一个从所有类型的资源检索数据的高效的,易于使用的方法。可插拔协议可以让你开发使用单个接口从多个网络协议检索数据的应用程序。
对于简单的请求和响应事务,WebClient 类提供上传和下载数据的最简单方法。WebClient 提供上传和下载文件的方法,发送和接收流,以及向服务器发送数据缓存区,并接收响应。WebClient 使用 WebRequest 和 WebResponse 类对网络资源作出实际的连接,这样可用于任何已注册的可插拔协议。
需要更复杂事务的客户端应用程序应通过 WebRequest 及其派生类从服务器请求数据。WebRequest 封装连接服务器、发送请求和接收响应的细节。WebRequest 类是一个抽象类,定义了一组对所有使用可插入式协议的应用程序可用的属性和方法。WebRequest 的派生类,如 HttpWebRequest的,实现定义在 WebRequest 的属性和方法。
WebRequest 类创建 WebRequest 派生类的特定协议实例,利用传递的 URI 来确定实例的类型。应用程序应该使用哪个 WebRequest 的派生类来处理请求,这是用 WebRequest.RegisterPrefix 方法注册派生类的构造函数。
调用 WebRequest.GetResponse 方法会对网络资源发出请求。GetResponse 方法从 WebRequest 属性构造特定协议的请求,与服务器进行 TCP 或 UDP 套接字连接,并发送请求。对于向服务器发送数据的请求,如 HTTP POST 或 FTP 请求,WebRequest.GetRequestStream 方法提供了一个包含发送数据的网络流。
GetResponse 方法返回一个与 WebRequest 相匹配的特定协议的 WebResponse。
WebResponse 类也是一个抽象类,它定义对所有使用可插入式协议的应用程序的属性和方法。WebResponse 派生类实现其基础协议的属性和方法。例如,HttpWebResponse 类实现对 HTTP 的 WebResponse 类。
服务器返回的数据对应用程序来说是 WebResponse.GetResponseStream 方法以流的方式。你可以使用此流,如下所示。
StreamReader sr =
new StreamReader(resp.GetResponseStream(), Encoding.ASCII);
创建 Internet 请求——Web、File 和 FTP 请求
应用程序通过 WebRequest.Create 方法创建 WebRequest 实例。该方法是静态方法,基于传递的 URI 创建从 WebRequest 派生的类。
NET Framework 提供 HttpWebRequest 类,它派生自 WebRequest,来处理 HTTP 和 HTTPS 请求。在大多数情况下,WebRequest 类提供了你发出请求的所有属性,但是,如果需要的话,你可以把 WebRequest 对象强制类型转换成 HttpWebRequest,以访问请求的 HTTP 属性。类似地,HttpWebResponse 对象来处理 HTTP 和 HTTPS 请求的响应。若访问 HttpWebResponse 对象的属性,需要把 WebResponse 对象强制类型转换成 HttpWebResponse。
.NET Framework 还提供了 FileWebRequest 和 FileWebResponse 类,来处理使用 "file:" 资源的请求。类似地,FtpWebRequest 和 FtpWebResponse 类用户 "ftp:"。
若处理使用应用程序协议的请求,需要实现从 WebRequest 和 WebResponse 派生的特定协议类。
请求一个 Web 页面并在流中检索
下面代码演示如何请求一个 Web 页面,并在流中检索结果。
WebClient myClient = new WebClient();
Stream response = myClient.OpenRead("http://www.contoso.com/index.htm");
// The stream data is used here.
response.Close();
如何使用 WebRequest 类请求数据
下面代码描述从服务器请求资源的步骤。例如,一个 Web 页面或文件。必须用 URI 确定资源。
using System;
using System.IO;
using System.Net;
using System.Text;
namespace Examples.System.Net
{
public class WebRequestGetExample
{
public static void Main ()
{
// Create a request for the URL.
WebRequest request = WebRequest.Create (
"http://www.contoso.com/default.html");
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// Get the response.
WebResponse response = request.GetResponse ();
// Display the status.
Console.WriteLine (((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream ();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader (dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd ();
// Display the content.
Console.WriteLine (responseFromServer);
// Clean up the streams and the response.
reader.Close ();
response.Close ();
}
}
}
说明:
- 用 URI 参数调用 Create 方法创建一个 WebRequest 实例。如,"WebRequest request = WebRequest.Create(http://www.contoso.com/);";
备注:.NET Framework 提供利用 URI,从 WebRequest 和 WebResponse 继承的具体协议类,URI 以 "http:"、"https:''、"ftp:" 和 "file:" 开头的 URI。若使用其他协议访问资源,必须实现从 WebRequest and WebResponse 类继承的具体协议类。
- 在 WebRequest 设置任何你需要的属性值。例如,若启用验证,设置 Credentials 属性。如,"request.Credentials = CredentialCache.DefaultCredentials;"。
多数情况下,WebRequest 类足够接收数据。但是,如果你需要设置具体协议的属性,必须把 WebRequest 强制转换成具体协议的类型。例如,若访问 HttpWebRequest 的属性,应该把 WebRequest 强制转换成 HttpWebRequest。如,设置 HTTP 协议的 UserAgent 属性:"((HttpWebRequest)request).UserAgent = ".NET Framework Example Client";";
- 调用 GetResponse 发送请求给服务器。返回 WebResponse 对象的实际类型由请求的 URI 决定。
接收响应完成后,必须调用 Close 方法。另外,若你从响应对象获得的是流,那么你可以调用 Stream.Close 方法来关闭。若没有关闭,你的应用程序会失去连接,成为不能处理其他请求。
- 你可以访问 WebResponse 的属性,或是把 WebResponse 强制类型转换成具体协议类的实例,来读取具体协议的属性。例如,若访问 HttpWebResponse 的属性,可以把 WebResponse 强制类型为 HttpWebResponse。如,"Console.WriteLine (((HttpWebResponse)response).StatusDescription);";
- 调用 WebResponse.GetResponseStream 方法获得服务器响应的流。如 "Stream dataStream = response.GetResponseStream ();";
- 从响应读取数据后,必须关闭,用 Stream.Close 方法或 WebResponse.Close 方法。没必要同时调用,但是同时调用也没什么伤害。当关闭响应时,WebResponse.Close 方法会调用 Stream.Close 方法。如 "response.Close();"。
如何使用 WebRequest 发送数据
下面代码描述发送数据给服务器的步骤。
using System;
using System.IO;
using System.Net;
using System.Text;
namespace Examples.System.Net
{
public class WebRequestPostExample
{
public static void Main ()
{
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create ("http://www.contoso.com/PostAccepter.aspx ");
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = "This is a test that posts this string to a Web server.";
byte[] byteArray = Encoding.UTF8.GetBytes (postData);
// Set the ContentType property of the WebRequest.
request.ContentType = "application/x-www-form-urlencoded";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream ();
// Write the data to the request stream.
dataStream.Write (byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close ();
// Get the response.
WebResponse response = request.GetResponse ();
// Display the status.
Console.WriteLine (((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream ();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader (dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd ();
// Display the content.
Console.WriteLine (responseFromServer);
// Clean up the streams.
reader.Close ();
dataStream.Close ();
response.Close ();
}
}
}
说明:
- 用带 URI 参数的 Create 方法 WebRequest 实例。URI 是接收数据的网络资源。如 "WebRequest request = WebRequest.Create("http://www.contoso.com/");";
- 在 WebRequest 设置任何你需要的属性值。例如,若启用验证,设置 Credentials 属性。
- 指定协议方法,如 HTTP POST 方法。如 "request.Method = "POST";";
- 设置 ContentLength 属性。如 "request.ContentLength = byteArray.Length;";
- 设置 ContentType 属性。如 "request.ContentType = "application/x-www-form-urlencoded";";
- 调用 GetRequestStream 方法获得流。如 "Stream dataStream = request.GetRequestStream ();";
- 向返回方法的流对象写入数据。如 "dataStream.Write (byteArray, 0, byteArray.Length);";
- 调用 Stream.Close 方法关闭请求的流。如 "dataStream.Close ();";
- 调用 GetResponse 向服务器发送请求。该方法返回一个包含服务器响应的对象。返回 WebResponse 对象的类型由 URI 决定。
- 你可以访问 WebResponse 的属性,或是把 WebResponse 强制类型转换成具体协议类的实例,来读取具体协议的属性。如 "Console.WriteLine (((HttpWebResponse)response).StatusDescription);";
调用 WebResponse 的 GetResponseStream 方法获得包含服务器发送的响应数据。如 "Stream data = response.GetResponseStream();";
- 从响应读取数据后,必须关闭,用 Stream.Close 方法或 WebResponse.Close 方法。没必要同时调用,但是同时调用也没什么伤害。当关闭响应时,WebResponse.Close 方法会调用 Stream.Close 方法。如 "response.Close();"。
如何检索与 WebRequest 匹配的具体协议的 WebResponse
下面演示检索与 WebRequest 匹配的具体协议的 WebResponse。
WebRequest req = WebRequest.Create("http://www.contoso.com/");
WebResponse resp = req.GetResponse();
使用网络流
在 .NET Framework 中,网络资源表示为流。通过看作流,.NET Framework 提供如下功能:
- 发送和接收 Web 数据的一般方法。无论文件的实际内容是什么——HTML、XML 或其他——你的应用程序可以使用 Stream.Write 和 Stream.Read 发送和接收数据。.
- 与 .NET Framework 的流兼容。整个 .NET Framework 都使用流,它具有完善的基础结构。例如,你的应用程序,可以把从 FileStream 读取 XML 数据,改成从 NetworkStream 读取数据,仅仅需要改几行代码而已。NetworkStream 和其他流的主要区别是,NetworkStream 是不能寻址(seek)的,CanSeek 属性总是返回 false,并且 Seek 和 Position 方法会抛出 NotSupportedException 异常。
- 数据到达时处理。流提供当从网络到达时访问数据,而不用迫使应用程序等到整个数据下载完成。
System.Net.Sockets 命名空间包含一个专门用于网络资源,实现的 Stream 的 NetworkStream 类。System.Net.Sockets 命名空间中的类使用 NetworkStream 类的代表流。
调用 WebRequest.GetRequestStream 方法,利用返回的流,向网络发送数据。WebRequest 将向服务器发送请求头,之后,你可以调用 BeginWrite、EndWrite 或 Write 方法向网络资源发送数据。某些协议,如 HTTP,可能会要求在发送数据前设置特性协议的属性。下面代码演示如何为发送数据设置 HTTP 协议的属性。假设,变量 sendData 包含要发送的数据,sendLength 变量是要发送的数据的字节数。
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, or Read 方法从网络资源读取数据。
当在网络资源中使用流时,记住如下几点:
- CanSeek 属性总是放回 false,因为 NetworkStream 类不能在流中改变位置。Seek 和 Position 方法会抛出 NotSupportedException 异常。
- 当使用 WebRequest 和 WebResponse,GetResponseStream 创建的流实例是只读的,GetRequestStream 创建的流实例是只写的。
- 使用 StreamReader 类很容易编码。下面代码使用 StreamReader 从 WebResponse 中读取 ASCII 编码的流。
- 如果网络资源不可用,那么 GetResponse 的调用会阻塞。可以使用 BeginGetResponse 和 EndGetResponse 的异步请求。
- 当创建与服务器的连接时,GetRequestStream 的调用会阻塞。可以使用 BeginGetRequestStream 和 EndGetRequestStream 的异步请求。
// 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 it.
sr.Close();
作出异步请求
System.Net 中的类使用 .NET Framework 标准的异步编程模型来异步访问网络资源。WebRequest 类的 BeginGetResponse 和 EndGetResponse 方法开始和完成网络资源的异步请求。
备注:
在异步回调方法中使用同步调用会导致性能损失。WebRequest 及其派生类作出的 Internet 请求必须使用 Stream.BeginRead 来读取 WebResponse.GetResponseStream 方法返回的流。
下面代码演示如何用 WebRequest 类使用异步调用。示例控制台应用程序,从命令行获得 URI,之后,随着从网络接收到数据,输出数据到终端。
程序定义了它自己使用的两个类:RequestState 类,通过异步调用传递数据;ClientGetAsync 类,实现对一个网络资源的异步请求。
RequestState 类保存请求状态。它包含 WebRequest 和 Stream 实例,包含当前请求的资源和响应的流,以及缓冲区,包含当前从网络资源接收的数据,还有一个 StringBuilder,它包含完整的响应。当 AsyncCallback 方法与 WebRequest.BeginGetResponse 注册时,RequestStateis 作为参数来传递。
ClientGetAsync 类实现对网络资源的异步请求,并向控制台输出响应。它包含的方法和属性如下所示:
- allDone 属性包含 ManualResetEvent 类的一个实例,指示请求完成的标识。
- Main() 方法读取命令行,开始对指定网络资源的请求。它创建 WebRequest 对象 wreq 和 RequestState 对象 rs,调用 BeginGetResponse 开始处理请求,之后调用 allDone.WaitOne() 方法,应用程序将退出,直到完成回调。从网络资源读取响应后,Main() 向控制台输出,应用程序结束。
- showusage() 方法向控制台显示该示例的用法。
- RespCallBack() 方法实现对网络资源的异步回调方法。它创建包含响应的 WebResponse 实例,获得响应流,并开始从流中异步读取数据。
- ReadCallBack() 方法实现对读取响应流的异步回调方法。它把从网络资源接收的数据传输到 RequestState 实例的 ResponseData 属性,再开始另一个异步读取,直到没有数据返回为止。一旦读取所有数据,ReadCallBack() 方法会关闭响应流,并调用 allDone.Set() 方法只是整个响应在 ResponseData 里。
备注
严格来说,所有网络流都要关闭。若你不关闭每个请求和响应流,那么应用程序将失去与服务器的连接,就不能处理其他请求。
using System;
using System.Net;
using System.Threading;
using System.Text;
using System.IO;
// The RequestState class passes data across async calls.
public class RequestState
{
const int BufferSize = 1024;
public StringBuilder RequestData;
public byte[] BufferRead;
public WebRequest Request;
public Stream ResponseStream;
// Create Decoder for appropriate enconding type.
public Decoder StreamDecode = Encoding.UTF8.GetDecoder();
public RequestState()
{
BufferRead = new byte[BufferSize];
RequestData = new StringBuilder(String.Empty);
Request = null;
ResponseStream = null;
}
}
// ClientGetAsync issues the async request.
class ClientGetAsync
{
public static ManualResetEvent allDone = new ManualResetEvent(false);
const int BUFFER_SIZE = 1024;
public static void Main(string[] args)
{
if (args.Length < 1)
{
showusage();
return;
}
// Get the URI from the command line.
Uri httpSite = new Uri(args[0]);
// Create the request object.
WebRequest wreq = WebRequest.Create(httpSite);
// Create the state object.
RequestState rs = new RequestState();
// Put the request into the state object so it can be passed around.
rs.Request = wreq;
// Issue the async request.
IAsyncResult r = (IAsyncResult) wreq.BeginGetResponse(
new AsyncCallback(RespCallback), rs);
// Wait until the ManualResetEvent is set so that the application
// does not exit until after the callback is called.
allDone.WaitOne();
Console.WriteLine(rs.RequestData.ToString());
}
public static void showusage() {
Console.WriteLine("Attempts to GET a URL");
Console.WriteLine("\r\nUsage:");
Console.WriteLine(" ClientGetAsync URL");
Console.WriteLine(" Example:");
Console.WriteLine(" ClientGetAsync http://www.contoso.com/");
}
private static void RespCallback(IAsyncResult ar)
{
// Get the RequestState object from the async result.
RequestState rs = (RequestState) ar.AsyncState;
// Get the WebRequest from RequestState.
WebRequest req = rs.Request;
// Call EndGetResponse, which produces the WebResponse object
// that came from the request issued above.
WebResponse resp = req.EndGetResponse(ar);
// Start reading data from the response stream.
Stream ResponseStream = resp.GetResponseStream();
// Store the response stream in RequestState to read
// the stream asynchronously.
rs.ResponseStream = ResponseStream;
// Pass rs.BufferRead to BeginRead. Read data into rs.BufferRead
IAsyncResult iarRead = ResponseStream.BeginRead(rs.BufferRead, 0,
BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
}
private static void ReadCallBack(IAsyncResult asyncResult)
{
// Get the RequestState object from AsyncResult.
RequestState rs = (RequestState)asyncResult.AsyncState;
// Retrieve the ResponseStream that was set in RespCallback.
Stream responseStream = rs.ResponseStream;
// Read rs.BufferRead to verify that it contains data.
int read = responseStream.EndRead( asyncResult );
if (read > 0)
{
// Prepare a Char array buffer for converting to Unicode.
Char[] charBuffer = new Char[BUFFER_SIZE];
// Convert byte stream to Char array and then to String.
// len contains the number of characters converted to Unicode.
int len =
rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);
String str = new String(charBuffer, 0, len);
// Append the recently read data to the RequestData stringbuilder
// object contained in RequestState.
rs.RequestData.Append(
Encoding.ASCII.GetString(rs.BufferRead, 0, read));
// Continue reading data until
// responseStream.EndRead returns –1.
IAsyncResult ar = responseStream.BeginRead(
rs.BufferRead, 0, BUFFER_SIZE,
new AsyncCallback(ReadCallBack), rs);
}
else
{
if(rs.RequestData.Length>0)
{
// Display data to the console.
string strContent;
strContent = rs.RequestData.ToString();
}
// Close down the response stream.
responseStream.Close();
// Set the ManualResetEvent so the main thread can exit.
allDone.Set();
}
return;
}
}