0

学校のプロジェクトでは、1.1 ではなく http 1.0 に準拠する必要があるマルチクライアント プロキシを作成しています (これにより簡単になります)。先生からは、完全に非同期にしたほうがいいと言われました。そこで、完全に非同期のプロキシを作成しました。問題は 1 つだけです。スレッドスリープを入れた場合にのみ機能しますが、これで高速になるわけではありませんが、機能させる唯一の方法です。解決策を見つけるのを手伝ってください。おそらく誰かがそれを機能させるためにスレッドスリープが必要な理由を知っていますか?

教師は毎年この問題に直面しており、見つかった唯一の解決策はスレッドスリープであるため、教師は実際の解決策を見つけていません。

まず、フォームの簡単なコードです。フォームには、開始ボタンと、リクエストを表示するためのテキスト ボックス、および応答を表示するためのテキスト ボックスがあります。フォームの後にプロキシのコードが続きます。ところで、Internet Explorer では http 1.0 モードに切り替えることができるので、これがテストする最良の方法です。また、ブラウザがプロキシサーバー (コードにリストされています) をリッスンするようにする必要があります。

using System;
using System.Windows.Forms;
using System.Threading;

namespace Proxy
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void startProxy_Click(object sender, EventArgs e)
        {
            var proxy = new Proxy(requestView, respondsView);
            var thread = new Thread(new ThreadStart(proxy.StartProxy));
            thread.IsBackground = true;
            thread.Start();
            startProxy.Enabled = false;
        }
    }
}

そして今、問題が存在するプロキシ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace Proxy
{
    class Proxy
    {
        private TextBox requestView;
        private TextBox respondsView;
        private delegate void UpdateLogCallback(string strMessage, TextBox txtView);
        public const int PROXY_PORT = 5008;
        public const int WEB_PROXY_PORT = 80;
        public const int BACKLOG = 20;
        public const int TIMEOUT = 4000;

        public Proxy(TextBox _requestView, TextBox _respondsView)
        {
            requestView = _requestView;
            respondsView = _respondsView;
        }

        public void StartProxy()
        {
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unspecified);
            clientSocket.Bind(new IPEndPoint(IPAddress.Any, PROXY_PORT));
            clientSocket.Listen(BACKLOG);
            clientSocket.BeginAccept(HandleConnection, clientSocket);
        }


        private void HandleConnection(IAsyncResult iar)
        {
            Socket clientSocket = iar.AsyncState as Socket;
            Socket client = clientSocket.EndAccept(iar);
            clientSocket.BeginAccept(HandleConnection, clientSocket);
            SocketData data = new SocketData() { SocketToClient = client };
            client.BeginReceive(data.buffer, 0, SocketData.BUFFER_SIZE, SocketFlags.None, OnDataArrived, data);
        }

        private void OnDataArrived(IAsyncResult iar)
        {
            SocketData socketdata = iar.AsyncState as SocketData;
            int bytesreceived = 0;
            UpdateLogCallback uLC = new UpdateLogCallback(ReceiveMessages);            
            socketdata.SocketToClient.ReceiveTimeout = TIMEOUT;
            try
            {
                bytesreceived = socketdata.SocketToClient.EndReceive(iar);
                if (bytesreceived == SocketData.BUFFER_SIZE)
                {
                    socketdata.sb.Append(ASCIIEncoding.ASCII.GetString(socketdata.buffer, 0, bytesreceived));
                    socketdata.SocketToClient.BeginReceive(socketdata.buffer, 0, SocketData.BUFFER_SIZE, SocketFlags.None, OnDataArrived, socketdata);
                }
                else
                {
                    socketdata.sb.Append(ASCIIEncoding.ASCII.GetString(socketdata.buffer, 0, bytesreceived));
                    string strContent = socketdata.sb.ToString();
                    string[] testing = strContent.Split(' ');
                    if (testing[0] == "CONNECT")
                    {
                        //this is to prevent weird request to microsoft servers(???) also prevents ssl request...                       
                    }
                    else
                    {
                        IPEndPoint ip = new IPEndPoint(Dns.GetHostEntry(GetHostnameFromRequest(strContent)).AddressList[0], WEB_PROXY_PORT);
                        Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unspecified);
                        server.Connect(ip);
                        requestView.Invoke(new UpdateLogCallback(uLC), new object[] { strContent, requestView });
                        server.Send(System.Text.ASCIIEncoding.ASCII.GetBytes(strContent));
                        socketdata.SocketToServer = server;                        
                        server.BeginReceive(socketdata.buffer2, 0, SocketData.BUFFER_SIZE, SocketFlags.None, OnWebsiteDataArrived, socketdata);
                    }
                }
            }
            catch
            {
                socketdata.SocketToClient.Close();
            }
        }

        private void OnWebsiteDataArrived(IAsyncResult iar)
        {
            SocketData socketdata = iar.AsyncState as SocketData;
            int bytesreceived = 0;
            UpdateLogCallback uLC = new UpdateLogCallback(ReceiveMessages);
            socketdata.SocketToServer.ReceiveTimeout = TIMEOUT;

            try
            {
                bytesreceived = socketdata.SocketToServer.EndReceive(iar);
                Thread.Sleep(10);
                if (bytesreceived == SocketData.BUFFER_SIZE)
                {
                    socketdata.sb2.Append(ASCIIEncoding.ASCII.GetString(socketdata.buffer2, 0, bytesreceived));
                    socketdata.SocketToClient.Send(socketdata.buffer2, 0, SocketData.BUFFER_SIZE, SocketFlags.None);
                    socketdata.SocketToServer.BeginReceive(socketdata.buffer2, 0, SocketData.BUFFER_SIZE, SocketFlags.None, OnWebsiteDataArrived, socketdata);
                }
                else
                {
                    socketdata.sb2.Append(ASCIIEncoding.ASCII.GetString(socketdata.buffer2, 0, bytesreceived));
                    respondsView.Invoke(new UpdateLogCallback(uLC), new object[] { socketdata.sb2.ToString(), respondsView });
                    socketdata.SocketToClient.Send(socketdata.buffer2, 0, bytesreceived, SocketFlags.None);
                    socketdata.SocketToClient.Close();
                    socketdata.SocketToServer.Close();
                }
            }
            catch
            {
                socketdata.SocketToClient.Close();
            }
        }

        private static string GetHostnameFromRequest(string strContent)
        {
            string[] host = strContent.Split(new string[] { "\r\n", ": " }, StringSplitOptions.RemoveEmptyEntries);
            int check = Array.IndexOf(host, "Host");
            return host[check + 1];
        }

        public void ReceiveMessages(string receiveMessages, TextBox txtView)
        {
            if (txtView.InvokeRequired)
            {
                UpdateLogCallback uLC = new UpdateLogCallback(ReceiveMessages);
                txtView.Invoke(new UpdateLogCallback(uLC), new object[] { receiveMessages, txtView });
            }
            else
            {
                txtView.AppendText(receiveMessages);
            }
        }

        public class SocketData
        {
            public SocketData()
            {
                this.packetlenght = 0;
            }
            public Socket SocketToClient { get; set; }
            public Socket SocketToServer { get; set; }
            public StringBuilder sb = new StringBuilder();
            public StringBuilder sb2 = new StringBuilder();
            public const int BUFFER_SIZE = 128;
            public byte[] buffer = new byte[BUFFER_SIZE];
            public byte[] buffer2 = new byte[BUFFER_SIZE];
            public int packetlenght { get; set; }
        }
    }
}
4

2 に答える 2

0

Socket.EndReceiveから:

EndReceive メソッドは、データが利用可能になるまでブロックします。コネクションレス プロトコルを使用している場合、EndReceive は、受信ネットワーク バッファーで利用可能な最初のエンキューされたデータグラムを読み取ります。接続指向のプロトコルを使用している場合、EndReceive メソッドは、BeginReceive メソッドのサイズ パラメータで指定したバイト数まで、使用可能なデータをすべて読み取ります。

< SocketData.BUFFER_SIZE(低速のネットワークを使用している場合)メッセージの最後を受信したときだけでなく、いつでもサイズが返される可能性があることを暗示するためにそれを読みました。< SocketData.BUFFER_SIZEしたがって、遅延により、メッセージが完了してから返されるのに十分な時間が追加される可能性があります。

于 2011-03-14T11:35:51.580 に答える
0
  1. コードをたどるのが難しくなるため、命名を検討する必要があります。例として:clientSocketはリッスン ソケットの名前としてはあまり適切ではありません。

  2. あなたの例外処理は良くありません。少なくとも例外をログに記録します。

  3. また、受信したバイト数がゼロかどうかを確認する必要があります。これは、リモート ソケットが接続を閉じたことを示します。

  4. BeginAcceptブロッキング操作ではないため、プロキシスレッドは直接終了します。

  5. ifあなたの がわかりませんOnDataArrived。バイト数がバッファサイズと同じかどうかを確認するのはなぜですか? TCP は、受信データに関しては何も保証しません。部分的に満たされたバッファは、メッセージが完全であることを意味しません。本体のバイト数が指定されたバイト数と同じになるまで、バッファの作成を続けますContent-Length

  6. についても同様ですOnWebsiteDataArrived。意図されていない方法で TCP を使用しようとしています。メッセージ指向ではありません。#5 で提案されているように、バッファーを構築し続けます。

于 2011-03-14T11:37:20.583 に答える