前言:

经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到我们想要的网页,这个过程中浏览器只是一个客户端,浏览器(应用层应用程序)通过HTTP协议把用户请求发送到服务端, 服务器接受到发送来的HTTP请求,然后对请求进行处理和响应,最后把响应的内容发送给客户端(浏览器这里充当了用户代理的客户端),浏览器再对接受到的响应内容(一般是HTML文件)进行解释并且显示出来。这就是一次完整的用户请求/响应模型,本专题所讲述的是一个简单的Web服务器,其他一些大型的Web服务器(IIS,Apache)也是这样的一个原理, 本专题只是简单讲述Web服务器的实现原理。

一、Socket编程实现一个简单的Web服务器

Socket这个概念是在Unix系统中提出来的。在Unix的时代,为了解决传输层的编程问题,Unix提供了类似于文件操作的网络操作方式——Socket,通过Socket,我们就可以像操作文件一样通过打开、写入、读取、关闭等操作完成网络编程,这样就使得网络编程可以统一到文件操作方面,这样就使我们更容易地编写网络应用程序。需要注意的是,应用层的协议需要网络程序专门处理,Socket不负责应用层协议,仅仅负责传输层的协议。

现在介绍下网络端口号(port)的概念,在同一个网络地址中,为了区分使用相同协议的不同应用程序,为不同的应用程序分配一个数字编号,我们把这个编号就成为网络端口号(就是区分同一个网络地址中不同的进程)。端口号是由一个两个字节的整数,所以取值范围为0~65535,这些端口号又分为三类:

  1. 第一类的范围是0~1023,称为众所周知的端口,这些端口号由特定的网络程序使用,例如,TCP协议使用80端口来完成Http协议的传输。
  2. 第二类的范围是1024~49151,称为登记端口,一般情况下不应该在程序中使用。
  3. 第三类的范围是49152~65535,称为私有端口, 这些端口可以由普通用户程序使用。

在我们用Socket开发网络应用程序中,还有一个就是端点的概念,在网络中,通过IP地址,协议和端口号可以唯一地确定网络上的一个应用程序,其中把IP地址和端口的组合叫做端点(EndPoint)。每个Socket需要绑定到一个端点上与其他端点进行通信。

介绍完基本的一些概念后,下面演示通过Socket编程实现一个简单的Web服务器,此实例中就是简单向浏览器返回一个固定的静态页面,实现代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets; namespace WebServer
{
/// <summary>
/// 实现一个简单的Web服务器
/// 该服务器向请求的浏览器返回一个静态的HTML页面
/// </summary>
class Program
{
static void Main(string[] args)
{
// IPAddress.Loopback获得本机的Ip环回地址,即127.0.0.1
IPAddress localAddress = IPAddress.Loopback;
//IPAddress localAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; // IPAddress.Loopback获得本机的Ip地址
//IPAddress localAddress = IPAddress.Any; // IPAddress.Any获得Ip地址0.0.0.0 // 创建可以访问的断点,49155表示端口号,如果这里设置为0,表示使用一个由系统分配的空闲的端口号
IPEndPoint localEndPoint = new IPEndPoint(localAddress, ); // 创建Socket对象,使用IPv4地址,数据通信类型为数据流,传输控制协议TCP协议
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 将负责监听的Socket绑定到唯一的IP和端口上
serverSocket.Bind(localEndPoint);
// 设置监听队列的长度
serverSocket.Listen();
// 是否监听
bool isListening = true;
while (isListening)
{
Console.WriteLine("等待连接请求......");
// 开始监听,这个方法会堵塞线程的执行,直到接受到一个客户端的连接请求
Socket clientSocket = serverSocket.Accept();
// 输出客户端的地址
Console.WriteLine("客户端的地址是:{0}", clientSocket.RemoteEndPoint);
// 把客户端的请求数据读入保存到一个数组中
byte[] clientBuffer = new byte[]; // 创建缓存区(字节数组)
int receiveLength = clientSocket.Receive(clientBuffer, clientBuffer.Length, SocketFlags.None);// 将浏览器发来数据存入缓存区,并获得真实接收数据长度
string requestString = Encoding.UTF8.GetString(clientBuffer, , 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.Shutdown(SocketShutdown.Both);
clientSocket.Close();
isListening = false;
}
// 关闭服务器
serverSocket.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.Net;
using System.Net.Sockets; namespace ConsoleApplication1
{
/// <summary>
/// 实现一个简单的Web服务器
/// 该服务器向请求的浏览器返回一个静态的HTML页面
/// </summary>
class Program
{
static void Main(string[] args)
{
// IPAddress.Loopback获得本机的Ip环回地址,即127.0.0.1
IPAddress localAddress = IPAddress.Loopback;
//IPAddress localAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; // IPAddress.Loopback获得本机的Ip地址
//IPAddress localAddress = IPAddress.Any; // IPAddress.Any获得Ip地址0.0.0.0 // 创建可以访问的断点,49155表示端口号,如果这里设置为0,表示使用一个由系统分配的空闲的端口号
IPEndPoint localEndPoint = new IPEndPoint(localAddress, ); // 创建Tcp监听器
TcpListener tcpListener = new TcpListener(localEndPoint);
// 启动监听
tcpListener.Start(); bool isListening = true;
while (isListening)
{
Console.WriteLine("等待连接请求......");
// 等待客户连接
TcpClient tcpClient = tcpListener.AcceptTcpClient();
if (tcpClient.Connected)
{
// 输出已经建立连接
Console.WriteLine("客户端的地址是:{0},已连接到远程服务器", tcpClient.Client.RemoteEndPoint);
} // 获得一个网络流对象
// 该网络流对象封装了Socket的输入和输出操作
// 此时通过对网络流对象进行写入来返回响应消息
// 通过对网络流对象进行读取来获得请求消息
NetworkStream netStream = tcpClient.GetStream();
// 把客户端的请求数据读入保存到一个数组中
byte[] clientBuffer = new byte[]; // 创建缓存区(字节数组)
int receiveLength = netStream.Read(clientBuffer, ,clientBuffer.Length);// 将浏览器发来数据存入缓存区,并获得真实接收数据长度
string requestString = Encoding.UTF8.GetString(clientBuffer, , 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);
// 断开连接
tcpClient.Close();
isListening = false;
}
// 关闭服务器
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

作者:Learning Hard
提示:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
如果对文章有任何问题,都可以再评论中留言,我会尽可能的答复您,谢谢你的阅读

转:【专题三】自定义Web服务器的更多相关文章

  1. atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty  HttpListener

    atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty  HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs   ...

  2. 自定义web服务器(四)

    关于HTTP协议的具体内容,前面章节已经有所讲解,相信读者已有所了解,在此不在累述,本章节讲解自定义web服务器.  一,.net提供自定义Web服务器的类 以下只是写主要的类 1.HTTPListe ...

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

    简易的自定义Web服务器 基于浏览器向服务端发起请求 两台主机各自的进程之间相互通信,需要协议.IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为 ...

  4. 高并发大流量专题---11、Web服务器的负载均衡

    高并发大流量专题---11.Web服务器的负载均衡 一.总结 一句话总结: 推荐使用nginx七层(应用层)负载均衡的实现:配置那是相当的简单 http{ upstream cluster{ serv ...

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

    转自:http://www.cnblogs.com/zhili/archive/2012/08/23/2652460.html 前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网 ...

  6. 专题三:自定义Web服务器

    前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到 ...

  7. Tomcat、Apache、IIS这三种Web服务器来讲述3种搭建JSP运行环境的方法

    一.相关软件介绍 1. J2SDK:Java2的软件开发工具,是Java应用程序的基础.JSP是基于Java技术的,所以配置JSP环境之前必须要安装J2SDK. 2. Apache服务器:Apache ...

  8. 图解HTTP权威指南(三)| Web服务器对HTTP请求的处理和响应

    作者简介   李先生(Lemon),高级运维工程师(自称),SRE专家(目标),梦想在35岁买一辆保时捷.喜欢钻研底层技术,认为底层基础才是王道.一切新技术都离不开操作系统(CPU.内存.磁盘).网络 ...

  9. Apache、Lighttpd、Nginx 三种web服务器对比

    简介 1.    Apache Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一.Apac ...

随机推荐

  1. linux 查看文件目录大小

    du [-abcDhHklmsSx] [-L <符号连接>][-X <文件>][--block-size][--exclude=<目录或文件>] [--max-de ...

  2. intellij idea建立maven项目

    配置jdk 配置mvn http://jingyan.baidu.com/article/d8072ac45d3660ec94cefd51.html 右键“计算机”,选择“属性”,之后点击“高级系统设 ...

  3. what's the 场外交易

    出自 MBA智库百科(https://wiki.mbalib.com/)   什么是场外交易 场外交易是指证券投资机构之间不通过股票交易所,而以电话.电传等方式相互进行的股票交易.           ...

  4. Python3学习之路~4.1 列表生成式、生成器

    1 列表生成式 我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求你把列表里的每个值加1,怎么实现?你可能会想到2种方式 a=[0,1,2,3,4,5,6,7,8 ...

  5. LigerUI子父窗口之间传参问题

    在父窗口自定义一个参数,该参数为一个方法,然后在子窗口使用 var dialog = frameElement.dialog; //调用页面的dialog对象(ligerui对象)该对象,取得父窗口定 ...

  6. [路径规划] VFF和VFH

    VFF虚拟力场法 #ifndef VFF_HEADER #define VFF_HEADER #include <vector> #include "utils\point.h& ...

  7. aop 日志统一处理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理.缓存.对象池管理以及日志记录.AOP将这些分散在各 ...

  8. 实现并发join的方法

    import threadingimport time def music(): print("begin to listen %s" %time.ctime()) time.sl ...

  9. 对于jquery实现原理的浅谈

    关键词:prototype(原型).它能让javascript的方法(也可看成:类)能够动态地追加方法(猜测:目的是为了代码实现引入“类的思想”) 废话少说,代码见真义. <html> & ...

  10. git-代码同步至github

    代码同步到github上 Last login: Wed Mar 27 13:53:06 on ttys001localhost:~ ligaijiang$ gitusage: git [--vers ...