本文从四个方面来讨论WEB开发的本质:
1、WEB的开发模式是什么样的?
2、什么是HTTP协议
3、WEB框架的底层是怎么实现的?
4、什么是Socket?
一、WEB开发模式
从开发模式这个维度来看,WEB开发分为前端和后端。如何区分前端和后端呢?其实可以用代码的运行环境来区分。前端人员写的代码运行在浏览器端,而后端写的代码运行在服务器端。以代码的运行环境作为边界,诞生了前后端分离的开发模式。这种细致的分工能够使开发人员更专注自己的领域,从而提升WEB开发效率。
如上图所示,前后端开发者互相配合,前端通过后端提供的接口发起请求,后端服务接收请求做出响应并返回给前端,返回的数据通过浏览器渲染展示给最终用户。
上文的图像及描述是对WEB开发模式的高层面抽象,下面我们来丰富其内部的细节。
大多数后端都要和数据库打交道,后端(server端)从数据库拿到原始数据,通过某种后端语言(比如java、php、python)实现的程序,对原始数据进行处理后返回给前端。
前端(client端)拿数据之后,在客户端进行渲染生成页面呈现给用户。前端的JavaScript在这里主要负责页面交互和发起请求等。HTML和CSS负责页面骨架和样式。
图中还缺少获取静态资源的服务,比如从Nginx获取JavaScript、HTML、CSS等静态资源。
下面我们从协议的角度来了解WEB开发的本质。
二、HTTP协议:
HTTP叫做超文本传输协议,是一个客户端与服务器端通信交互的协议,它是一种基于请求和与响应、无状态的、应用层的协议,默认端口是80。下面会对几个关键词做出解释。
有些小伙伴可能对协议这个词不是很清晰,简单来讲协议就是各服务之间通信遵守的约定及规则。
请求与响应:很好理解,而且上文已经通过图形说明了。
无状态:每一次请求和响应不具备上下文关联关系,彼此独立。
应用层:每个应用层协议都是为了解决某一类应用问题,而这种问题的解决又必须通过位于不同主机中的多个应用进程之间的通信和协同工作完成。应用进程之间的这种通信情况必须严格遵守这些规则。
所以应用层的具体内容就是精确定义这些通信规则,定义的内容大致有:
- 应用进程交换的报文类型、比如请求报文和响应报文
- 各种报文的语法,如报文中各个字段及其详细描述
- 字段的语义,即包含在字段中的信息的含义
- 进程何时、如何发送报文,以及对报文进行响应的规则。
请求、响应报文的模板图
请求:
请求报文
响应:
响应报文
实例1:Json
通过wireshark抓包分析请求响应报文
实例2:html
待截图todo
了解了协议,下面我们来看看WEB框架的底层逻辑。
三、WEB框架的底层逻辑:
所有的WEB应用的背后都是socket服务,我们平常用的浏览器就是socket客户端,了解到这一层面我们自己就可以实现一个web框架了。
用python实现最简化的WEB服务:- import socket
- # 创建套接字
- sock = socket.socket()
- #绑定地址(ip 端口号)
- sock.bind(('127.0.0.1', 80))
- #监听
- sock.listen()
- while True:
- # 等待客户端的连接
- conn, addr = sock.accept()
- # 接收数据
- data = conn.recv(8096)
- #在浏览器访问1ocalhost:80的时候,会得到响应OK
- conn.send(b'ok')
- conn.close()
复制代码 python3 web_framework.py 执行后浏览器访问
浏览器中返回代码中的send ok
用户的浏览器输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?
答案就是我们上文提到的HTTP协议。
首先让我们来打印下服务端接收到的消息:
看到这是不是想起了我们上文提到的报文模板了?对,WEB服务本质上就是依据HTTP报文模板解析请求、响应的字符串。
WEB框架如何根据不同的url路径来返回不同的内容呢?- import socket
- # 创建套接字
- sock = socket.socket()
- #绑定地址(ip 端口号)
- sock.bind(('127.0.0.1', 80))
- #监听
- sock.listen()
- while True:
- # 等待客户端的连接
- conn, addr = sock.accept()
- # 接收数据
- data = conn.recv(8096)
- data = str(data, encoding='utf-8')
- # 按照\r\n分割获取请求行
- # GET /index HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: keep-alive\r\n
- requestLine = data.split('\r\n')[0]
- # 使用空格分割获取url路径/index
- # GET /index HTTP/1.1
- url_path = requestLine.split(' ')[1]
- # 先发响应行
- conn.send(b'HTTP/1.1 200 OK \r\n\r\n')
- # 根据不同的url路径返回不同内容
- if '/' == url_path:
- conn.send(b'root path request')
- elif '/index' == url_path:
- conn.send(b'index path request')
- else:
- conn.send(b'404 not found')
- conn.close()
复制代码
离框架更进一步,不同的路径,对应不同的处理方法:- import socket
- sock = socket.socket()
- sock.bind(('127.0.0.1', 8000))
- sock.listen()
- def index(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding='utf-8')
- def home(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding='utf-8')
- func_urls = [
- ('/index/', index),
- ('/home/', home),
- ]
- while True:
- conn, addr = sock.accept()
- data = conn.recv(8096)
- data = str(data, encoding='utf-8')
- requestLine = data.split('\r\n')[0]
- url = requestLine.split(' ')[1]
- # 路径和函数的对应关系
- func = None
- for i in func_urls:
- if i[0] == url:
- func = i[1]
- break
- if func:
- response = func(url)
- else:
- response = b'404 not found!'
- conn.send(b'HTTP/1.1 200 OK')
- conn.send(response)
- conn.close()
- sock.close()
复制代码 认真看完样例代码你就掌握了WEB框架的底层逻辑。
接下来我们来简单看看socket的定义:
4、Socket定义
Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。
socket图解
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口 |