网络知识 - 简易的自定义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, 1024, SocketFlags.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[] { 13, 10 };

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

基于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 ShowStatusMessage( string msg );
        public ShowStatusMessage showStatusMessage;
        public delegate void ShowGetOrSendMessage( string 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委托来显示消息  
            }
            catch
            {
                //如果出现异常则关闭现有连接,清除所有资源
                statusStrip.Invoke( showStatusMessage, "对方没有发送消息或接收消息失败……" );            
                if (client != null) client.Close( );
                if (reader != null) reader.Close( );
                if (writer != null) writer.Close( );
                statusStrip.Invoke( showStatusMessage, "连接已经断开……" );
                //重新开启新线程来接收请求
                Thread thread = new Thread( Request );
                thread.Start( );
            }
        }

//发送消息
        private void SenMessage_Click( object sender, EventArgs e )
        {           
            string senMsg = SendMessageBox.Text;
            if(senMsg == string.Empty)
            {
                MessageBox.Show("发送的消息不能为空" );
                return;
            }

statusStrip.Invoke( showStatusMessage, "正在发送消息……" );
            Thread proxyThread = new Thread( ( ) =>
            {
                try
                {
                    writer.Write( senMsg );
                    Thread.Sleep( 3000 ); //模拟发送延时
                    writer.Flush( );
                    statusStrip.Invoke( showStatusMessage, "消息发送成功……" );
                    ShowMessageBox.Invoke( showGetOrSendMessage, senMsg );//在创建"公共消息框控件"的线程上调用showGetOrSendMessage委托来显示消息
                }
                catch 
                {
                    //如果出现异常则需要关闭现有连接,清除所有资源后重新开始
                    statusStrip.Invoke( showStatusMessage, "消息发送失败……" );
                    if (client != null) client.Close( );
                    if (reader != null) reader.Close( );
                    if (writer != null) writer.Close( );
                    //重新开启新线程来接收请求
                    Thread thread = new Thread( Request );
                    thread.Start( );
                }
            } );

proxyThread.Start( );
        }

//关闭监听
        private void CloseTcpListen_Click( object sender, EventArgs e )
        {
            if (client != null) client.Close( );
            if (reader != null) reader.Close( );
            if (writer != null) writer.Close( );
            lister.Stop( );
            statusStrip.Invoke( showStatusMessage, "监听已经关闭……" );
        }

//断开连接
        private void NoConnect_Click( object sender, EventArgs e )
        {
            if (client != null) client.Close( );
            if (reader != null) reader.Close( );
            if (writer != null) writer.Close( );
            statusStrip.Invoke( showStatusMessage, "连接已断开……" );
        }

//清空消息
        private void ClearMessage_Click( object sender, EventArgs e )
        {
            ShowMessageBox.Clear( );
        }

//点击关闭窗口按钮时,关闭TCP侦听,否则它会一直开启
        private void Server_FormClosing( object sender, FormClosingEventArgs e )
        {
            lister.Stop( );
        }
    }
}

客户端源码

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

namespace TCPClient
{
    public partial class Client : Form
    {
        public TcpClient server;
        public IPAddress ipAddress;
        public NetworkStream networkStream;
        public BinaryReader reader;
        public BinaryWriter writer;
        public IPEndPoint ipPoint;
        public delegate void ShowStatusMessage( string msg );
        public ShowStatusMessage showStatusMessage;
        public delegate void ShowGetOrSendMessage( string msg );
        public ShowGetOrSendMessage showGetOrSendMessage;

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

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

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

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

//连接服务器
        private void ConnectToServer_Click( object sender, EventArgs e )
        {            
            if (ServerIP.Text == string.Empty || Port.Text == string.Empty)
            {
                MessageBox.Show( "IP和端口都必须提供" );
                return;
            }
            string IPData = ServerIP.Text;
            int PortData = Convert.ToInt32( Port.Text );

//开启新线程发起连接请求
            Thread thread = new Thread(()=>
            {
                statusStrip.Invoke( showStatusMessage, "正在连接服务器……" );
                try
                {
                    server = new TcpClient( );//服务端开启了TCP监听,正等待着接收客户端的连接请求,此处根据服务端的端点调用连接方法,就可以建立连接
                    server.Connect( IPData, PortData );
                    Thread.Sleep( 1000 ); //模拟发送延时
                    if (server != null)
                    {
                        statusStrip.Invoke( showStatusMessage, "连接服务器成功……" );
                        networkStream = server.GetStream( ); //接收到ASK数据包后会得到一个NetworkStream,利用它可以读取远程数据或向远程发送数据
                        reader = new BinaryReader( networkStream );
                        writer = new BinaryWriter( networkStream );
                    }
                }
                catch
                {
                    statusStrip.Invoke( showStatusMessage, "连接服务器失败……" );
                }
            } );
            thread.Start( );
        }

//接收消息
        private void GetMessage_Click( object sender, EventArgs e )
        {
            //开启新线程来接收远程消息
            Thread proxyThread = new Thread( ( ) =>
            {
                statusStrip.Invoke( showStatusMessage, "消息接收中……" );
                try
                {
                    ShowMessageBox.Invoke( showGetOrSendMessage, reader.ReadString( ) );
                    statusStrip.Invoke( showStatusMessage, "消息接收成功……" );
                }
                catch
                {
                    //如果出现异常则关闭现有连接,清除所有资源
                    statusStrip.Invoke( showStatusMessage, "对方没有发送消息或接收消息失败……" );
                    if (server != null) server.Close( );
                    if (reader != null) reader.Close( );
                    if (writer != null) writer.Close( );
                    statusStrip.Invoke( showStatusMessage, "连接已经断开,请重新点击‘连接服务器‘按钮……" );
                }
            } );
            proxyThread.Start( );
        }

//发送消息
        private void SenMessage_Click( object sender, EventArgs e )
        {
            if (SendMessageBox.Text == string.Empty)
            {
                MessageBox.Show( "发送消息不能为空" );
            }
            string sendMsg = SendMessageBox.Text;

//开启新线程来发送消息
            Thread proxyThread = new Thread( ( ) =>
            {
                try
                {
                    statusStrip.Invoke( showStatusMessage, "消息发送中……" );                    
                    writer.Write( sendMsg );
                    writer.Flush( );
                    Thread.Sleep( 2000 );
                    ShowMessageBox.Invoke( showGetOrSendMessage, sendMsg );
                    statusStrip.Invoke( showStatusMessage, "消息发送成功……" );
                }
                catch
                {
                    //如果出现异常则关闭现有连接,清除所有资源
                    statusStrip.Invoke( showStatusMessage, "消息发送失败……" );
                    if (server != null) server.Close( );
                    if (reader != null) reader.Close( );
                    if (writer != null) writer.Close( );
                    statusStrip.Invoke( showStatusMessage, "连接已经断开,请重新点击‘连接服务器‘按钮……" );
                }
            } );
            proxyThread.Start( );
        }

//断开连接
        private void NoConnect_Click( object sender, EventArgs e )
        {
            if (server != null) server.Close( );
            if (reader != null) reader.Close( );
            if (writer != null) writer.Close( );
            statusStrip.Invoke( showStatusMessage, "连接已断开……" );
        }

//清空消息
        private void ClearMessage_Click( object sender, EventArgs e )
        {
            ShowMessageBox.Clear( );
        }

//关闭窗口
        private void CloseWin_Click( object sender, EventArgs e )
        {
            this.Close( );
        }
    }
}

参考资料

msdn:基于TCP协议的简单通信程序

网络知识 - 学习总目录

原文地址:https://www.cnblogs.com/myrocknroll/p/8458609.html

时间: 2024-08-22 02:51:39

网络知识 - 简易的自定义Web服务器的相关文章

[C# 网络编程系列]专题三:自定义Web服务器

转自:http://www.cnblogs.com/zhili/archive/2012/08/23/2652460.html 前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到我们想要的网页,这个过程中浏览器只是一个客户端,浏览器(应用层应用程序)通过HTTP协议把用户请求发送到服务端, 服务器接受到发送来的HTTP请求,然后对请求进行处理和响应

网络操作系统 第十一章 Web 服务器的按照与配置

习题 1.如何在WindowsServer 2008 系统中使用IIS进行多站点配置与管理? 1)运行"开始"–"程序"–"管理工具"–"服务器管理器"打开: 选择"WEB服务器(IIS)",注意:首次安装IIS的时候,当你点击"下一步"的时候会提示"是否添加WEB服务器所需的功能"对话框. 提示在安装IIS时,必须同时安装"Windows进程激活服务&qu

自己动手开发简易的Web服务器

使用python实现一个简易版的web服务器,旨在了解web服务器的工作原理,及了解HTTP协议.没有涉及多线程处理,并发之类的内容,以后再专门另外研究.首先上代码,稍后再作讲解. # coding=utf-8 import socket class httpd(object): def __init__(self,host,port): self.host = host self.port = port def parse_info(self,data): global _ENV _ENV =

Visual Studio中用于ASP.NET Web项目的Web服务器

当您在 Visual Studio 中开发 Web 项目时,需要 Web 服务器才能测试或运行它们. 利用 Visual Studio,您可以使用不同的 Web 服务器进行测试,包括 IIS Express.Internet Information Services (IIS).外部主机或自定义 Web 服务器. 您可以将其中任何一种 Web 服务器用于基于文件的 Web 应用程序项目. 对于基于文件的网站项目,您可以使用 IIS Express. 本主题介绍每种 Web 服务器以及如何选择要用

Visual Studio 中用于 ASP.NET Web 项目的 Web 服务器

Visual Studio 中用于 ASP.NET Web 项目的 Web 服务器 当您在 Visual Studio 中开发 Web 项目时,需要 Web 服务器才能测试或运行它们.             利用 Visual Studio,您可以使用不同的 Web 服务器进行测试,包括 IIS Express.Internet Information Services (IIS).外部主机或自定义 Web 服务器.  您可以将其中任何一种 Web 服务器用于基于文件的 Web 应用程序项目.

Web服务器、Web容器、Application服务器、反向代理服务器的区别与联系

在Web开发中,经常会听到Web服务器(Web Server).Web容器(Web Container).应用服务器(Application Server).反向代理服务器(Reverse Proxy Server)等容易混淆且不好理解名词.在面试中,这也是经常被问到的.本文介绍对四者的理解.区别与联系. 一.Web服务器(Web Server) 1. Web Server 或者叫 HTTP Server ,Web服务器的基本功能就是提供Web信息浏览服务.接受客户端的请求以及响应.处理Http

WEB服务器与应用服务器

Web服务器(Web Server) 通俗的讲,Web服务器的基本功能就是提供Web信息浏览服务.它只需支持HTTP协议.HTML文档格式及URL.与客户端的网络浏览器配合.因为Web服务器主 要支持的协议就是HTTP,所以通常情况下HTTP服务器和WEB服务器是相等的. 以这样的定义,IIS.Apache.Tomcat都可以属于Web服务器. [转载使用,请注明出处:http://blog.csdn.net/mahoking] Web服务器(Web Server)可以解析(handles)HT

WEB服务器与应用服务器解疑

1.WEB服务器: 理解WEB服务器,首先你要理解什么是WEB?WEB你可以简单理解为你所看到的HTML页面就是WEB的数据元素,处理这些数据元素的应用软件就叫WEB服务器,如IIS.apache. WEB服务器与客户端打交道,它要处理的主要信息有:session.request.response.HTML.JS.CS等. 2.应用服务器: 应用服务器如JSP,处理的是非常规性WEB页面(JSP文件),他动态生成WEB页面,生成的WEB页面在发送给客户端(实际上当应用服务器处理完一个JSP请求并

WEB服务器与应用服务器的区别

一.简述 WEB服务器与应用服务器的区别: 1.WEB服务器: 理解WEB服务器,首先你要理解什么是WEB?WEB你可以简单理解为你所看到的HTML页面就是WEB的数据元素,处理这些数据元素的应用软件就叫WEB服务器,如IIS.apache. WEB服务器与客户端打交道,它要处理的主要信息有:session.request.response.HTML.JS.CS等. 2.应用服务器: 应用服务器如JSP,处理的是非常规性WEB页面(JSP文件),他动态生成WEB页面,生成的WEB页面在发送给客户