Как создать полностью Асинхронный прокси (http 1.0 compiant not 1.1) - PullRequest
0 голосов
/ 14 марта 2011

Для школьного проекта я делаю прокси для нескольких клиентов, который должен быть совместим с http 1.0, а не 1.1 (чтобы это было проще). Учитель сказал мне, что лучше сделать его полностью асинхронным. Так что я сделал полностью асинхронный прокси, есть только одна проблема. Это работает только тогда, когда я помещаю в него поток потоков, но это не делает его быстрее, но это единственный способ, позволяющий ему работать. Пожалуйста, помогите мне найти решение, и, может быть, кто-то знает, зачем ему нужен ThreadSep, чтобы он работал?

Учитель видит эту проблему каждый год, и единственное найденное решение - это спящий поток, поэтому учитель не нашел реального решения.

Сначала простой код для формы. Форма имеет кнопку запуска и текстовое поле для просмотра запроса и текстовое поле для просмотра ответов. После формы следует код для прокси. Кстати, в Internet Explorer вы можете переключиться в режим http 1.0, так что это лучший способ для тестирования, также вам нужно заставить браузер прослушивать прокси-сервер (указанный в коде de).

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; }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 14 марта 2011
  1. Вы должны пройти через наименование, так как это усложняет соблюдение вашего кода.Как пример: clientSocket не очень хорошее имя для сокета прослушивания.

  2. Ваша обработка исключений не подходит.По крайней мере, зарегистрируйте исключения.

  3. И вам необходимо проверить, равно ли количество полученных байтов нулю.Это означает, что удаленный сокет закрыл соединение.

  4. Ваш прокси-поток умрет напрямую, поскольку BeginAccept не является операцией блокировки.

  5. Я не понимаю ваш if в OnDataArrived.Зачем проверять, совпадает ли количество байтов с размером буфера?TCP ничего не гарантирует, когда дело доходит до полученных данных.Частично заполненный буфер не означает, что сообщение завершено.Продолжайте создавать буфер, пока количество байтов тела не станет таким, как указано Content-Length.

  6. То же самое относится к OnWebsiteDataArrived.Вы пытаетесь использовать TCP таким образом, для которого он не предназначен.Это не ориентировано на сообщения.Продолжайте строить буфер, как предложено в # 5.

0 голосов
/ 14 марта 2011

С Socket.EndReceive :

Метод EndReceive блокируется до тех пор, пока данные не станут доступны. Если вы используете протокол без установления соединения, EndReceive прочитает первую поставленную в очередь дейтаграмму, доступную во входящем сетевом буфере. Если вы используете протокол с установлением соединения, метод EndReceive будет считывать столько данных, сколько доступно, вплоть до количества байтов, которое вы указали в параметре размера метода BeginReceive.

Я прочитал это, чтобы подразумевать, что (если вы находитесь в медленной сети), он может вернуть размер < SocketData.BUFFER_SIZE в любое время, а не только когда вы получили конец сообщения. Таким образом, задержка, вероятно, добавляет достаточно времени, чтобы единственное время, которое она возвращает < SocketData.BUFFER_SIZE, это когда сообщение завершено.

...