第34篇 再谈http协议
从打开一个网址说起
当在浏览器中输入一个网址的时候,浏览器会渲染出对应的网页的内容。作为web开发人员来说,应该知道这个过程:
当输入的一个网址为域名的时候,浏览器则根据本机的网关和DNS服务器来解析出访问的真正的IP地址。如果是直接访问IP则直接与服务器通信,发送请求。 请求原理简单如下:
(此处只是简单表示下域名解析原理,解析过程比这个复杂的多。)发送请求的时候会经历TCP三次握手过程(http也是基于TCP的协议),当TCP连接建立成功后,浏览器会根据http协议,把请求的内容封装成请求报文,发送给web服务器.
服务器会根据请求的报文的内容,执行对应的程序和读取对应的文件,按照http协议的规则返回响应内容(包括header和body)。
服务器根据响应头来解析响应的内容,完成html+css+js的渲染和执行。
http协议
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
用简单的话来说,当客户端与服务器端通信的时候,需要传输的内容有(HTML 文件,js+css, 图片,文本, 查询结果等),http协议把内容传输规范化。可以随便查看下一个http协议的内容:
(上面的图是请求jqury的请求和响应信息)
请求的头信息
GET /script/jquery.js HTTP/1.1
Host: common.cnblogs.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: */*
Referer: http://kb.cnblogs.com/page/130970/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: __gads=ID=e2f30d616c5344bf:T=1484287454:S=ALNI_MY6vTrW8EvdzER-v1nE-YnGJjxTzA; pgv_pvi=1271870464; __utma=226521935.1402060598.1484617348.1487768840.1487819679.5; __utmz=226521935.1487819679.5.3.utmcsr=bing|utmccn=(organic)|utmcmd=organic|utmctr=c%23%20Semaphore; _ga=GA1.2.1402060598.1484617348
上面有几个重点的信息:
GET /script/jquery.js HTTP/1.1 这个代表请求的方法是GET(常用的有GET,POST,PUT,DELETE),请求的URI为 /script/jquery.js, HTTP的版本信息为1.1
Accept-Encoding: 是浏览器发给服务器,声明浏览器支持的编码类型
Referer: 告诉服务器我是从哪个页面链接过来的
User-Agent: 当前设备的一些信息
Cookie: 客户端存储的信息,一般是登录信息或者其它认证信息
响应头信息
HTTP/1.1 200 OK
Server: Tengine
Content-Type: application/javascript
Content-Length: 33411
Connection: keep-alive
Date: Wed, 20 Jul 2016 02:26:20 GMT
Vary: Accept-Encoding
Cache-Control: public,max-age=25920000
Last-Modified: Fri, 15 Feb 2013 03:06:57 GMT
Content-Encoding: gzip
Via: cache14.l2cm12[0,200-0,H], cache10.l2cm12[1,0], kunlun5.cn199[0,200-0,H], kunlun9.cn199[0,0]
Age: 19461677
X-Cache: HIT TCP_MEM_HIT dirn:9:763113955
X-Swift-SaveTime: Fri, 26 Aug 2016 05:18:04 GMT
X-Swift-CacheTime: 22712896
Timing-Allow-Origin: *
EagleId: 3d9a7e0914884432578382004e
HTTP/1.1 200 OK: 这上代表响应http版本和响应状态
Server: 代表响应的服务器的类型,此处为Tengine
Content-Type:响应的类型,此处是 application/javascript,即是js文件
Content-Length: 代表响应的字节数
Content-Encoding:内容的编码方式,此处为gzip
HTTP三点注意事项:
HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
浏览器
什么是浏览器
看到http协议都是由Chrome浏览器生成和发送的,这里可以把浏览器简单的定义:
浏览器是一个客户端程序,能够发送http协议、解析http响应、渲染html+css+js
捕获浏览器Http协议
浏览器与服务器的通信是基于TCP,发送是请求相关的内容,服务端收到请求会进行解析。基于这个原理,可以用一个用程序监听一个端口,用浏览器访问这个端口(发送数据),来捕获请求的内容。搭建一个winform窗体如下:
当点击开启的时候,运行如下代码:
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endPoint = new IPEndPoint(IPAddress.Parse(this.txtIP.Text), int.Parse(txtPort.Text));
socket.Bind(endPoint);
socket.Listen(10);
if (isAutoOpen.Checked)
{
//使用默认的浏览器打开
System.Diagnostics.Process.Start("http://" + this.txtIP.Text + ":" + this.txtPort.Text);
}
ThreadPool.QueueUserWorkItem(r =>
{
while (true)
{
var socketProx = socket.Accept(); //接收数据
var bytes = new byte[1024 * 1024];
var len = socketProx.Receive(bytes, 0, bytes.Length, SocketFlags.None);//获得长度
var httpHeader = Encoding.Default.GetString(bytes, 0, len);//把二进制的数据转成文本
SetText(httpHeader);//内容的显示
}
});
运行程序,会自动打开默认的浏览器,获得当前的ip和随机生成的接口,运行的结果如下:
监控获得内容如下:
GET / HTTP/1.1
Host: 10.28.11.43:55882
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
捕获的http的信息,与上面谈到的信息格式是相同的。
响应请求的内容
当请求过来的时候,基于上面的代码,可以给浏览器发送一个信息,在while块里面添加代码:
while (true)
{
var socketProx = socket.Accept(); //接收数据
var bytes = new byte[1024 * 1024];
var len = socketProx.Receive(bytes, 0, bytes.Length, SocketFlags.None);
var httpHeader = Encoding.Default.GetString(bytes, 0, len);
SetText(httpHeader);
if (!string.IsNullOrEmpty(httpHeader))
{
var httpResponse = "<h1>Hello Http</h1>";
var sentBytes = Encoding.Default.GetBytes(httpResponse);
socketProx.Send(sentBytes, 0, sentBytes.Length, SocketFlags.None);
socketProx.Shutdown(SocketShutdown.Both);
}
}
用Chrome请求结果为:
用火狐请求的结果为:
在面对上面的两个浏览器的请求结果时候,可以看到,此时程序已经和浏览器进行了通信。造成上面的两个结果的不同,是没有指定响应的内容,浏览器按照默认的格式进行渲染,所以Chrome认为是html,而FireFox认为是text。
避免上面的结果,可以响应的时候把返回的内容进行约束,即:Content-Type属性。把响应的内容修改如下:
var httpResponse = "<h1>Hello Http</h1>";
var sentBytes = Encoding.Default.GetBytes(httpResponse);
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.1 200 OK");
sb.AppendLine("Date:" + DateTime.Now);
sb.AppendLine("Content-Type:text/html");
sb.AppendLine("Content-Length:" + sentBytes.Length);
sb.AppendLine();
var sentHeader = Encoding.Default.GetBytes(sb.ToString());
socketProx.Send(sentHeader, 0, sentHeader.Length, SocketFlags.None);//发送内容前,先发送响应的头文件
socketProx.Send(sentBytes, 0, sentBytes.Length, SocketFlags.None);
socketProx.Shutdown(SocketShutdown.Both);
修改后,fireFox响应结果如下:
简单的示例 --- 简单文件服务器
利用上面所述的demo,可以简单的扩展成一个简单的文件服务器。当程序启动的时候,读取当前的目录所有的文件信息转成html信息,发送给浏览器。运行结果如下 :
这样可以简单做一个服务器,使用ajax和文件分享都是可以的。git地址:https://github.com/fuwei199006/DemoLib/tree/master/Http%E5%8D%8F%E8%AE%AE/Tools.Server
写于 2017.03.02
第34篇 再谈http协议的更多相关文章
- 浅谈HTTP中Get与Post的区别/HTTP协议与HTML表单(再谈GET与POST的区别)
HTTP协议与HTML表单(再谈GET与POST的区别) GET方式在request-line中传送数据:POST方式在request-line及request-body中均可以传送数据. http: ...
- 从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造
在<在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP>里面提到 单个TCP包每次打包1448字节的数据进行发送(以太网Ethernet最大的数据帧是1518字节,以 ...
- Python基础篇 -- 小数据池和再谈编码
小数据池 1. id() 通过id()可以查看到一个变量表示的值在内存中的地址 s = "Agoni" print(id(s)) # 2410961093272 2. is 和 = ...
- 再谈HTTP2性能提升之背后原理—HTTP2历史解剖
即使千辛万苦,还是把网站升级到http2了,遇坑如<phpcms v9站http升级到https加http2遇到到坑>. 因为理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 ...
- 再谈Transaction——MySQL事务处理分析
MySQL 事务基础概念/Definition of Transaction 事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个 sql 语句,这些语句要么都执行 ...
- [转载]再谈百度:KPI、无人机,以及一个必须给父母看的案例
[转载]再谈百度:KPI.无人机,以及一个必须给父母看的案例 发表于 2016-03-15 | 0 Comments | 阅读次数 33 原文: 再谈百度:KPI.无人机,以及一个必须 ...
- Unity教程之再谈Unity中的优化技术
这是从 Unity教程之再谈Unity中的优化技术 这篇文章里提取出来的一部分,这篇文章让我学到了挺多可能我应该知道却还没知道的知识,写的挺好的 优化几何体 这一步主要是为了针对性能瓶颈中的”顶点 ...
- 【转】 浅谈Radius协议
浅谈Radius协议 2013-12-03 16:06 5791人阅读 评论(0) 收藏 举报 分类: Radius协议分析(6) 从事Radius协议开发有段时间了,小弟不怕才疏学浅,卖弄一下, ...
- 转:浅谈Radius协议 -来自CSDN:http://blog.csdn.net/wangpengqi/article/details/17097221
浅谈Radius协议 2013-12-03 16:06 5791人阅读 评论(0) 收藏 举报 分类: Radius协议分析(6) 从事Radius协议开发有段时间了,小弟不怕才疏学浅,卖弄一下, ...
随机推荐
- window 2008+apache2.4.4+php5.5+mysql-5.6.12+phpmyadmin4.0.4.1安装过程(参考他人文章基础上加上自己遇到的问题)
一.window server2008的安装 1.我用U盘安装的,先用UltraISO把server2008刻录到U盘中,过程我搜了一下,帖个地址: http://wenku.baidu.com/vi ...
- oracle 查询哪些表分区
如果查询当前用户下得分区表:select * from user_tables where partitioned='YES'如果要查询整个数据库中的分区表:select * from dba_tab ...
- 在Windows下开发Node.js的C/C++原生扩展
准备工作 (1)本机系统说明:本人机器为win7 64位,32位也可以. (2)软件安装: VISUAL C++ 2010 EXPRESS(Visual Studio 2010也可以): window ...
- Flex移动皮肤开发(二)
范例文件 mobile-skinning-part2.zip 在这个讨论创建 Flex 移动 skin 的系列的 第 1 部分 中,我讨论了 Flex 团队在 Mobile 主题中所做的性能优化的原理 ...
- Servlet 获取IllegelStateException
Servlet 获取IllegelStateException: response提交之后,进行requestDispatcher.forwar(),会产生这样的问题: 但是必须是outputStre ...
- Runtime of Objective-C
[0] Outline -- [1] 版本和平台 -- [2] 与Runtime System交互 -- [3] 方法的动态决议 -- [4] 消息转发 -- [5] 类型编码 -- [6 ...
- loadrunner controller:实时查看VUser的运行情况
1) 如下图,在Run标签页,点击"Vusers..."打开Vuser窗口: 2) 如下图选中一个Vuser点击按钮可以打开Run-Time Vie ...
- mysql表名忽略大小写
安装完数据库,建表查询发现表不存在,原来是表名大小写写错了,原来Linux下的MySQL默认是区分表名大小写的,这样的话对变成灰带来很大的不变,如何才能使mysql表名不区分大小写呢? 通过如下设置, ...
- ABP入门系列(10)——扩展AbpSession
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 一.AbpSession是Session吗? 1.首先来看看它们分别对应的类型是什么? 查看源码发 ...
- 学习篇之String()
// 3个特殊的引用类型:Boolean,Number,String var s1 = "some text"; ,); // me t ,); // me ,-); // so ...