从打开的HTTP流中读取数据

19

我正在尝试使用.NET WebRequest/WebResponse类来访问Twitter的流API,地址为"http://stream.twitter.com/spritzer.json"

我需要能够打开连接并从打开的连接中逐步读取数据。

目前,当我调用WebRequest.GetResponse方法时,它会阻塞直到整个响应被下载。我知道有一个BeginGetResponse方法,但这只会在后台线程上执行相同的操作。我需要在下载仍在进行中时访问响应流。但是,使用这些类似乎不可能做到这一点。

Twitter文档中针对此问题有一个具体的评论:

“请注意,一些HTTP客户端库只有在服务器关闭连接之后才返回响应正文。这些客户端无法用于访问Streaming API。您必须使用会逐步返回响应数据的HTTP客户端。大多数强大的HTTP客户端库都将提供此功能。例如,Apache HttpClient将处理此用例。”

他们指向了Appache HttpClient,但这并不能帮助我,因为我需要使用.NET。

有什么想法可以在WebRequest/WebResponse中实现这一点,还是我必须使用更低级别的网络类?也许还有其他库可以让我做到这一点?

谢谢。 Allen

4个回答

17

我最终使用了TcpClient,效果很好。不过如果能使用WebRequest/WebResponse实现这个功能的话还是很感兴趣的,以下是我的代码,如果有人感兴趣的话:

using (TcpClient client = new TcpClient())
{

    string requestString = "GET /spritzer.json HTTP/1.1\r\n";
    requestString += "Authorization: " + token + "\r\n";
    requestString += "Host: stream.twitter.com\r\n";
    requestString += "Connection: keep-alive\r\n";
    requestString += "\r\n";

    client.Connect("stream.twitter.com", 80);

    using (NetworkStream stream = client.GetStream())
    {
        // Send the request.
        StreamWriter writer = new StreamWriter(stream);
        writer.Write(requestString);
        writer.Flush();

        // Process the response.
        StreamReader rdr = new StreamReader(stream);

        while (!rdr.EndOfStream)
        {
            Console.WriteLine(rdr.ReadLine());
        }
    }
}

4
HTTP 是一种基于行的协议。你需要用 \r\n 来分隔命令。 - Viet
client.GetStream() 返回的流实际上是从网络堆栈中流式传输还是只是缓存的内存流? - Agile Jedi

13

BeginGetResponse 是你需要的方法,它允许你逐步读取响应流:

class Program
{
    static void Main(string[] args)
    {
        WebRequest request = WebRequest.Create("http://stream.twitter.com/spritzer.json");
        request.Credentials = new NetworkCredential("username", "password");
        request.BeginGetResponse(ar => 
        {
            var req = (WebRequest)ar.AsyncState;
            // TODO: Add exception handling: EndGetResponse could throw
            using (var response = req.EndGetResponse(ar))
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                // This loop goes as long as twitter is streaming
                while (!reader.EndOfStream)
                {
                    Console.WriteLine(reader.ReadLine());
                }
            }
        }, request);

        // Press Enter to stop program
        Console.ReadLine();
    }
}

如果您更喜欢使用WebClient(个人认为比WebRequest更好用):

using (var client = new WebClient())
{
    client.Credentials = new NetworkCredential("username", "password");
    client.OpenReadCompleted += (sender, e) =>
    {
        using (var reader = new StreamReader(e.Result))
        {
            while (!reader.EndOfStream)
            {
                Console.WriteLine(reader.ReadLine());
            }
        }
    };
    client.OpenReadAsync(new Uri("http://stream.twitter.com/spritzer.json"));
}
Console.ReadLine();

我没有尝试过这个,因为我认为req.EndGetResponse会像req.GetResponse一样阻塞,让我面临同样的问题。不过我会试一试。 - user108687
我尝试过这个,但没有成功。由 .GetResponseStream() 返回的流是一个 MemoryStream,其中包含响应的全部内容。 - Jeb
@Darin Dimitrov,如果在数据流中间有长时间间隔,您如何处理超时? - Brian Behm
感谢WebClient示例,当连接到Twitter过滤流端点https://api.twitter.com/2/tweets/search/stream时,它对我很有用。我只需要将Credentials行替换为client.Headers[HttpRequestHeader.Authorization] = $"Bearer {settings.Twitter.BearerToken}";,当然还有Uri部分。 - Jan

1

我认为现代的做法在这里:

var client = new HttpClient();
using var stream = await client.GetStreamAsync("http://stream.twitter.com/spritzer.json");

0
你试过WebRequest.BeginGetRequestStream()吗?
或者像这样:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (http://www.twitter.com );
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream()); 

string str = reader.ReadLine();
while(str != null)
{
   Console.WriteLine(str);
   str = reader.ReadLine();
}

我无法使用 WebRequest.BeginGetRequestStream,因为我并不是在尝试异步向服务器写入数据。此外,在您的示例中,您调用了 GetResponse。在此时,WebRequest 正在阻塞,同时下载服务器响应。但连接从未关闭,因为它是一个有效无限的数据流。 - user108687

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接