专题三:自定义Web服务器
前言:
经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到我们想要的网页,这个过程中浏览器只是一个客户端,浏览器(应用层应用程序)通过HTTP协议把用户请求发送到服务端, 服务器接受到发送来的HTTP请求,然后对请求进行处理和响应,最后把响应的内容发送给客户端(浏览器这里充当了用户代理的客户端),浏览器再对接受到的响应内容(一般是HTML文件)进行解释并且显示出来。这就是一次完整的用户请求/响应模型,本专题所讲述的是一个简单的Web服务器,其他一些大型的Web服务器(IIS,Apache)也是这样的一个原理, 本专题只是简单讲述Web服务器的实现原理。
一、Socket编程实现一个简单的Web服务器
Socket这个概念是在Unix系统中提出来的。在Unix的时代,为了解决传输层的编程问题,Unix提供了类似于文件操作的网络操作方式——Socket,通过Socket,我们就可以像操作文件一样通过打开、写入、读取、关闭等操作完成网络编程,这样就使得网络编程可以统一到文件操作方面,这样就使我们更容易地编写网络应用程序。需要注意的是,应用层的协议需要网络程序专门处理,Socket不负责应用层协议,仅仅负责传输层的协议。
现在介绍下网络端口号(port)的概念,在同一个网络地址中,为了区分使用相同协议的不同应用程序,为不同的应用程序分配一个数字编号,我们把这个编号就成为网络端口号(就是区分同一个网络地址中不同的进程)。端口号是由一个两个字节的整数,所以取值范围为0~65535,这些端口号又分为三类:
- 第一类的范围是0~1023,称为众所周知的端口,这些端口号由特定的网络程序使用,例如,TCP协议使用80端口来完成Http协议的传输。
- 第二类的范围是1024~49151,称为登记端口,一般情况下不应该在程序中使用。
- 第三类的范围是49152~65535,称为私有端口, 这些端口可以由普通用户程序使用。
在我们用Socket开发网络应用程序中,还有一个就是端点的概念,在网络中,通过IP地址,协议和端口号可以唯一地确定网络上的一个应用程序,其中把IP地址和端口的组合叫做端点(EndPoint)。每个Socket需要绑定到一个端点上与其他端点进行通信。
介绍完基本的一些概念后,下面演示通过Socket编程实现一个简单的Web服务器,此实例中就是简单向浏览器返回一个固定的静态页面,实现代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks; namespace WebServer
{
class Program
{
/// <summary>
/// 实现一个简单的Web服务器
/// 该服务器向请求的浏览器返回一个静态的HTML页面
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
//获取本机的IP地址
IPAddress localaddress = IPAddress.Loopback;
//创建可以访问的断点,49155表示端口号,如果这里设置为0,表示使用一个由系统分配的空闲的端口号
IPEndPoint endpoint = new IPEndPoint(localaddress,);
//创建Socket对象,使用IPv4地址,数据通信类型为数据流,传输控制协议为TCP协议。
Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//将Socket绑定到端点上
socket.Bind(endpoint);
//设置连接队列的长度
socket.Listen(); while (true)
{
Console.WriteLine("Wait an connect Request...");
//开始监听,这个方法会堵塞线程的执行,直到接收到一个客户端的连接请求
Socket clientsocket = socket.Accept(); //输出客户端的地址
Console.WriteLine("Client Address is :{0}",clientsocket.RemoteEndPoint);
//把客户端的请求数据读入保存到一个数组中
byte[] buffer = new byte[]; int receivelength = clientsocket.Receive(buffer,,SocketFlags.None); //接收指定字节数的数据
string requeststring = Encoding.UTF8.GetString(buffer,,receivelength); //解码为字符串 //在服务器端输出请求的消息
Console.WriteLine(requeststring); //服务器端作出相应内容
//相应的状态行
string statusLine = "HTTP/1.1 200 OK\r\n";
byte[] responseStatusLineBytes = Encoding.UTF8.GetBytes(statusLine);
string responseBody = "<html><head><title>Default Page</title></head><body><p style='font:bold;font-size:24pt'>Welcome you</p></body></html>";
string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length); byte[] responseHeaderBytes = Encoding.UTF8.GetBytes(responseHeader);
byte[] responseBodyBytes = Encoding.UTF8.GetBytes(responseBody); //向客户端发送状态行
clientsocket.Send(responseStatusLineBytes); //向客户端发送回应头信息
clientsocket.Send(responseHeaderBytes); //发送头部和内容的空行
clientsocket.Send(new byte[] {, }); //向客户端发送主体部分
clientsocket.Send(responseBodyBytes); //断开连接
clientsocket.Close();
Console.ReadKey();
break;
}
//关闭服务器
socket.Close();
}
}
}
运行结果:
首先运行服务端后的界面:
在浏览器中输入http://localhost:49155/ 则浏览器可以看到如下的所示的结果:
此时在服务器端显示的输出为:
这里只是简单实现了一个web服务器的功能,当然实际的Web服务器通过用户的发来的Http请求中获得请求文件类型,请求文件名以及请求目录等信息,然后Web服务器根据这些请求信息从服务器的物理目录中寻找请求的文件,如果在服务器中找到请求的文件,然后服务器把响应内容发送给客户端。这里只是通过这个简单的Web服务器让大家理解请求/响应模型以及Web服务器的工作原理,一些复杂的Web服务器也是在此基础进行一些其他功能的扩展。
二、基于TcpListener的Web服务器
在.net平台下, 为了简化网络编程,.net对套接字又进行了一次封装,封装后的类是在System.Net.Sockets命名空间下的TcpListener类和TcpClient类,使用TcpListener类用来监听和接收传入的连接请求,在该类的构造函数中只需要传递一组网络端点信息就可以准备好监听参数,而不需要设置使用的网络协议等细节,调用Start方法后,监听工作就开始(间接调用了Socket.Listen方法),AcceptTcpClient方法将阻塞进程,直到一个客户端发来连接请求为止。
封装了Socket的TcpClient对象,同时从传入的连接队列中删除该客户端的连接请求。此时通过这个TcpClient对象与客户端进行通信。
下面是基于TcpListener和TcpClient的一个简单的Web服务器的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets; namespace TcpWebServer
{
class Program
{
static void Main(string[] args)
{
//获取本机的IP地址
IPAddress localaddress = IPAddress.Loopback; //创建可以访问的端点,49155表示端口号,如果设置为0,表示使用一个由系统分配的空闲的端口号
IPEndPoint endpoint = new IPEndPoint(localaddress,); //创建Tcp监听器
TcpListener tcpListener = new TcpListener(endpoint); //启动监听
tcpListener.Start();
Console.WriteLine("Wait an connect Request...");
while (true)
{
//等待客户连接
TcpClient client = tcpListener.AcceptTcpClient();
if (client.Connected == true)
{
//输出已经建立连接
Console.WriteLine("Created connection");
}
//获得一个网络流对象
//该网络流对象封装了Socket的输入和输出操作
//此时通过对网络流对象进行写入来返回响应消息
//通过对网络流对象进行读取来获得请求消息
NetworkStream netstream = client.GetStream(); //提供基础数据流
//把客户端的请求数据读入保存到一个数组中
byte[] buffer = new byte[]; int receivelength = netstream.Read(buffer,,);
string requeststring = Encoding.UTF8.GetString(buffer,,receivelength); //在服务器端输出请求的消息
Console.WriteLine(requeststring); //服务器端作出相应内容
//响应的状态行
string statusLine = "HTTP/1.1 200 OK\r\n";
byte[] responseStatusLineBytes = Encoding.UTF8.GetBytes(statusLine);
string responseBody = "<html><head><title>Default Page</title></head><body><p style='font:bold;font-size:24pt'>Welcome you</p></body></html>";
string responseHeader = string.Format("Content-Type: text/html; charset=UTf-8\r\nContent-Length: {0}\r\n", responseBody.Length); byte[] responseHeaderBytes = Encoding.UTF8.GetBytes(responseHeader);
byte[] responseBodyBytes = Encoding.UTF8.GetBytes(responseBody); //写入状态行消息
netstream.Write(responseStatusLineBytes,,responseStatusLineBytes.Length);
//写入回应的头部
netstream.Write(responseHeaderBytes,,responseHeaderBytes.Length);
//写入回应头部和内容之间的空行
netstream.Write(new byte[] {,},,); //写入回应的内容
netstream.Write(responseBodyBytes,,responseBodyBytes.Length); //关闭与客户端的连接
client.Close();
Console.ReadKey();
break;
}
//关闭服务器
tcpListener.Stop();
}
}
}
程序的输出结果和前面的用Socket实现的效果相同,这里就不再贴图了,这里实现的Web服务器都是建立控制台的应用程序来实现的,感兴趣的朋友也可以用Windows窗体进行实现,同时这里也只是简单列出了采用同步的方式进行实现的,同时TcpListener类和TcpClient类同时支持异步操作的方法,下面列出这个两个类中异步操作的方法如下表:
类 |
方法 |
说明 |
TcpListener |
BeginAcceptTcpClient |
开始一个异步操作接受一个传入的连接 |
EndAcceptTcpClient |
异步接受传入的连接,并创建新的TcpClient对象来处理客户端的通信 |
|
TcpClient |
BeginConnect |
开始一个对远程主机连接的异步请求 |
EndConnect |
异步接受传入的连接尝试。 |
如果想了解线程同步和异步的朋友可以参考我的多线程处理系列:http://www.cnblogs.com/zhili/archive/2012/07/21/ThreadsSynchronous.html
三、总结
到这里这篇文章就差不多介绍到这里了,本专题是介绍如何自定义一个简单Web服务器,通过这个专题希望大家可以对Web服务器的工作过程有一个简单的了解。
另外在这个专题里面我们是用IE浏览器进行发送客户请求的,所以后面专题将介绍自定义一个浏览器,通过我们自定义的浏览器来对Web服务器发送请求,然后在自己自定义的浏览器中把响应消息显示出来。
转自:http://www.cnblogs.com/zhili/archive/2012/08/23/WebServer.html
专题三:自定义Web服务器的更多相关文章
- atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener
atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs ...
- 自定义web服务器(四)
关于HTTP协议的具体内容,前面章节已经有所讲解,相信读者已有所了解,在此不在累述,本章节讲解自定义web服务器. 一,.net提供自定义Web服务器的类 以下只是写主要的类 1.HTTPListe ...
- 网络知识 - 简易的自定义Web服务器
简易的自定义Web服务器 基于浏览器向服务端发起请求 两台主机各自的进程之间相互通信,需要协议.IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为 ...
- 高并发大流量专题---11、Web服务器的负载均衡
高并发大流量专题---11.Web服务器的负载均衡 一.总结 一句话总结: 推荐使用nginx七层(应用层)负载均衡的实现:配置那是相当的简单 http{ upstream cluster{ serv ...
- [C# 网络编程系列]专题三:自定义Web服务器
转自:http://www.cnblogs.com/zhili/archive/2012/08/23/2652460.html 前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网 ...
- 转:【专题三】自定义Web服务器
前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到 ...
- Tomcat、Apache、IIS这三种Web服务器来讲述3种搭建JSP运行环境的方法
一.相关软件介绍 1. J2SDK:Java2的软件开发工具,是Java应用程序的基础.JSP是基于Java技术的,所以配置JSP环境之前必须要安装J2SDK. 2. Apache服务器:Apache ...
- 图解HTTP权威指南(三)| Web服务器对HTTP请求的处理和响应
作者简介 李先生(Lemon),高级运维工程师(自称),SRE专家(目标),梦想在35岁买一辆保时捷.喜欢钻研底层技术,认为底层基础才是王道.一切新技术都离不开操作系统(CPU.内存.磁盘).网络 ...
- Apache、Lighttpd、Nginx 三种web服务器对比
简介 1. Apache Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一.Apac ...
随机推荐
- SecurityContextHolder.getContext().getAuthentication()为null的情况
原理: UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication() . ...
- SecureCRT在Tab下的Title显示IP
注意:要针对每个Session进行修改才行. 参考: http://www.cnblogs.com/tyhmj/archive/2013/12/20/3483247.html
- 信息收集工具recon-ng详细使用教程
前言: 最近在找Recon-ng详细一点的教程,可是Google才发现资料都很零散而且不详细,所以我打算具体写一下.Recon-ng在渗透过程中主要扮演信息收集工作的角色,同时也可以当作渗透工具,不过 ...
- STL中常用的c++语法
函数调用操作(c++语法中的左右小括号)可以被重载,STL的特殊版本都以仿函数形式呈现.如果对某个class进行operator()重载,它就成为一个仿函数. 仿函数(functor),就是使一个类的 ...
- 003 rip
r0#config t Enter configuration commands, one per line. End with CNTL/Z. r0(config)#router rip r0(c ...
- bzoj3190【JLOI2013】赛车
3190: [JLOI2013]赛车 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1445 Solved: 454 [Submit][Statu ...
- ORACLE取周、月、季、年的開始时间和结束时间
1 取周的開始时间和结束时间 取周的開始时间.以星期一为開始. SQL>SELECT TRUNC(TO_DATE('2013-11-25 10:31:11','YYYY ...
- Codeforces 216D Spider's Web 树状数组+模拟
题目链接:http://codeforces.com/problemset/problem/216/D 题意: 对于一个梯形区域,假设梯形左边的点数!=梯形右边的点数,那么这个梯形为红色.否则为绿色, ...
- linux 经常使用网络命令
1. ifconfig ifconfig主要是能手动启动.观察和改动网络接口的相关參数.能改动的參数许多,包含IP參数及MTU等都能改动,他的语法例如以下: [root@linux ~]# ifco ...
- cocos2dx游戏开发学习笔记2-从helloworld開始
一.新建project 具体安装和新建project的方法在cocos2dx文件夹下的README.md文件里已经有具体说明,这里仅仅做简介. 1.上官网下载cocos2dx-3.0的源代码.http ...