网络知识 - 简易的自定义Web服务器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络知识 - 简易的自定义Web服务器相关的知识,希望对你有一定的参考价值。

简易的自定义Web服务器

基于浏览器向服务端发起请求

两台主机各自的进程之间相互通信,需要协议、IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为端点(EndPoint),在网络编程的世界里,.NET提供了Socket(套接字)类,此类处于传输层之中,Socket使开发人员可以以编程的方式侦听远程主机向本机发送的数据,并对到达传输层的数据包做出处理,同时它还可以向远程发送数据包。也即,Socket用于处理传输的数据。

using System.Net;
using System.Net.Sockets;

namespace ConsoleHttp
{
    class Program
    {
        static void Main( string[] args )
        {
            //本机IP
            IPAddress address = IPAddress.Loopback;
            //本程序的IP和端口(端点)
            IPEndPoint ipPoint = new IPEndPoint( address, 49155 );
            //ipv4
            var netWork = AddressFamily.InterNetwork;
            //创建Socket对象
            Socket socket = new Socket( netWork, SocketType.Stream, ProtocolType.Tcp );
            //将Socket绑定到端点
            socket.Bind( ipPoint );
            socket.Listen( 100 );//侦听请求的队列的最大长度为100
            while (true)
            {
                Console.WriteLine( $"已开启侦听,\\n本机端点为: { ipPoint } \\n正在等待远程主机的请求……" );

                //接收……

                Socket clientSocket = socket.Accept( );//阻塞线程直到至少有一台远程主机发送的数据包被socket接收     
                byte[] dataBuffer = new byte[1024];//数据存储区,最大存储1M的数据
                int len = clientSocket.Receive( dataBuffer, 1024SocketFlags.None );//将接收的数据存入存储区,返回数据的字节长度
                string DataStr = Encoding.UTF8.GetString( dataBuffer, 0, len );//将字节转换为字符串

                Console.WriteLine( $"远程主机端点:{clientSocket.RemoteEndPoint}" );//输出远程主机的端点
                Console.WriteLine( $"数据字节长度:{len}" ); //输出接收的数据的字节长度
                Console.WriteLine( $"请求数据:\\n {DataStr}" ); //输出接收的数据

                //响应……
                string HttpDataLine = "HTTP/1.1 200 OK\\r\\n"//报文状态行
                string HttpBody = "<html><head><title>Default Page</title></head><body><p style=\'font:bold;font-size:24pt\'>寂静的春天</p></body></html>"//报文主体
                string HttpHeader = $"Content-Type: text/html; charset=UTf-8\\n Content-Length: {HttpBody.Length}\\n";


                byte[] HttpDataLineByte = Encoding.UTF8.GetBytes( HttpDataLine );
                byte[] HttpBodyByte = Encoding.UTF8.GetBytes( HttpBody );
                byte[] HttpHeaderByte = Encoding.UTF8.GetBytes( HttpHeader );
                byte[] HttpNullLineByte = new byte[] { 1310 };

                clientSocket.Send( HttpDataLineByte );
                clientSocket.Send( HttpHeaderByte );
                clientSocket.Send( HttpNullLineByte );
                clientSocket.Send( HttpBodyByte );

                //断开连接
                clientSocket.Close( );              
            }
        }
    }
}

在浏览器输入端点进行访问,因为浏览器实已经实现了Http协议,浏览器处于应用层,封装好请求后会往下传递给传输层,封装TCP端口再传递给网络层直到请求发送至服务端,所以可以直接看到服务端返回的结果:

TcpListener封装了Socket,所以也可以使用TcpListener来监听请求 

using System.Net;
using System.Net.Sockets;

namespace ConsoleHttp
{
    class Program
    {
        static void Main( string[] args )
        {
            //本机IP
            IPAddress address = IPAddress.Loopback;
            //本程序的IP和端口(端点)
            IPEndPoint ipPoint = new IPEndPoint( address, 49155 );
            //ipv4
            var netWork = AddressFamily.InterNetwork;
            //创建Tcp监听
            TcpListener tcp = new TcpListener( ipPoint );
            tcp.Start( );
          
            while (true)
            {
                Console.WriteLine( $"已开启侦听,\\n本机端点为: { ipPoint } \\n正在等待远程主机的请求……" );
                //接收……
                TcpClient clientTcp = tcp.AcceptTcpClient( );
                if(clientTcp.Connected)
                {
                    Console.WriteLine( "连接已经建立……" );
                    NetworkStream networkStream = clientTcp.GetStream( ); //此类可自动从Socket中读取远程主机发起的请求数据,也可以输出数据

                    byte[] dataBuffer = new byte[1024];//数据存储区,最大存储1M的数据
                    int len = networkStream.Read(dataBuffer,0,1024);//将接收的数据存入存储区,返回数据的字节长度
                    string DataStr = Encoding.UTF8.GetString( dataBuffer, 0, len );//将字节转换为字符串

                    Console.WriteLine( $"远程主机端点:{clientTcp.Client.RemoteEndPoint}" );//输出远程主机的端点
                    Console.WriteLine( $"数据字节长度:{len}" ); //输出接收的数据的字节长度
                    Console.WriteLine( $"请求数据:\\n {DataStr}" ); //输出接收的数据

                    //响应……
                    string HttpDataLine = "HTTP/1.1 200 OK\\r\\n"; //报文状态行
                    string HttpBody = "<html><head><title>Default Page</title></head><body><p style=\'font:bold;font-size:24pt\'>寂静的春天</p></body></html>"; //报文主体
                    string HttpHeader = $"Content-Type: text/html; charset=UTf-8\\n Content-Length: {HttpBody.Length}\\n";//报头

                    byte[] HttpDataLineByte = Encoding.UTF8.GetBytes( HttpDataLine );
                    byte[] HttpBodyByte = Encoding.UTF8.GetBytes( HttpBody );
                    byte[] HttpHeaderByte = Encoding.UTF8.GetBytes( HttpHeader );
                    byte[] HttpNullLineByte = new byte[] { 13, 10 };

                    networkStream.Write( HttpDataLineByte, 0, HttpDataLineByte.Length );
                    networkStream.Write( HttpHeaderByte, 0, HttpHeaderByte.Length );
                    networkStream.Write( HttpNullLineByte, 0, HttpNullLineByte.Length );
                    networkStream.Write( HttpBodyByte, 0, HttpBodyByte.Length );
                }
                //断开连接
                clientTcp.Close( );              
            }
        }
    }
}
View Code

 

基于windows窗体实现双方发送即时通信 

分别创建两个windows窗体项目,命名为TCPServer和TCPClient。两个项目的窗体控件的名称是一样的,如下:

服务端通过TcpListener开启监听,然后通过开启新的线程并使用TcpListener的AcceptTcpClient方法去监听客户端的请求,而客户端则开启新线程并通过TcpClient发起远程连接请求。这样双方就可以建立一个连接。接着,服务端的AcceptTcpClient方法会阻塞线程直到接受到一个请求为止,此时它会返回一个NetworkStream实例,此类提供了读取远程数据、发送数据的方法,此后,双方的互动都是通过这个唯一的NetworkStream实例的方法(Read、Write)来完成,发送数据和接收数据时都使用新线程来处理,并且应将发送数据和接收数据的逻辑都放入try块,这样一旦互动过程出现异常则可以关闭当前的Tcp连接、清空NetworkStream资源,然后服务端重新开启新线程继续监听客户端的连接请求,而客户端则重新发送远程连接的请求即可。

服务端源码

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

namespace TCPServer
{
    public partial class Server : Form
    {
        public const int Port = 51388;
        public TcpListener lister;
        public TcpClient client;
        public IPAddress ipAddress;
        public NetworkStream networkStream;
        public BinaryReader reader;
        public BinaryWriter writer;
        public IPEndPoint ipPoint;
        public delegate void ShowStatusMessagestring msg );
        public ShowStatusMessage showStatusMessage;
        public delegate void ShowGetOrSendMessagestring msg );
        public ShowGetOrSendMessage showGetOrSendMessage;

        //构造器
        public Server( )
        {
            InitializeComponent( );          

            //状态栏信息和公共消息框信息
            showStatusMessage = new ShowStatusMessage( ShowStatusCallBack );
            showGetOrSendMessage = new ShowGetOrSendMessage( ShowGetOrSendCallBack );

            //本机IP
            IPAddress address = IPAddress.Loopback;
            //端点
            ipPoint = new IPEndPoint( address, Port );
            //创建Socket监听
            lister = new TcpListener( ipPoint );

            //显示本机IP和端口
            IPAddressBox.ReadOnly = true;
            PortBox.ReadOnly = true;
            IPAddressBox.Text = address.ToString( );
            PortBox.Text = Port.ToString();
        }

        //状态栏显示目前的连接状态和数据发送、接收的状态
        public void ShowStatusCallBack( string msg )
        {
            toolStripStatusLabel.Text = msg;
        }

        //设置公共消息框的数据
        public void ShowGetOrSendCallBack(string msg)
        {
            ShowMessageBox.Text +=$"\\r\\n来自{client.Client.RemoteEndPoint}的消息:";
            ShowMessageBox.Text += "\\r\\n" + msg;
        }

        //开启监听
        private void TcpListenStart_Click( object sender, EventArgs e )
        {
            lister.Start( );
            //开启新线程
            Thread thread = new Thread( Request );
            thread.Start( );
        }        

        //接收请求
        private void Request( )
        {
            statusStrip.Invoke( showStatusMessage, "正在监听……" );
            Thread.Sleep( 1000 );
            
            try
            {
                statusStrip.Invoke( showStatusMessage, "等待连接……" );
                client = lister.AcceptTcpClient( ); //阻塞线程,接收队列中的客户端请求
                if (client!=null)
                {
                    statusStrip.Invoke( showStatusMessage, "连接已经建立……" );
                    networkStream = client.GetStream( );
                    reader = new BinaryReader( networkStream );
                    writer = new BinaryWriter( networkStream );
                }
            }
            catch 
            {
                statusStrip.Invoke( showStatusMessage, "连接失败……" );
            }
        }

        //接收消息
        private void GetMessage_Click( object sender, EventArgs e )
        {
            try
            {
                statusStrip.Invoke( showStatusMessage, "消息接收中……" );
                ShowMessageBox.Invoke( showGetOrSendMessage, reader.ReadString( ) );//在创建"公共消息框控件"的线程上调用showGetOrSendMessage委托来显示消息  
            }
        &n

以上是关于网络知识 - 简易的自定义Web服务器的主要内容,如果未能解决你的问题,请参考以下文章

Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)

SharePoint 2013 中的自定义 Web 服务与模拟

Tinywebserver:一个简易的web服务器

一个简易的web服务器:Tinywebserver

用 Web 实现一个简易的音频编辑器

django框架简介及自定义简易版框架