向服务器请求数据的五种技术

Ajax,在它*基本的层面,是一种与服务器通讯而不重载当前页面的方法,数据可从服务器获得或发送给服务器。有多种不同的方法构造这种通讯通道,每种方法都有自己的优势和限制。
有五种常用技术用于向服务器请求数据:
(1)XMLHttpRequest (XHR)
(2)动态脚本标签插入
(3)框架
(4)Comet
(5)多部分的XHR
在现代高性能JavaScript中使用的三种技术是XHR,动态脚本标签插入和多部分的XHR。使用Comet和iframe(作为数据传输技术)往往是*限情况,不在这里讨论。

 一、XMLHttpRequest
目前*常用的方法中,XMLHttpRequest(XHR)用来异步收发数据。所有现代浏览器都能够很好地支持它,而且能够精细地控制发送请求和数据接收。你可以向请求报文中添加任意的头信息和参数(包括GET和POST),并读取从服务器返回的头信息,以及响应文本自身。以下是使用示例:
var url = ‘/data.php’;
var params = [
‘id=934875’,
‘limit=20’
];
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState=== 4) {
var responseHeaders = req.getAllResponseHeaders();
var data = req.responseText;
}
}
req.open(‘GET’, url + ‘?’ + params.join(‘&’), true);
req.setRequestHeader(‘X-Requested-With’, ‘XMLHttpRequest’);
req.send(null);
此例显示了如何从URL请求数据,使用参数,以及如何读取响应报文和头信息。readyState等于4表示整个响应报文已经收并完可用于操作。
readyState等于3则表示此时正在与服务器交互,响应报文还在传输之中。这就是所谓的“流”,它是提高数据请求性能的强大工具:
req.onreadystatechange = function() {
if (req.readyState=== 3) {
var dataSoFar = req.responseText;

}
else if (req.readyState=== 4) {
var data = req.responseText;

}
}
由于XHR提供了高级别的控制,浏览器在上面增加了一些限制。你不能使用XHR从当前运行的代码域之外请求数据,而且老版本的IE 也不提供readyState3,它不支持流。从请求返回的数据像一个字符串或者一个XML对象那样对待,这意味着处理大量数据将相当缓慢。
尽管有这些缺点,XHR仍旧是*常用的请求数据技术,也是*强大的,它应当成为你的首选。
当使用XHR请求数据时,你可以选择POST 或GET。如果请求不改变服务器状态只是取回数据(又称作幂等动作)则使用GET。GET请求被缓冲起来,如果你多次提取相同的数据可提高性能。
只有当URL和参数的长度超过了2’048个字符时才使用POST提取数据。因为Internet Explorer限制URL的长度,过长将导致请求(参数)被截断。
 二、动态脚本标签插入
该技术克服了XHR的*大限制:它可以从不同域的服务器上获取数据。这是一种黑客技术,而不是实例化一个专用对象,你用JavaScript创建了一个新脚本标签,并将它的源属性设置为一个指向不同域的URL。
var scriptElement = document.createElement(‘script’);
scriptElement.src = ‘http://any-domain.com/javascript/lib.js’;
document.getElementsByTagName_r(‘head’)[0].appendChild(scriptElement);
但是动态脚本标签插入与XHR相比只提供更少的控制。你不能通过请求发送信息头。参数只能通过GET方法传递,不能用POST。你不能设置请求的超时或重试,实际上,你不需要知道它是否失败了。你必须等待所有数据返回之后才可以访问它们。你不能访问响应信息头或者像访问字符串那样访问整个响应报文。
*后一点非常重要。因为响应报文被用作脚本标签的源码,它必须是可执行的JavaScript。你不能使用裸XML,或者裸JSON,任何数据,无论什么格式,必须在一个回调函数之中被组装起来。
var scriptElement = document.createElement(‘script’);
scriptElement.src = ‘http://any-domain.com/javascript/lib.js’;
document.getElementsByTagName_r(‘head’)[0].appendChild(scriptElement);
function jsonCallback(jsonString) {
var data = (‘(‘ + jsonString + ‘)’);
}
在这个例子中,lib.js 文件将调用jsonCallback 函数组装数据:
jsonCallback({ “status”: 1, “colors”: [ “#fff”, “#000”, “#ff0000” ] });
尽管有这些限制,此技术仍然非常迅速。其响应结果是运行JavaScript,而不是作为字符串必须被进一步处理。正因为如此,它可能是客户端上获取并解析数据*快的方法。我们比较了动态脚本标签插入和XHR的性能,在本章后面JSON 一节中。
请小心使用这种技术从你不能直接控制的服务器上请求数据。JavaScript没有权限或访问控制的概念,所以你的页面上任何使用动态脚本标签插入的代码都可以完全控制整个页面。包括修改任何内容、将用户重定向到另一个站点,或跟踪他们在页面上的操作并将数据发送给第三方。使用外部来源的代码时务必非常小心。
 三、多部分XHR
多部分XHR(MXHR)允许你只用一个HTTP 请求就可以从服务器端获取多个资源。它通过将资源(可以是CSS 文件,HTML 片段,JavaScript代码,或base64 编码的图片)打包成一个由特定分隔符界定的大字符串,从服务器端发送到客户端。JavaScript代码处理此长字符串,根据它的媒体类型和其他“信息头”解析出每个资源。
让我们从头到尾跟随这个过程。首先,发送一个请求向服务器索取几个图像资源:
var req = new XMLHttpRequest();
req.open(‘GET’, ‘rollup_images.php’, true);
req.onreadystatechange = function() {
if (req.readyState== 4) {
splitImages(req.responseText);
}
};
req.send(null);
这是一个非常简单的请求。你向rollup_images.php 要求数据,一旦你收到返回结果,就将它交给函数splitImages处理。
下一步,服务器读取图片并将它们转换为字符串:
$images = array(‘kitten.jpg’, ‘sunset.jpg’, ‘baby.jpg’);
foreach ($images as $image) {
$image_fh = fopen($image, ‘r’);
$image_data = fread($image_fh, filesize($image));
fclose($image_fh);
$payloads[] = base64_encode($image_data);
}
$newline = chr(1);
echo implode($newline, $payloads);
这段PHP代码读取三个图片,并将它们转换成base64字符串。它们之间用一个简单的字符,UNICODE的1,连接起来,然后返回给客户端。
然后回到客户端,此数据由splitImage 函数处理:
function splitImages(imageString) {
var imageData = imageString.split(“\u0001”);
var imageElement;
for (var i = 0, len = imageData.length; i < len; i++) {
imageElement = document.createElement(‘img’);
imageElement.src = ‘data:image/jpeg;base64,’ + imageData[i];
document.getElementById(‘container’).appendChild(imageElement);
}
}
此函数将拼接而成的字符串分解为三段。每段用于创建一个图像元素,然后将图像元素插入页面中。图像不是从base64 转换成二进制,而是使用data:URL 并指定image/jpeg 媒体类型。
*终结果是:在一次HTTP 请求中向浏览器传入了三张图片。也可以传入20 张或100 张,响应报文会更大,但也只是一次HTTP 请求。它也可以扩展至其他类型的资源。JavaScript文件,CSS 文件,HTML片段,许多类型的图片都可以合并成一次响应。任何数据类型都可作为一个JavaScript处理的字符串被发送。下面的函数用于将JavaScript代码、CSS 样式表和图片转换为浏览器可用的资源:
function handleImageData(data, mimeType) {
var img = document.createElement(‘img’);
img.src = ‘data:’ + mimeType + ‘;base64,’ + data;
return img;
}
function handleCss(data) {
var style = document.createElement(‘style’);
style.type = ‘text/css’;
var node = document.createTextNode(data);
style.appendChild(node);
document.getElementsByTagName_r(‘head’)[0].appendChild(style);
}
function handleJavaScript(data) {
(data);
}
由于MXHR响应报文越来越大,有必要在每个资源收到时立刻处理,而不是等待整个响应报文接收完成。这可以通过监听readyState3 实现:
var req = new XMLHttpRequest();
var getLatestPacketInterval, lastLength = 0;
req.open(‘GET’, ‘rollup_images.php’, true);
req.onreadystatechange = readyStateHandler;
req.send(null);
function readyStateHandler{
if (req.readyState=== 3 && getLatestPacketInterval === null) {
getLatestPacketInterval = window.setInterval(function() {
getLatestPacket();
}, 15);
}
if (req.readyState=== 4) {
clearInterval(getLatestPacketInterval);
getLatestPacket();
}
}
function getLatestPacket() {
var length = req.responseText.length;
var packet = req.responseText.substring(lastLength, length);
processPacket(packet);
lastLength = length;
}
当readyState3*次发出时,启动了一个定时器。每隔15毫秒检查一次响应报文中的新数据。数据片段被收集起来直到发现一个分隔符,然后一切都作为一个完整的资源处理。以健壮的方式使用MXHR的代码很复杂但值得进一步研究。
使用此技术有一些缺点,其中*大的缺点是以此方法获得的资源不能被浏览器缓存。如果你使用MXHR获取一个特定的CSS 文件然后在下一个页面中正常加载它,它不在缓存中。因为整批资源是作为一个长字符串传输的,然后由JavaScript代码分割。由于没有办法用程序将文件放入浏览器缓存中,所以用这种方法获取的资源也无法存放在那里。
另一个缺点是:老版本的Internet Explorer不支持readyState3或data: URL。Internet Explorer 8两个都支持,但在Internet Explorer 6和7中必须设法变通。
尽管有这些缺点,但某些情况下MXHR仍然显著提高了整体页面的性能:网页包含许多其他地方不会用到的资源(所以不需要缓存),尤其是图片。
网站为每个页面使用了独一无二的打包的JavaScript或CSS文件以减少HTTP请求,因为它们对每个页面来说是独一的,所以不需要从缓存中读取,除非重新载入特定页面。
由于HTTP请求是Ajax中**端的瓶颈之一,减少其需求数量对整个页面性能有很大影响。尤其是当你将100个图片请求转化为一个MXHR请求时。Ad hoc 在现代浏览器上测试了大量图片,其结果显示出此技术比逐个请求快了4到10倍。
有时你不关心接收数据,而只要将数据发送给服务器。你可以发送用户的非私有信息以备日后分析,或者捕获所有脚本错误然后将有关细节发送给服务器进行记录和提示。当数据只需发送给服务器时,有两种广泛应用的技术:XHR和灯标。
 (1) XMLHttpRequest
虽然XHR主要用于从服务器获取数据,它也可以用来将数据发回。数据可以用GET或POST 方式发回,以及任意数量的HTTP 信息头。这给你很大灵活性。当你向服务器发回的数据量超过浏览器的*大URL长度时XHR特别有用。这种情况下,你可以用POST 方式发回数据:
var url = ‘/data.php’;
var params = [
‘id=934875’,
‘limit=20’
];
var req = new XMLHttpRequest();
req.onerror = function() {
// Error.
};
req.onreadystatechange = function() {
if (req.readyState== 4) {
// Success.
}
};
req.open(‘POST’, url, true);
req.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
req.setRequestHeader(‘Content-Length’, params.length);
req.send(params.join(‘&’));
正如你在这个例子中看到的,如果失败了我们什么也不做。当我们用XHR捕获登陆用户统计信息时这么做通常没什么问题,但是,如果发送到服务器的是至关重要的数据,你可以添加代码在失败时重试:
function xhrPost(url, params, callback) {
var req = new XMLHttpRequest();
req.onerror = function() {
setTimeout(function() {
xhrPost(url, params, callback);
}, 1000);
};
req.onreadystatechange = function() {
if (req.readyState== 4) {
if (callback && typeof callback === ‘function’) {
callback();
}
}
};
req.open(‘POST’, url, true);
req.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
req.setRequestHeader(‘Content-Length’, params.length);
req.send(params.join(‘&’));
}
当使用XHR将数据发回服务器时,它比使用GET要快。这是因为对少量数据而言,向服务器发送一个GET请求要占用一个单独的数据包。另一方面,一个POST至少发送两个数据包,一个用于信息头。另一个用于POST体。POST更适合于向服务器发送大量数据,即因为它不关心额外数据包的数量,又因为Internet Explorer 的URL长度限制,它不可能使用过长的GET请求。
 (2) 灯标
此技术与动态脚本标签插入非常类似。JavaScript用于创建一个新的Image 对象,将src 设置为服务器上一个脚本文件的URL。此URL 包含我们打算通过GET格式传回的键值对数据。注意并没有创建img 元素或者将它们插入到DOM 中。
var url = ‘/status_tracker.php’;
var params = [
‘step=2’,
‘time=1248027314’
];
(new Image()).src = url + ‘?’ + params.join(‘&’);
服务器取得此数据并保存下来,而不必向客户端返回什么,因此没有实际的图像显示。这是将信息发回服务器的*有效方法。其开销很小,而且任何服务器端错误都不会影响客户端。
简单的图像灯标意味着你所能做的受到限制。你不能发送POST 数据,所以你被URL 长度限制在一个相当小的字符数量上。你可以用非常有限的方法接收返回数据。可以监听Image 对象的load 事件,它可以告诉你服务器端是否成功接收了数据。你还可以检查服务器返回图片的宽度和高度(如果返回了一张图片)并用这些数字通知你服务器的状态。例如,宽度为1 表示“成功”,2 表示“重试”。
如果你不需要为此响应返回数据,那么你应当发送一个204 No Content 响应代码,无消息正文。它将阻止客户端继续等待永远不会到来的消息体:
var url = ‘/status_tracker.php’;
var params = [
‘step=2’,
‘time=1248027314’
];
var beacon = new Image();
beacon.src = url + ‘?’ + params.join(‘&’);
beacon.onload = function() {
if (this.width == 1) {
// Success.
}
else if (this.width == 2) {
// Failure; create another beacon and try again.
}
};
beacon.onerror = function() {
// Error; wait a bit, then create another beacon and try again.
};
灯标是向服务器回送数据*快和*有效的方法。服务器根本不需要发回任何响应正文,所以你不必担心客户端下载数据。唯一的缺点是接收到的响应类型是受限的。如果你需要向客户端返回大量数据,那么使用XHR。如果你只关心将数据发送到服务器端(可能需要*少的回复),那么使用图像灯标。

使用Python来做物联网

使用Python来做物联网,就是这么easy!

 

搭建整套物联网系统的方法有很多,*近四处捣鼓,使用python + 阿里云搭建一套*简单的物联系统,可以将单片机上的数据通过阿里云传输到PC端。 一、基本结构 先看架构图 框架.png 二、设备端接入 物联网终端是用的ESP32,是一款自带蓝牙和Wifi的单片机。利用它可以直接接入互联网,无需其他模块。当然你可以将现在流行的NB-Iot模块来联网,不过需要自己写一下驱动程序。我买的模块是支持micropython开发的,在淘宝上可以搜索到,用起来很方便。有时间我会补上这一块的初步教程。 ESP32模块.jpg Micropython是可以在低端硬件上运行的python,可以使用python语言直接操作IO 和MCU的外设比如UART、I2C等,用起来非常方便,不要搭建复杂的开发环境,也不需要学习寄存器配置。作为一个对传统MCU开发非常熟悉的硬件工程师来说,感觉操作起来非常简单。目前Micropython已经支持很多硬件了,应该用比较广泛的STM32部分系列也被支持。Micropython也已经支持很多常用的库,比如蓝牙,telnet,mqtt等。下面这个链接是micropython的中文论坛。 www.micropython.org.cn/bbs/forum.p… ESP32 通过wifi 接入互联网,使用mqtt协议接入阿里云,将温度数据上传至阿里云。在云端通过消息订阅可以直接查看温度信息。在PC端使用python调用MQTT协议,接入到阿里云。但是PC端和ESP32在阿里云上是两个不同的设备,需要通过阿里云来转发信息,这样PC就可以拿到ESP32上传的数据了。 ESP32 上的代码如下: from umqtt.simple import MQTTClient import usocket as socket import time import wifi

wifi.connect()

#Demo_01 ProductKey = “”#使用你自己的 ClientId = “1234|securemode=3,signmethod=hmacsha1|” DeviceName = “Demo_01” DeviceSecret = “**********************”#使用你自己的

strBroker = ProductKey + “.iot-as-mqtt.cn-shanghai.aliyuncs.com” Brokerport = 1883

user_name = “Demo_01&”#使用你自己的 user_password = “*************************************”#使用你自己的

print(“clientid:”,ClientId,” “,”Broker:”,strBroker,” “,”User Name:”,user_name,” “,”Password:”,user_password,” “)

def connect(): client = MQTTClient(client_id = ClientId,server= strBroker,port=Brokerport,user=user_name, password=user_password,keepalive=60) #please make sure keepalive value is not 0

有几点需要说明: 1.代码中的wifi.connect()函数需要自己编写,网上能搜到类似的,也可以打赏私信我所要源码。 2.阿里云物联网平台的接入需要进行三元组认证,会根据一定的规则生成登录名和密码,这个网上信息还是比较全面的。 3.向阿里云物联网平台发布消息的格式一定要按照代码中所写,网上很多代码,但是对这一块的描述都不清楚。 Micropython使用的umqtt.simple库,一定要设置keepalive时间,否则无法连接。这一点我是摸索了好久,*终通过查看库的源码才发现的问题。 三、云端设置 在云端建立一个高级产品,并创建两个设备,以供ESP32 和PC连接。 device.JPG 需要在产品中定义一下功能。 device_define.JPG 云端和设备端都建立好了之后,可以查看设备运行状态看到数据上传 云端数据查看.JPG 这是查看数据记录得到的结果 云端数据记录.JPG 当你看到正确的数据之后,就说明你的成功接入物联网并上传了数据。 接下来就是*重要的部分——设置是使用规则引擎来进行数据转发,将设备demo_01的数据转发到demo_02。这一步的语法很重要,虽然有官网有详细教程,但是当时还是搞了好久才完全正确。 规则查询语句: SELECT items.IndoorTemperature.value as IndoorTemperature FROM “/sys/use-your-productkey-here/Demo_01/thing/event/property/post” WHERE items.IndoorTemperature.value > 0 四、PC端接入 PC 端使用python模拟MQTT设备登陆阿里云订阅消息就行了,只要装好python很快就可以实现,网上也有很多代码。代码的很大一部分就是在做三元组认证,可以将这部分稍微修改一下来计算ESP32 登陆时所需的 PC端python代码如下: # coding=utf-8 import datetime import time import hmac import hashlib import math

try: import paho.mqtt.client as mqtt except ImportError: print(“MQTT client not find. Please install as follow:”) print(“pip install paho-mqtt”)

#Demo_02 ProductKey = “*********”#使用你自己的 ClientId = “2234” # 自定义clientId DeviceName = “Demo_02” DeviceSecret =”**********************************8″#使用你自己的

us = math.modf(time.time())[0] ms = int(round(us * 1000)) timestamp = str(ms)

def calculation_sign(signmethod): data = “”.join((“clientId”, ClientId, “deviceName”, DeviceName, “productKey”, ProductKey, “timestamp”, timestamp))

strBroker = ProductKey + “.iot-as-mqtt.cn-shanghai.aliyuncs.com” port = 1883

client_id = “”.join((ClientId, “|securemode=3”, “,signmethod=”, “hmacsha1”, “,timestamp=”, timestamp, “|”)) username = “”.join((DeviceName, “&”, ProductKey)) password = calculation_sign(“hmacsha1”)

print(“=”*60) print(strBroker) print(“client_id:”, client_id) print(“username:”, username) print(“password:”, password) print(“=”*60)

def on_connect(client, userdata, flags, rc): print(“OnConnetc, rc: ” + str(rc))

def on_publish(client, msg, rc): if rc == 0: print(“publish success, msg = ” + msg)

def on_subscribe(mqttc, obj, mid, granted_qos): print(“Subscribed: ” + str(mid) + ” ” + str(granted_qos))

def on_log(mqttc, obj, level, string): print(“Log:” + string)

def on_message(mqttc, obj, msg): curtime = datetime.datetime.now() strcurtime = curtime.strftime(“%Y-%m-%d %H:%M:%S”) print(strcurtime + “: ” + msg.topic + ” ” + str(msg.qos) + ” ” + str(msg.payload)) on_exec(str(msg.payload))

def on_exec(strcmd): print(“Exec:”, strcmd) strExec = strcmd

if name == ‘main’: mqttc = mqtt.Client(client_id) mqttc.username_pw_set(username, password) mqttc.on_message = on_message mqttc.on_connect = on_connect mqttc.on_publish = on_publish mqttc.on_subscribe = on_subscribe mqttc.on_log = on_log mqttc.connect(strBroker, port, 120) # mqttc.loop_start() time.sleep(1) temperature =27.55 mqttc.subscribe(“/sys/************/Demo_02/thing/service/property/set”, qos=1) # 换成自己的 #send_mseg = ‘{“pm_25″: %s,”area”:”%s”,”time”:”%s”}’ % (0, 0, datetime.datetime.now()) #send_mseg = ‘{“id”: “1234”, “version”: “1.0”,”params”: {“IndoorTemperature”: %s},”method”: “thing.event.property.post”}’%(temperature) send_mseg = ‘{“params”: {“IndoorTemperature”: %s},”method”: “thing.event.property.post”}’ % (temperature)

五、总结 工作之余了解了一下物联网的发展,看到有意思的东西打算学一下,刚好看到了microPython,震惊之余,决心做点小东西玩玩。 这套框架全部使用python实现,比我了解到的*大多数物联网方案要简单太多,虽然有些开发首先,但是用来实现一些简单设计应该是不成问题的,只要你会python,这套系统可以很快构建。当然python也是非常好学的,长期使用C语言的人根本不需要什么学习就可以上手。记住,你不是一个人在战斗。

Python前景介绍

Python前景介绍

2019年刚到来,作为一个学Python,使用Python的学生来说,这无疑是一个令人振奋的消息——Python编程语言获得了“年度编程语言”的称号! Python之所以获得这一称号,是因为与所有其他语言相比,它在2018年获得了*多的排名。Python语言获得了3.62%的优势,其次是VisualBasic.NET和Java。Python现在肯定已经成为大型编程语言的一部分。近20年来,C、C+和Java一直排在前3名,遥遥*于其他方面。Python现在加入了这3种语言。它是当今大学教学*频繁的*语言,在统计领域排名*,在人工智能编程中排名*,在脚本编写方面排名*,在编写系统测试方面排名*。除此之外,Python在网络编程和科学计算方面也处于*地位。 曾经,Python被称为网红语言。大数据时代的到来,Python进入了人们的视线;人工智能的兴起,Python再次受人关注。语法简单,代码优美,Python开始被广泛的学习。Python凭借着社区强大的第三方库,已经被广泛的应用到各个领域中去,Python是脚本语言,Python是人工智能首选语言,Python是全栈式开发语言。总之,Python无处不在。

浏览器与服务器通信的过程

首先当用户在浏览器的地址栏中敲入了网站的网址 ( 比如: alibaba.com ) ,这时浏览器会首先通过访问的域名来定位到IP (DNS) 从而找到去哪里获取资源, 这时, 浏览器会依次进行如下查找:

1. 浏览器缓存 :

浏览器首先会在自己的缓存中查找有没有对应的域名 – IP匹配, 如果好运的话, 这里就可以直接尝试去访问资源了, 如果运气平平则往下走吧.

2. 系统缓存 :

浏览器缓存中没有命中, 浏览器会告诉操作系统:”嘿, 我在我自己口袋里没找到, 可能丢了, 我得去你那看看”, 然后, 一个系统进程(?)调取系统中的DNS缓存进行查询, 重复上一条的运气判断…

3. 路由器缓存 :

走到这, 运气还真不太好啊, 操作系统也没辙了, 那怎么办呢, 向路由去要要看吧… 重复运气判断…

4. ISP DNS缓存 :

好吧, 真不知道说运气好还是运气不好了, 不废话, 去ISP (网络提供商) 的DNS缓存服务器中寻找了, 一般情况下, 在ISP端的缓存中都能找到相应的缓存记录了, 不该这么背了, 或者… 您的ISP有够菜…

5. 递归搜索…

*无奈的情况发生了, 在前面都没有办法命中的DNS缓存的情况下, ISP的DNS服务器开始从root域名服务器开始进行递归, 顺序是从.com顶级域名服务器到alibaba的域名服务器, 再没找到…好吧, 您认为您要去的网站真的公开存在么…?

两个KEY POINT:

首先我们想想,我们要和接线员通话是不是要约定一个大家都能听得懂的语言,否则我说中文他说英语,这样就谁也听不懂谁的话,也不会完成通话,那么这个约定的语言是什么呢?那就是HTTP协议。

超文本传输协议(HTTP,HyperText Transfer Protocol) 是互联网上应用*为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP*初的目的是为了提供一种发布和接收HTML页面的方法。

我们可以把它理解成一种浏览器和服务器都遵循的一种语法规范,所有的信息都是通过这种语法规范传输的,这样浏览器和服务器都可以正确的理解。

浏览器和服务器一般并不是直接连接上的,而是需要通过中间的网络设备,就像我们的声音并不是直接传到接线员的耳朵里,而是要通过电话线通过电波传送一样,浏览器和服务器所发送的遵循HTTP协议的信息也要通过网络设备的传递才能被对方所接收。而这就需要一种在网络设备(网线)上传输数据的一种通用的语法规范(协议)。这样的协议使用*多的有2种:TCP协议和UDP协议

下面让我们来看看我们浏览网页的时候发生了什么吧。

1.首先我们在地址栏上输入我们想要打开的网址,然后我们通常会按下回车。这样一个请求就由浏览器以一种满足http协议的请求报文的形式发往服务器,请求报文中包含了要请求的页面地址,请求的文件类型等一系列信息。

2.在请求报文传递至客户端得网络设备的时候,网络设备把请求报文包装在一个满足TCP协议的数据中,通过网线传向服务器的网络设备。

3.服务器的网络设备接收到数据后,使用特殊的算法将数据解译,重新恢复成浏览器发出满足http协议的请求报文的形式,然后传向服务器软件。

4.服务软件得到请求报文后,根据请求报文所请求的页面地址在服务器的数据库中找到相应的页面,然后生成满足http协议的响应报文发向浏览器。响应报文中包括了响应报文头和被请求页面的代码(响应报文体)。

5.同样的,响应报文通过服务器的网络设备,被包装在一个满足TCP协议的数据,通过网线传向客户端的网络设备。

6.客户端的网络设备将响应报文解析,然后传给浏览器软件,浏览器在将响应报文解析,这样我们就在浏览器上看到了想要看到的网页。

B/S(Browser/Server)结构

就是浏览器/服务器结构,它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户工作界面是通过WWW浏览器来实现,*少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,形成所谓三层3-tier结构。本文将主要讲解浏览器和服务器通信的过程。

浏览器和服务器之间的通讯并不是看上去那么简单,里面还是有着许多的门道的。想要简单理解浏览器和服务器之间的通讯,我们可以打一个简单的比方:

设想我们自己就是浏览器,服务器就是10086语音服务台,我们现在想要和10086服务台取得联系(比如说想骚扰接线员MM),我们该怎么办呢?

当然,大多数人都应该想到,那就是要有部电话。是这样的,这就引出了我们的*个概念:套接字(Socket)

套接字在百度百科上的解释是:

多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。

这个解释太学术了,我们可以简单把套接字理解成一个电话,我们可以从中发送信息和获取信息。

好了,电话有了,我们该给10086服务台打电话了。等等,我们是不是忘了一点,给10086打电话是不是要保证10086服务台也要有一部电话来等待我们的来电呢?对的,那么就是说服务器端也要有一个套接字,专门接受浏览器的请求。一般当一个服务器启动服务后就会开启一个监听连接的套接字,专门等待浏览器连接,接受浏览器请求。

接下来我们要开始打电话了。大多数人都应该有给10086服务台打电话的经验吧,接通后并不是马上由接线员MM来接听你的电话的,而是根据语音提示选择你想要的服务。当你选择了语音服务以后,系统才会自动给你安排一个接线员MM来接听你的电话。那么这个过程是不是又生成了一个套接字呢?是的,没错,我们可以把呼叫系统(也就是给你语音提示,让你选择服务的系统)当成专门接受浏览器的请求的那个套接字,当浏览器发送了一个请求后系统自动生成一个专门和你的浏览器通信的套接字,这样我们就可以和接线员MM通话了,浏览器也就可以和服务器通信了。

当然这远远不是B/S结构的全部,我们还需要进一步深化:

首先我们想想,我们要和接线员通话是不是要约定一个大家都能听得懂的语言,否则我说中文他说英语,这样就谁也听不懂谁的话,也不会完成通话,那么这个约定的语言是什么呢?那就是HTTP协议。

还是先来看百度百科上的解释:

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用*为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP*初的目的是为了提供一种发布和接收HTML页面的方法。

我们就可以把它理解成一种浏览器和服务器都遵循的一种语法规范,所有的信息都是通过这种语法规范传输的,这样浏览器和服务器都可以正确的理解。

浏览器和服务器一般并不是直接连接上的,而是需要通过中间的网络设备,就像我们的声音并不是直接传到接线员的耳朵里,而是要通过电话线通过电波传送一样,浏览器和服务器所发送的遵循HTTP协议的信息也要通过网络设备的传递才能被对方所接收。而这就需要一种在网络设备(网线)上传输数据的一种通用的语法规范(协议)。这样的协议使用*多的有2种:TCP协议和UDP协议

TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

不管使用哪种方法,总之这样网络设备间也有了一套传输的协议,这样一来,浏览器和服务器才能真正的实现通信。

下面让我们来看看我们浏览网页的时候发生了什么吧。

1.首先我们在地址栏上输入我们想要打开的网址,然后我们通常会按下回车。这样一个请求就由浏览器以一种满足http协议的请求报文的形式发往服务器,请求报文中包含了要请求的页面地址,请求的文件类型等一系列信息。

2.在请求报文传递至客户端得网络设备的时候,网络设备把请求报文包装在一个满足TCP协议的数据中,通过网线传向服务器的网络设备。

3.服务器的网络设备接收到数据后,使用特殊的算法将数据解译,重新恢复成浏览器发出满足http协议的请求报文的形式,然后传向服务器软件。

4.服务软件得到请求报文后,根据请求报文所请求的页面地址在服务器的数据库中找到相应的页面,然后生成满足http协议的响应报文发向浏览器。响应报文中包括了响应报文头和被请求页面的代码(响应报文体)。

5.同样的,响应报文通过服务器的网络设备,被包装在一个满足TCP协议的数据,通过网线传向客户端的网络设备。

6.客户端的网络设备将响应报文解析,然后传给浏览器软件,浏览器在将响应报文解析,这样我们就在浏览器上看到了想要看到的网页。

以上只是浏览器与服务器之间通信的*简单的形式,实际使用中,一个网页往往包含着html代码,js代码,css样式表,图片等等多种数据,而这些数据并不是一次性由服务器传给浏览器的,而是存在着一定的顺序。首先服务器收到浏览器的请求后会将html代码发给浏览器,浏览器收到响应后会解析响应报文,发现html代码中包含着js代码和css样式表,而浏览器端并没有这些数据,所以浏览器会再次发送请求,向服务器请求js代码或css样式表数据(注意一次只能请求一种类型的数据),服务器收到请求后,会根据浏览器的请求再次找到数据库中的js文件或css样式表,将其发送到浏览器端。当网页中包含图片的时候也是同理。就这样循环往复,经过多次浏览器的请求和服务器的响应,当浏览器发现自己已经有了所有需要的数据后,会停止发送请求,这样一个完整的网页就呈现在我们眼前了。

以上只是浏览器与服务器之间通信的大致过程,要想详细了解其中门道还请各位参考更详细的文章。