网络知识 - 简易的自定义Web服务器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络知识 - 简易的自定义Web服务器相关的知识,希望对你有一定的参考价值。
简易的自定义Web服务器
基于浏览器向服务端发起请求
两台主机各自的进程之间相互通信,需要协议、IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为端点(EndPoint),在网络编程的世界里,.NET提供了Socket(套接字)类,此类处于传输层之中,Socket使开发人员可以以编程的方式侦听远程主机向本机发送的数据,并对到达传输层的数据包做出处理,同时它还可以向远程发送数据包。也即,Socket用于处理传输的数据。
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.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.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委托来显示消息
}
&n
以上是关于网络知识 - 简易的自定义Web服务器的主要内容,如果未能解决你的问题,请参考以下文章
Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)