0

.net ソケットを使用して Web ページをロードする必要があるため、データの接続方法と受信方法を自分で制御できます。

ノート

  • ここでは WebClient と HttpWebRequest を使用することはできません。TcpClient と Socket を使用する必要があります。
  • それら(HttpWebRequest)を使用できれば幸いですが、接続とデータを自分で制御します。
  • 私の本当の目標は、独自の HTTP ルールを使用して、Socket を使用して任意の Web ページをロードすることです。

アップデート

C# HTTP プロキシ サーバーを実行し、WebClient プロキシ アドレスを使用することで、WebClient を使用して接続とデータを自分で制御することができます。 #メンタリス

4

2 に答える 2

1

チャンク転送エンコーディングは、RFCでかなり明確に説明されています。各チャンクは次のものから存在します。

chunk-size[;chunk-extensions]<CRLF>
chunk-data<CRLF>

チャンク サイズが最初に送信されます。これは、期待されるチャンク データのバイト数を指定する 16 進数であり、オプションでチャンク拡張が続き、CRLF または が続き\r\nます。指定されたバイト数を読み取った後、別の CRLF が予想されるため、さらに 2 バイトを読み取る必要があります。

その後、次のチャンクの読み取りを開始できます。chunk-sizeがの場合0、(バッファに追加せずに) さらに 2 つ読み取ることを期待するCRLF'sと、サイズ 0 のチャンクが最後のチャンクを示すため、すべてのデータを受信したことになります。

を使用してチャンクを読み取ることはできないことに注意してくださいReadLine()。応答本文の改行 (つまりchunk-data) は行として認識されるため、チャンク全体が読み取られる前に返される可能性があります。

于 2012-07-26T12:52:55.173 に答える
0

.Netで利用可能なすべての種類のストリームで機能するC#を使用して、チャンク化されたWebページを含む任意のWebページをロードする方法は次のとおりです。

このコードはRFC2616-Section-3.6に基づいています

public class HttpStream : Stream
{
    internal readonly Stream InnerStream;
    private long length;
    private bool canRead;

    internal bool Chunked { get; set; }
    internal int ChunkLength { get; set; }
    internal int ChunkReceivedPosition { get; set; }

    internal HttpStream(Stream innerStream)
    {
        InnerStream = innerStream;
        ChunkLength = -1;
        canRead = true;
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (!canRead)
            return -1;

        var bytesReadInCallSession = 0;

        if (Chunked)
        {
            do
            {
                if (ChunkLength == -1)
                {
                    // read next chunked content size
                    string chunkLengthString = InnerStream.ReadLine(returnLineEndBytes: false);
                    ChunkLength = Convert.ToInt32(chunkLengthString, 16);
                }

                // end of HTTP response-body
                if (ChunkLength == 0)
                {
                    canRead = false;
                    break;
                }

                int toRead = ChunkLength;
                if (count + ChunkReceivedPosition - bytesReadInCallSession < ChunkLength)
                    toRead = count + ChunkReceivedPosition - bytesReadInCallSession;

                // read chunked part
                while (ChunkReceivedPosition < toRead)
                {
                    var bytesRead = InnerStream.Read(buffer, offset + bytesReadInCallSession, toRead - ChunkReceivedPosition);
                    ChunkReceivedPosition += bytesRead;
                    bytesReadInCallSession += bytesRead;
                    Position += bytesRead;
                }

                if (ChunkReceivedPosition == ChunkLength)
                {
                    // force to read next chunk size in next loop
                    ChunkLength = -1;
                    ChunkReceivedPosition = 0;

                    // discard anything until we reach after the first CR LF
                    InnerStream.ReadLine();
                }

                if (bytesReadInCallSession == count)
                    break;
            } while (true);

            if (!canRead)
            {
                do
                {
                    string trailer = InnerStream.ReadLine();
                    if (String.IsNullOrWhiteSpace(trailer))
                        break;

                    // TODO: process trailers
                } while (true);
            }

            return bytesReadInCallSession;
        }
        else
        {
            var countRead = InnerStream.Read(buffer, offset, count);
            Position += countRead;
            return countRead;
        }
    }

    public override void SetLength(long value)
    {
        length = value;
    }

    public override bool CanRead
    {
        get { return canRead; }
    }

    public override long Length
    {
        get { return length; }
    }

    public override long Position
    {
        get;
        set;
    }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override bool CanSeek
    {
        get { throw new NotImplementedException(); }
    }

    public override bool CanWrite
    {
        get { throw new NotImplementedException(); }
    }
}

StreamクラスのExtension-Methodとして高度なReadLineが必要です

public static class Extensions
{
    public static string ReadLine(this Stream stream, bool returnLineEndBytes = true, byte[] lineEndBytes = null)
    {
        // default end line bytes
        if (lineEndBytes == null)
            lineEndBytes = new byte[2] { 13, 10 };

        StringBuilder stringBuilder = new StringBuilder("");
        var buffer = new byte[lineEndBytes.Length];
        var index = 0;

        do
        {
            var byteRead = stream.ReadByte();

            // end of stream break loop
            if (byteRead == -1)
                break;

            stringBuilder.Append((char)byteRead);

            buffer[index] = (byte)byteRead;

            if (index == lineEndBytes.Length - 1 && buffer.SequenceEqual(lineEndBytes))
                break;

            // shift bytes by one to the left
            if (index == lineEndBytes.Length - 1)
                buffer = buffer.Skip(1).Concat(new byte[] { 0 }).ToArray();

            if (index < lineEndBytes.Length - 1)
                index++;

        } while (true);

        if (!returnLineEndBytes)
            stringBuilder = stringBuilder.Remove(stringBuilder.Length - lineEndBytes.Length, lineEndBytes.Length);

        return stringBuilder.ToString();
    }
} 
于 2012-07-26T20:19:13.733 に答える