使用套接字简单的HTTP代理:问题简单、问题、HTTP

2023-09-03 05:33:19 作者:时光从指尖划过

我想学习如何插槽工作在C#。我的想法是编写一个简单的HTTP代理: 这是我的code:

I'm trying to learn how sockets works in C#. My idea was to program a simple http proxy: Here's my code:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        ThreadPool.SetMaxThreads(1000, 500);
        ThreadPool.SetMinThreads(500, 250);

        TcpListener listener = new TcpListener(IPAddress.Any, 8282);
        listener.Start();

        while (true)
        {
            Socket client = listener.AcceptSocket();
            ThreadPool.QueueUserWorkItem(ProcessSocket, client);
        }
    }

    private static readonly string patternHostPort = @"(Host:\s)(\S+)(:)(\d+)";
    private static readonly string patternHost = @"(Host:\s)(\S+)";
    private static Regex regexHostPort = new Regex(patternHostPort);
    private static Regex regexHost = new Regex(patternHost);

    static void ProcessSocket(object request)
    {
        string requestString = string.Empty;
        MemoryStream mStream = new MemoryStream();
        int bytesReceived;
        int bytesSended;
        byte[] buffer;
        byte[] byteOriginalRequest;

        Socket socketClient = (Socket)request;
        Console.WriteLine("Incoming connection: " + socketClient.RemoteEndPoint.ToString());

        buffer = new byte[4096];

        bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
        mStream.Write(buffer, 0, bytesReceived);
        while (socketClient.Available > 0)
        {
            bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
            mStream.Write(buffer, 0, bytesReceived);
        }

        mStream.Close();

        byteOriginalRequest = mStream.ToArray();
        requestString = Encoding.ASCII.GetString(byteOriginalRequest);
        //Console.WriteLine(requestString);

        #region Get requested Host and Port
        string srvHost = string.Empty;
        string srvPort = string.Empty;

        Match matchHostPort = regexHostPort.Match(requestString);
        if (matchHostPort.Success)
        {
            srvHost = matchHostPort.Groups[2].Value;
            srvPort = matchHostPort.Groups[4].Value;
        }
        else
        {
            Match matchHost = regexHost.Match(requestString);
            if (matchHost.Success)
            {
                srvHost = matchHost.Groups[2].Value;
                srvPort = "80";
            }
            else
            {
                Console.WriteLine("Invalid request?");
            }
        }
        #endregion

        Console.WriteLine(string.Format("Request to {0} on port {1}", srvHost, srvPort));

        IPAddress[] ipAddress = Dns.GetHostAddresses(srvHost);
        IPEndPoint endPoint = new IPEndPoint(ipAddress[0], int.Parse(srvPort));

        using (Socket socketProxy = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
        {
            socketProxy.Connect(endPoint);

            bytesSended = socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);

            MemoryStream m2Stream = new MemoryStream();
            bytesReceived = 1;
            while (bytesReceived > 0)
            {
                bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                m2Stream.Write(buffer, 0, bytesReceived);
            }

            m2Stream.Close();
            byte[] finalResponse = m2Stream.ToArray();
            string stringFinalResponse = Encoding.ASCII.GetString(finalResponse);

            bytesSended = socketClient.Send(finalResponse, finalResponse.Length, SocketFlags.None);

            socketProxy.Close();
        }

        socketClient.Close();
    }
}

下面的一些问题:

1)为什么,如果我取代这种

1) Why if I replace this

bytesReceived = 1;
while (bytesReceived > 0)
{
    bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
    m2Stream.Write(buffer, 0, bytesReceived);
}

while (socketProxy.Available > 0)
{
    bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
    m2Stream.Write(buffer, 0, bytesReceived);
}

我只得到一个空白页面(浏览器)?

I only get an empty page (in the browser)?

2)可能与1有关),但...当前code它需要一段时间来加载任何简单的页面(如谷歌),调试我已经发现,问题可能出在socketProxy.Receive ...但我不知道为什么。

2) Maybe is related with 1) but ... with the current code it takes a while to load any simple page (like google), debugging i've found that the problem may be in socketProxy.Receive ... but I don't know why.

3)有

socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);

socketProxy.Send(byteOriginalRequest);

? (更多的参数不是一个有效的答案:)

? (More parameters is not a valid answer :)

4)所有推荐阅读?书,教程,...?

4) Any recommended reading? book, tutorial, ... ?

5)任何其他建议,学习插座?

5) Any other suggestion to learn sockets?

感谢您的时间。 最好的问候。

Thanks for your time. Best regards.

推荐答案

1)Socket.Available返回零,如果没有在缓冲区你把它叫做即时可用的数据。 Socket.Read将阻塞(等待)的数据,如果没有该产品尚未到达。所以,这就是区别。通过调用读,你让等待数据。通过检查可用的,你不让它等待。所以,如果你碰巧看到可用的数据到来之前,这将是零。

1) Socket.Available returns zero if there is no data available in the buffer at the instant you call it. Socket.Read will block (wait) for data to arrive if there is none available yet. So that's the difference. By calling Read, you make it wait for data. By examining Available, you don't make it wait. So if you happen to look at Available before the data arrives, it will be zero.

2)不知道为什么它的速度慢,但你并不需要使用的内存缓冲区要发送回客户端的数据,因为你不研究它。从一个插座只需直接读取和写入到另一个。

2) Not sure why its slow, but you don't need to use a memory buffer for the data you are sending back to the client, because you don't examine it. Just read from one socket and write directly to the other.

3)这两个调用是相同的。

3) The two calls are identical.

至于4)和5)时,CLR套接字API非常接近原始的C API,所以你可以看看上有良好的C套接字编程任何教程或信息,以获得更多的提示,如果C#教程是很难找到

As for 4) and 5), the CLR socket API is very close to the original C API so you can look at any tutorials or info on good socket programming in C to get more tips, if C# tutorials are hard to find.

此外,你应该调用Close之前调用关机。当你调用关闭,插座断开,这意味着另一端将失去连接,并且不能读取任何数据,他们还没有读 - 另一端缓冲区由操作系统被破坏,数据将被丢弃。调用shutdown(SocketShutdown.Send)导致另一端得到零字节时,他们调用Read(他们读过剩余的数据后)。因此,然后调用读,直到它返回零,它告诉你的另一端得到了所有的数据,还要求关闭。然后,你可以调用关闭。

Also, you should call Shutdown before calling Close. When you call Close, the socket is disconnected, which means that the other end loses the connection and cannot read any data that they had not yet read - the buffer on the other end is destroyed by the OS and the data is discarded. Calling Shutdown(SocketShutdown.Send) causes the other end to get zero bytes when they call Read (after they've read the remaining data). So then you call Read until it returns zero, which tells you that the other end has got all the data and has also called Shutdown. Then you can call Close.

最后,当你调用发送最后一次到所有的数据发送回客户端,也可以不发送的所有数据在一杆。所以,你应该循环,并保持发送无论是留下来发送,直到它已全部发送。

Finally, when you call Send for the last time to send all the data back to the client, it may not send all of the data in one shot. So you should loop and keep sending whatever is left to send until it has all been sent.