ASP.net Essence Theory Uses Console Application to Create Asp.net Server

Keywords: socket encoding Web Server network

theme outline
Asp.net Application Domain, HttpRunTime
edit time
Newly build 20170925
Serial number Reference material
1 Asp.net Essence Theory
2 C# Advanced Programming (7th Edition)
3 http://blog.csdn.net/sh524555685/article/details/7454244 (Application Domain Interpretation)
4 http://blog.csdn.net/lhc1105/article/details/47815971 (Assemblies added to GAC methods)

Web application, in the final analysis, is a kind of network processing program. The key of network processing program is listening and processing.

listener

Socket is the most original network listener. The following example shows the html page that listens on port 9152 and returns to hello,world.

Socket listener

namespace SocketWeb
{
    public class Socket
    {
        public void run()
        {
            IPAddress address = IPAddress.Loopback;

            IPEndPoint endPoint = new IPEndPoint(address, 9152);

            System.Net.Sockets.Socket socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            socket.Bind(endPoint);

            socket.Listen(10);

            Console.WriteLine("Start listening, port number is _____________{0}", endPoint.Port);

            while (true)
            {
                System.Net.Sockets.Socket client = socket.Accept();
                try
                {

                    Console.WriteLine(client.RemoteEndPoint);

                    byte[] buffer = new byte[4096];

                    int length = client.Receive(buffer, 4096, SocketFlags.None);

                    System.Text.Encoding utf8 = System.Text.Encoding.UTF8;

                    string requestString = utf8.GetString(buffer, 0, length);

                    Console.WriteLine(requestString);

                    string statusLine = "HTTP/1.1 200 OK\r\n";
                    //string statusLine = "200 OK\r\n";

                    byte[] statusLineByte = utf8.GetBytes(statusLine);

                    string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
                        "<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";

                    //string responseBody = @"123456";

                    byte[] responseBodyByte = utf8.GetBytes(responseBody);

                    string responseHead = string.Format("Content-Type:text/html; charset=utf-8\r\nContent-Length:{0}\r\n",responseBodyByte.Length);

                    byte[] responseHeadByte = utf8.GetBytes(responseHead);

                    client.Send(statusLineByte);

                    client.Send(responseHeadByte);

                    //Separation of header and content
                    client.Send(new byte[] { 13,10});

                    client.Send(responseBodyByte);

                    client.Close();

                    if (Console.KeyAvailable)
                    {
                        break;
                    }
                }
                catch (Exception ex)
                {
                    var msg = ex.Message;
                    client.Close();
                }

            }

            socket.Close();
        }
    }
}

Running this run method in main function to listen, and inputting localhost:9152 in browser can return the response:

It is important to note that when sending response content, you must separate the header from the content:

Otherwise, the connection will be terminated:

TCP listener

In order to simplify the listening program of TCP protocol, a TcpListener class is provided. The following code has the same effect as socket program.

namespace Aspnet.TcpListener
{
    public class WebTcp
    {
        public void run()
        {
            IPAddress address = IPAddress.Loopback;

            IPEndPoint endPoint = new IPEndPoint(address, 9152);

            System.Net.Sockets.TcpListener tcpListener = new System.Net.Sockets.TcpListener(endPoint);

            tcpListener.Start();

            Console.WriteLine("Start listening, port number is _____________{0}", endPoint.Port);

            while (true)
            {
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                Console.WriteLine("Connections have been established");

                //Get a network stream
                NetworkStream ns = tcpClient.GetStream();                 

                try
                {

                    byte[] buffer = new byte[4096];

                    int length = ns.Read(buffer,0,4096);

                    System.Text.Encoding utf8 = System.Text.Encoding.UTF8;

                    string requestString = utf8.GetString(buffer, 0, length);

                    Console.WriteLine(requestString);

                    string statusLine = "HTTP/1.1 200 OK\r\n";
                    //string statusLine = "200 OK\r\n";

                    byte[] statusLineByte = utf8.GetBytes(statusLine);

                    string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
                        "<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";


                    byte[] responseBodyByte = utf8.GetBytes(responseBody);

                    string responseHead = string.Format("Content-Type:text/html; charset=utf-8\r\nContent-Length:{0}\r\n", responseBodyByte.Length);

                    byte[] responseHeadByte = utf8.GetBytes(responseHead);


                    ns.Write(statusLineByte,0,statusLineByte.Length);
                    ns.Write(responseHeadByte, 0, responseHeadByte.Length);
                    ns.Write(new byte[] { 13, 10 }, 0, 2);
                    ns.Write(responseBodyByte, 0, responseBodyByte.Length);

                    ns.Close();

                    if (Console.KeyAvailable)
                    {
                        break;
                    }
                }
                catch (Exception ex)
                {
                    var msg = ex.Message;
                    ns.Close();
                }
            }

            tcpListener.Stop();
        }

    }
}

HTTP listener

To further simplify the HTTP protocol listener,. net provides an HttpListener class that provides addresses and listener port numbers in the form of strings.

  public void run()
        {
            string prex = "http://localhost:9152/";
            System.Net.HttpListener httpListener = new System.Net.HttpListener();

            httpListener.Prefixes.Add(prex);
            httpListener.Start();

            Console.WriteLine("Start monitoring.....");

            while (true)
            {
                HttpListenerContext context = httpListener.GetContext();
                Console.WriteLine("Connections have been established");

                try
                {
                    HttpListenerRequest request = context.Request;
                    Console.WriteLine(request.ToString());

                    HttpListenerResponse response = context.Response;   
                    string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
                        "<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";

                    response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(responseBody);
                    response.ContentType = "text/html; charset=utf-8";

                    Stream output = response.OutputStream;
                    StreamWriter streamWriter = new StreamWriter(output);
                    streamWriter.Write(responseBody);

                    streamWriter.Close();
                    if (Console.KeyAvailable)
                    {
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);                  
                }
            }

            httpListener.Stop();
        }

As you can see, the above listening and processing programs are actually together, just sending back some fixed content through the listening program. Next, create your own Web server by creating custom application domains and custom request handling functions.

Custom Web Server

The so-called custom Web server is to reuse the previous http listener and call its own request processing program, completely abandoning the IIS server.

Before you create it, you need to understand a concept first.

Web application domain

In previous technologies of. net, processes were used as separate boundaries, and each process had its own private virtual memory. In. net architecture, applications have a new boundary: application domain. Multiple applications can run in multiple application domains of a process.

So why introduce the application domain?
We know that all. net applications run in a managed environment, but the operating system only provides processes for the program to run, while the process only provides basic memory management, it does not know what is managed code. So managed code, or. Net programs we created, cannot run directly in the operating system process. To enable managed code to run on unmanaged processes, an intermediary is needed, which can run on unmanaged processes and provide a running environment for managed code. This mediator is the Application Domain (abbreviated as App Domain). So our. Net program, whether it's a Windows form, a Web form, a console application, or an assembly, always runs in an App Domain.

If there is only one class library assembly (.dll file), a process cannot be started (it is not an executable file). Therefore, creating a process requires loading an executable assembly (Windows forms, console applications, etc. exe files). When the executable assembly is loaded,. Net creates a new application domain in the current process, called the default application domain. A process will only create a default application domain with the same name as the assembly. The default application domain cannot be uninstalled and coexists with the process it is in.

So why can managed code be run when application domains are introduced? That is, how does the application domain provide a hosted environment?
Simply put, it's because the application domain allows the assembly it loads to access the services provided by. net Runtime. These services include Managed Heap, Garbage Collector, JIT compiler and other. Net underlying mechanisms. These services themselves (which constitute. Net Runtime) are implemented by unmanaged C++.

As you can see, a process can contain multiple application domains, each of which has its own runtime environment.

Create application domains for custom Web servers

A process can contain multiple application domains. We create a console exe program to start the process. We call the default application domain created by the start process the console application domain. We put the functions of listening and data postback in this application domain. In addition, an application domain is created to process requests, assuming that it is called a Web server application domain. Communication between two application domains involves a cross-domain access problem. Classes accessed across domains must be derived from System.MarshalByRefObject.

Request Processing Class

Add an assembly Aspnet.WebServer and define a WebServer class:

namespace Aspnet.WebServer
{
    public class WebServer:System.MarshalByRefObject
    {
        public void ProcessRequest(string page,string query,System.IO.TextWriter writer)
        {

            Console.WriteLine("Web Server application domain ID={0}",AppDomain.CurrentDomain.Id);
            System.Web.Hosting.SimpleWorkerRequest simpleRequest = new System.Web.Hosting.SimpleWorkerRequest(page,query,writer);
            System.Web.HttpRuntime.ProcessRequest(simpleRequest);
        }

    }
}

This class encapsulates a SimpleWorkerRequest object and calls the ProcessRequest method that should run. This is our custom Web handler, which prints out the current application domain ID and transfers it to the runtime processing.

Rewrite HttpLinstener listener

The listener, using the HTTP Linstener above, adds a delegation of the processing program to it and transfers the request processing to the delegation.

  public delegate void ProcessRequestHandler(string page, string query, System.IO.TextWriter writer);

        public ProcessRequestHandler processRequestHandler;

        public void setProcessRequestHandler(ProcessRequestHandler processRequestHandler)
        {
            this.processRequestHandler = processRequestHandler;
        }   

New monitoring procedures:

public void run()
        {
            string prex = "http://localhost:9152/";
            System.Net.HttpListener httpListener = new System.Net.HttpListener();

            httpListener.Prefixes.Add(prex);
            httpListener.Start();

            Console.WriteLine("Console application domain ID={0}",AppDomain.CurrentDomain.Id);
            Console.WriteLine("Start monitoring.....");

            while (true)
            {
                HttpListenerContext context = httpListener.GetContext();
                Console.WriteLine("Connections have been established");

                try
                {
                    HttpListenerRequest request = context.Request;
                    Console.WriteLine(request.ToString());

                    HttpListenerResponse response = context.Response;


                    using (TextWriter streamWriter = new StreamWriter(response.OutputStream))
                    {
                        string path = Path.GetFileName(request.Url.AbsolutePath);

                        StringWriter sw = new StringWriter();

                        this.processRequestHandler(path, request.Url.Query, sw);

                        var code = sw.Encoding;

                        //--Get processing results
                        string content = sw.ToString();                      
                        sw.Close();

                        Console.WriteLine(content);

                        response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(content);
                        response.ContentType = "text/html; charset=utf-8";
                        streamWriter.Write(content);

                        Console.WriteLine("Process OK");

                    }

                    if (Console.KeyAvailable)
                    {
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            httpListener.Stop();
        }

Custom application domain

Now that both the listener and the handler are ready, we need to customize our Web application domain. Create using the CreateApplicationHost method:

namespace Aspnet.SelfAppDomain
{
    public class SelfAppDomain
    {
        public void build()
        {

            System.Type hostType = typeof(WebServer.WebServer);
            WebServer.WebServer sefWebServer = System.Web.Hosting.ApplicationHost.CreateApplicationHost(hostType, "/",
                System.Environment.CurrentDirectory) as WebServer.WebServer;

            Console.WriteLine("CurrentDomain ID:{0}", AppDomain.CurrentDomain.Id);

            WebHttp httpListener = new WebHttp();
            httpListener.setProcessRequestHandler(sefWebServer.ProcessRequest);

            httpListener.run();            
        }
    }
}

In particular, create a new application domain using the Create Application Host, which will reload the hostType as follows:
1) GAC
2) The bin folder in the physical file directory of the website.
Search in order, or you will report that you have not found a file error.

But strangely, I always fail to copy assemblies to bin directory. Therefore, in order to run smoothly, it has to be loaded into GAC. Loading method:
Run the VS2015 command prompt
1) Generating public key by sn-k command

2) Use this public key signature in assembly properties

3) Add to GAC using the gacutil.exe-i command

If everything goes smoothly and the console application runs, we have completed our custom Web server:
Enter in the browser http://localhost:9152 There are indeed two application domains on the console:

Posted by NeverPool on Tue, 21 May 2019 17:48:56 -0700