0

对于一个学校项目,我正在制作一个必须符合 http 1.0 而不是 1.1 的多客户端代理(这样更容易)。老师告诉我最好让它完全异步。所以我做了一个完全异步的代理,只有一个问题。它仅在我将 threadleep 放入其中时才有效,但这并不能使它更快,但这是让它工作的唯一方法。请帮我找到一个解决方案,也许有人知道为什么它需要线程睡眠才能让它工作?

老师每年都会看到这个问题,唯一找到的解决方案就是threadsleep,所以老师还没有找到真正的解决方案。

首先是表单的简单代码。该表单有一个开始按钮和一个用于查看请求的文本框和一个用于查看响应的文本框。在表单之后是代理的代码。顺便说一句,在 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; }
        }
    }
}
4

2 回答 2

0
  1. 您应该仔细命名,因为这会使您的代码难以遵循。举个例子:clientSocket对于监听套接字来说,这不是一个很好的名字。

  2. 你的异常处理不好。至少记录异常。

  3. 并且您需要检查接收到的字节数是否为零。它表示远程套接字已关闭连接。

  4. 您的代理线程将直接死亡,因为BeginAccept不是阻塞操作。

  5. 我不明白你ifOnDataArrived。为什么要检查字节数是否与缓冲区大小相同?对于接收到的数据,TCP 不做任何保证。部分填充的缓冲区并不意味着消息已完成。继续构建缓冲区,直到正文字节数与指定的相同Content-Length

  6. 也是如此OnWebsiteDataArrived。您正在尝试以不适合的方式使用 TCP。它不是面向消息的。按照#5 中的建议继续构建缓冲区。

于 2011-03-14T11:37:20.583 回答
0

Socket.EndReceive

EndReceive 方法将阻塞,直到数据可用。如果您使用的是无连接协议,EndReceive 将读取传入网络缓冲区中可用的第一个入队数据报。如果您使用的是面向连接的协议,则 EndReceive 方法将读取尽可能多的数据,直到您在 BeginReceive 方法的 size 参数中指定的字节数。

我读到这意味着(如果您的网络速度较慢),它可能< SocketData.BUFFER_SIZE随时返回一个大小,而不仅仅是在您收到消息结尾时。所以延迟可能会增加足够的时间,它返回的唯一时间< SocketData.BUFFER_SIZE是消息完成后。

于 2011-03-14T11:35:51.580 回答