【Web后端笔记】基于Socket实现的简单Web服务器搭建

我们在地址栏中输入的内容称为通用资源标记符(Universal Resource Identifier,URI)它有很多种样式,在Web中我们通常称为统一资源定位符(Uniform Resource Locator,URL)的形式,它的格式如下: 

协议://主机[.端口号][绝对路径[?参数]] 

http://www.cnblogs.com/DebugLZQ/中,http表示协议名称;www.cnblogs.com表示主机的地址;可选的端口号没有出现,那么,将使用http协议默认的端口号80;绝对路径为/DebugLZQ/;在这个例子中没有参数出现。

在.NET中,不管是URI还是URL,都使用定义在System命名空间中得URI类来进行处理。对应上面的介绍,这个类定义了5个属性,分别对应5个组成部分,如下所示:

    Scheme:协议的名称

    Host:取得URI地址中得主机部分

    Port:取得端口号

    AbsolutePath:绝对路径部分

    Query:URI地址中得参数部分

下面的例子演示了地址中各个部分:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5
 6 namespace URI说明
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             System.Uri DebugLZQAddress = new Uri("http://www.cnblogs.com/DebugLZQ/");
13             Console.WriteLine("Scheme: {0}",DebugLZQAddress.Scheme );
14             Console.WriteLine("Host: {0}", DebugLZQAddress.Host );
15             Console.WriteLine("Port: {0}", DebugLZQAddress.Port );
16             Console.WriteLine("AbsolutePath: {0}", DebugLZQAddress.AbsolutePath );
17             Console.WriteLine("Query: {0}", DebugLZQAddress.Query );
18         }
19     }
20 }

输出结果如下:

其中绝对路径部分使用类似于Unix的文件目录的形式来描述服务器中得资源,这个绝对路径被传送到服务器之后,在Web服务器上通常被称为虚拟路径。

我们在地址栏输入URL后,如何找到服务器呢?互联网上的主机千千万,我们要访问的服务器是互联网上数千万台服务器中得一台,它很可能远在地球的另一边。浏览器要找到服务器,需要提供服务器的网络地址。

在当前的TCP/IP协议下,所谓服务器的网络地址,就是一个IP地址,目前我们使用IPv4的地址,即IP协议第4个版本规定的地址,每个地址由四个字节共32位组成。理论上将,可以表示4G个网络地址。通常我们用远点分隔四个数字来表示一个地址,每个数字对应地址的一个字节,例如,微软的IP地址为:207.46.19.254,直接在地址栏中输入http://207.46.19.254也可以访问网页。

但是,这些数字实在很难让人记忆,人们更愿意通过一个有意义的名字来找到一台主机。在经历了短暂得互联网初期阶段之后,1983年,保罗·莫卡派(Paul Mockapetris)发明了域名系统,这样,在互联网上,我们可以为IP地址起一个有意义的名字以方便找寻主机,这个名字成为域名。比如,微软Web服务器的域名为www.microsoft.com,这个名字对应实际IP地址为207.46.19.254。

虽然这个名字很好记,但是只有这个名字并不能直接找到微软的Web服务器,必须建立起名字和IP地址之间的对应关系。这个工作由域名服务器DNS(即Domain Name Server)完成。DNS服务器提供一个列私语分层的通讯录,允许用户通过域名来查找对应的地址,或者完成通过地址来查找对应的域名。通常情况下,互联网服务商已经为我们自动设置了DNS服务器,因此可以简单地通过www.microsoft.com域名找到微软的Web服务器。

找到服务器之后,需要将请求从我们的客户端传输到服务器,那么,两台计算机是如何通信的呢?他们如何才能理解彼此发送的数据呢?这就需要提到协议。

当浏览器寻找到Web服务器的地址之后,浏览器帮助我们把对服务器的请求转换为一系列参数发送给Web服务器。服务器受到浏览器发来的请求参数之后,将会分析这些数据,并进行处理。然后向浏览器回应处理的结果,也就是一些新的数据;这些数据通常是HTML网页或者图片。浏览器收到之后,解析这些数据,将它们呈现在浏览器的窗口中,这就是我们看到的网页。

在浏览器与Web服务器的对话中,需要使用双方都能够理解的语法规范进行通信,这种程序之间进行通信的语法规范,我们称之为协议。协议有许多种,根据国际标准化组织ISO的网络参考模型,程序与程序之间的通信可分为7层,从低到高依次为:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。每层都有自己对应的协议。比如,应用层之间的协议我们称之为应用层协议。不同的应用程序可能有着不同的应用层协议。同一层的协议也可能有很多种。

浏览器与Web服务器之间的协议是应用层协议,当前,我们主要遵循的协议为HTTP/1.1。HTTP协议是Web开发的基础,这是一个无状态的协议,客户机与服务器之间通过请求和相应完成一次会话(Session)。每次会话中,通信双方发送的数据称为消息(Message),消息分两种:请求消息和回应消息。

消息的格式如图所示。

图DebugLZQ用绘图画的,不太美观。吼吼。。。 博友心声:真丑。。。

每个消息可能由三部分组成,第一部分为请求行或者回应的状态行,第二部分为消息的头部,第三部分为消息体部分。消息头部分和消息体部分使用一个空行进行分隔。

通常情况下,我们在客户端使用浏览器来访问服务器,浏览器软件帮助我们构造所有的请求消息。使用Fiddler软件,可以帮助我们检测到浏览器与服务器之间的通信内容,如图所示。

上图右上部为浏览器请求的内容,可以看到,第一行为请求行,请求的内容为:

GET http://www.microsoft.com/en-us/default.aspx HTTP/1.1

下面的连续N行为请求头部分,然后是一个空行,由于是GET请求,所以没有请求体部分。

图右下部为服务器回应的内容,第一行为回应的状态行,HTTP/1.1 200 OK表示请求的内容可以找到,但是需要到另外的地址去取。下面的15行为回应的头部。一个空行分隔了回应的头部和回应体部分,回应体中为一个简单的HTML网页。

HTTP协议定义了内容的格式,这是一个应用层的协议,应用层协议的内容需要通过传输层在浏览器和服务器之间传送,TCP/IP协议是ISO网络参考模型的一种实现。在TCP/IP协议中,与网络程序员相关的主要有两层:传输层和应用层。

传输层协议负责解决数据传输问题,包括数据通行的可靠性问题。传输层依赖更底层的网络层来完成实际的数据传输,在TCP/IP网络协议中,负责可靠通信的传输层协议为TCP协议。而网络层一般用网络驱动来实现,普通的程序员不会涉及;在TCP/IP协议中,网络层的协议为IP协议。

应用层用于在特定的应用程序之间传输数据。HTTP协议就是TCP/IP协议中专门用于浏览器与Web服务器之间通信的应用层协议。应用层协议依赖于传输层协议完成数据传输,传输层协议依赖于网络层协议王城数据传输,他们之间的关系如下图(浏览器与服务器之间网络通信的传输过程):

到这里,我们的准备理论超不读了,哦,还得再认识下Socket。

在遥远的Unix时代,为了解决传输层的编程问题,从4.2BSD Unix开始,Unix提供了类似于文件操作的网络操作方式----Socket。通过Socket,程序员可以像文件一样通过打开、写入、读取、关闭等操作完成网络编程。这使得网络编程可以统一到文件操作之下。通过Socket帮助程序员解决网络传输层的问题,而系统中得网络系统负责处理网络内部的复杂操作,这样程序员就可以比较容易地编写网络应用程序。需要注意的是应用层的协议需要针对网络程序专门处理,Socket不负责应用层的协议,仅仅负责传输层的协议。

当然网络毕竟不是简单的文件,所以,在使用Socket的时候,程序员还是需要设置一些网络相关的细节问题参数。

当通过Socket开发网络应用程序的时候,首先需要考虑所使用的网络类型,主要包括以下三个方面:

    1)Socket类型,使用网络协议的类别,如IPv4的类型为PF_INET。

    2)数据通信的类型,常见的数据报(SOCK_DGRAM)、数据流(SOCK_STREAM)。

    3)使用的网络协议,比如:TCP协议。

在同一个网络地址上,为了区分使用相同协议的不同应用程序,可以为不同的应用程序分配一个数字编号,这个编号称为网络端口号(port)。端口号是一个两字节的证书,取值范围从0~65535。IANA(Internet Assigned Number Authority,互联网地址分配机构)维护了一个端口分配列表,这些端口分三类,第一类的范围是0~1023,称为众所周知的端口,由IANA进行控制和分配,由特定的网络程序使用,例如,TCP协议使用80号端口来完成HTTP协议的传输。第二类的范围是1024~49151,称为登记端口,这些端口不由IANA控制,但是IANA委会了一个登记的列表,如果没有在IANA登记的话,也不应该在程序中使用。但是大多数的系统中,在没有冲突的情况下,也可以有用户程序使用。第三类的范围是49152~65535,称为动态或者似有端口号,这些端口可以由普通用户程序使用。

对于一个网络应用程序来说,通过地址、协议和端口号可以唯一地确定网络上的一个应用程序。其中地址和端口的组合称为端点(EndPoint)。每个Socket需要绑定到一个端点上与其他端点进行通信。

在.NET中,System.Net命名空间提供了网络编程的大多数数据类型以及常用操作,其中常用的类型如下:

    1)IPAddress类用来表示一个IP地址。

    2)IPEndPoint类用来表示一个IP地址和一个端口号的组合,称为网络的端点。

    3)System.Net.Sockets命名空间中提供了基于Socket编程的数据类型。

    4)Socket类封装了Socket的操作。

常用的操作如下:

    1)Listen:设置基于连接通信的Socket进入坚挺状态,并设置等待队列的长度。

    2)Accept:等待一个新的连接,当新连接到达的时候,返回一个指针对新连接的Socket对象。通过新的Socket对象,可以与新连接通信。

    3)Receive:通过Socket接受字节数据,保存到一个字节数组中,返回实际接受的字节数。

    4)Send:通过Socket发送预先保存在字节数组中得数据。

博友声音:够了,说了这么多,DebugLZQ真是不嫌麻烦。。。快!!!!!

DebugLZQ:吼吼,有了上面的基础,下面用代码演示如何通过Socket编程创建一个简单的Web服务器。必要说明:这个服务器通过49152号端口提供访问,向浏览器返回一个固定的静态网页。在这个解决方案中,请求的消息由浏览器生成,并发送到服务器,这个程序将简单地显示请求信息。回应的消息由服务器程序生成,通过Socket传输层返回给浏览器。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Net;//
 6 using System.Net.Sockets;//
 7
 8 namespace 基于Socket的最简单Web服务器
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             IPAddress address = IPAddress.Loopback;//取得本机的loopback网络地址,即127.0.0.1
15             IPEndPoint endPoint = new IPEndPoint(address, 49152);//创建可访问的端点,49152表示端口号,如果设置为0,表示使用一个空闲的端口号
16             Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建socket,使用IPv4地址,数据通信类型为字节流,TCP协议
17             socket.Bind(endPoint);//将socket绑定到一个端点上
18             socket.Listen(10);//设置连接队列的长度
19             Console.WriteLine("开始监听,端口号:{0}",endPoint.Port );
20             while (true)
21             {
22                 Socket client = socket.Accept();//开始监听,这个方法会阻塞线程的执行,直到接受到一个客户端的请求连接
23                 Console.WriteLine(client.RemoteEndPoint);//输出客户端的地址
24                 byte[] buffer = new byte[4096];//准备读取客户端请求的数据,读取的数据将保存在一个数组中
25                 int length = client.Receive(buffer, 4096, SocketFlags.None);//接受数据
26                 //将请求数据翻译为UTF-8
27                 System.Text.Encoding utf8 = System.Text.Encoding.UTF8;
28                 string requestString = utf8.GetString(buffer, 0, length);
29                 Console.WriteLine(requestString);//显示请求
30                 //回应的状态行
31                 string statusLine = "HTTP/1.1 200 OK\r\n";
32                 byte[] statusLineBytes = utf8.GetBytes(statusLine);
33                 //准备发送回客户端的网页
34                 string responseBody = "<html><head><title>From Socket Server</title></head><body><h1>Hello world.<h1></body></html>";
35                 byte[] responseBodyBytes = utf8.GetBytes(responseBody);
36                 //回应的头部
37                 string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length );
38                 byte[] responseHeaderBytes = utf8.GetBytes(responseHeader);
39
40                 //向客户端发送状态信息
41                 client.Send(statusLineBytes);
42                 //向客户端发送回应头
43                 client.Send(responseHeaderBytes);
44                 //头部与内容的分隔行
45                 client.Send(new byte[]{13,10});
46                 //向客户端发送内容部分
47                 client.Send(responseBodyBytes);
48
49                 //断开与客户端的连接
50                 client.Close();
51                 if (Console.KeyAvailable)
52                     break;
53             }
54             socket.Close();
55         }
56     }
57 }

运行后,在浏览器的窗口中输入:http://localhost:49152/,浏览器中可以看到如下的显示结果。

在命令行中看到如下输出:

参考原文:http://www.cnblogs.com/DebugLZQ/archive/2011/12/06/2278234.html

时间: 2024-12-18 02:39:05

【Web后端笔记】基于Socket实现的简单Web服务器搭建的相关文章

一个基于AIO实现的简单web服务器

一下是一个基于AIO实现的简单web服务器,这是一个简单例子 /** * 一个简单的web 服务器<br/> * 通过浏览器输入localhost:8080/访问 * * @author Joeson * @since 2014/05 * */ public class AioServer implements Runnable { private AsynchronousChannelGroup asyncChannelGroup; private AsynchronousServerSoc

Socket网络编程--简单Web服务器(1)

这一次的Socket系列准备讲Web服务器.就是编写一个简单的Web服务器,具体怎么做呢?我也不是很清楚流程,所以我找来了一个开源的小的Web服务器--tinyhttpd.这个服务器才500多行的代码,使用C语言.这一小节就不讲别的内容了.就对这个程序进行一些注释和讲解了. 主函数: 1 int main(void) 2 { 3 int server_sock = -1; 4 u_short port = 0; 5 int client_sock = -1; 6 struct sockaddr_

Socket网络编程--简单Web服务器(6)

本来是想实现ssl连接的,但是弄了好久都不成功,就索性不做了,等以后有能力再做了.所以这一小节就是本次的最后一节了.就简单的说几个注意点. 1.加个配置文件 使用单例模式,使用一个类,该类保存一些信息,例如一个配置类的一个属性为PAGE404的字符串,该字符串保存一个文件地址,然后我们的Page_404函数就可以用access判断PAGE404这个字符串对应的文件是否存在,如果存在那么如果是404页面那么就cat这个文件,而不是默认的404函数里面的页面.还有个端口什么的都是通过一个类似宏定义一

【Web后端笔记】jsp 的四个作用域 :page、request、session和application的区别

1.区别: 1.page指当前页面有效.在一个jsp页面里有效 2.request 指在一次请求的全过程中有效,即从http请求到服务器处理结束,返回响应的整个过程,存放在HttpServletRequest对象中.在这个过程中可以使用forward方式跳转多个jsp.在这些页面里你都可以使用这个变量. 3.Session是用户全局变量,在整个会话期间都有效.只要页面不关闭就一直有效(或者直到用户一直未活动导致会话过期,默认session过期时间为30分钟,或调用HttpSession的inva

Socket网络编程--简单Web服务器(2)

上一小节通过阅读开源的Web服务器--tinyhttpd.大概知道了一次交互的请求信息和应答信息的具体过程.接下来我就自己简单的实现一个Web服务器. 下面这个程序只是实现一个简单的框架出来.这次先实现能够Accept客户端的请求. 简单创建web服务器 webserver.h 1 #include <iostream> 2 #include <string> 3 #include <string.h> 4 #include <stdio.h> 5 #inc

Socket网络编程--简单Web服务器(4)

上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式.我们增加一个函数用于判断格式 1 int WebServer::get_filetype(char *type,char *path)//用于判断该url指向文件的后缀 2 { 3 if(strstr(path,".html")) 4 strcpy(type,"text/html"); 5 else if(strstr(path,".gif")) 6 strcpy

Socket网络编程--简单Web服务器(5)

这一小节我们将实现服务器对get和post的请求进行对cgi程序的调用.对于web服务器以前的章节已经实现了对get和post请求的调用接口,接下来给出对应接口的实现. 1 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args) 2 { 3 ServerExecuteCGI(cli_fd,path,args); 4 return 0; 5 } 6 int WebServer::ServerPostFunction(in

基于socket实现的简单的聊天程序

记得八年前第一次使用socket做的一个五子棋程序,需要序列化棋子对象,传递到对方的电脑上. 一个偶然的机会,第二次使用socket做点事情.先看聊天服务器端的实现: 服务器端要实现以下功能:      1.启动服务,开启监听      2.持续不断地接收消息      3.发送消息 启动服务,创建监听socket,绑定Ip和端口: 1 /// <summary> 2 /// 启动服务 3 /// </summary> 4 private void Start() 5 { 6 so

基于WSGI封装一个简单WEB框架

from wsgiref.simple_server import make_serverdef index(): return 'index'def login(): return 'login'def routes(): urlpatterns=( ('/index',index), ('/login',login), ) return urlpatternsdef runserver(environ,start_response): start_response('200 OK' , [(