一、web框架
web框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以快速开发特定的系统。他山之石,可以攻玉。python的所有web框架,都是对socket进行封装的。
web应用本质上是一个socket服务端,用户的浏览器是一个socket客户端。socket处在应用层与传输层之间,是操作系统中I/O系统的延伸部分(接口),负责系统进程和应用之间的通信。【python网络编程基础】
上面这个解释看起来有点费劲。重新解释一遍:
socket是在应用层和传输层之间的一个抽象层,扮演“信使”角色。它把tcp/ip层复杂的操作抽象为几个简单的接口以供应用层调用,从而实现在网络中通信。
django是python web开发的主流框架,另外还有flask和tensorflow。django框架必须掌握,要学精通。
web应用的流程:
//浏览器发送一个HTTP请求;//服务器收到请求,根据请求信息,进行函数处理,生成一个HTML文档;//服务器把HTML文档作为HTTP响应的Body发送给浏览器;//浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示;
回顾一下socket的udp连接和tcp连接,注:查看当前进程和杀死进程的window命令:
netstat -ano|findstr 45678
taskkill -PID 45678的进程 -F
udp客户端和服务端
import socket# upd链接# SOCK_DGRAM:数据报套接字,主要用于UDP协议udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 关闭防火墙# 同一网段(局域网)下,主机的ip地址和端口号.sendAddr = (‘192.168.10.247‘, 8080)# 绑定端口:写的是自己的ip和固定的端口,一般是写在sever端.udpSocket.bind((‘‘, 9900))# sendData = bytes(input(‘请输入要发送的数据:‘), ‘gbk‘)# gbk, utf8, strsendData = input(‘请输入要发送的数据:‘).encode(‘gbk‘)# python3是unicode编码,也就是以unicode格式写成的字节.# encode,重写编码:就是把unicode环境下的数据,重新编码成指定格式的字节,比如gbk, utf8等.然后接收方以同样的解码格式解码.# 用网络串口助手作为udp的服务端.网络串口助手是字节和十六进制的字节,没有unicode编码,有gbk编码和utf8编码.所以要解码成gbk# 反过来,接收数据的时候,也要知道对方发送的数据要怎么解码.# 使用udp发送数据,每一次发送都需要写上接收方的ip地址和端口号udpSocket.sendto(sendData, sendAddr)# udpSocket.sendto(b‘hahahaha‘, (‘192.168.10.247‘, 8080))udpSocket.close()
import socketudpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 接收方一般需要绑定端口# ‘‘表示自己电脑的任何一个ip,即无线和有限同时连接或者电脑有不同的网卡(桥接),会有多个ip.# 绑定自己的端口bindAddr = (‘‘, 7788)udpSocket.bind(bindAddr)recvData = udpSocket.recvfrom(1024)# print(recvData)print(recvData[0].decode(‘gbk‘))udpSocket.close()# recvData的格式:(data, (‘ip‘, 端口)).它是一个元组,前面是数据,后面是一个包含ip和端口的元组.
tcp客户端和服务端
import sockettcpClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)serverAddr = (‘192.168.10.247‘, 8899)# tcp的三次握手,写进了这一句话tcpClient.connect(serverAddr)sendData = input(‘‘)# 直接用send就行了,udp是用sendtotcpClient.send(sendData.encode(‘gbk‘))recvData = tcpClient.recv(1024)print(‘接收到的数据为:%s‘ % recvData.decode(‘gbk‘))tcpClient.close()# 为什么用send而不是sendto?因为tcp连接是事先链接好了,后面就直接发就行了。前面的connect已经连接好了,后面直接用send发送即可。# 而udp必须用sendto,是发一次数据,连接一次。必须要指定对方的ip和port。# 相同的道理,在tcpServer端,要写recv,而不是recvfrom来接收数据
import sockettcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcpServer.bind((‘‘, 8899))tcpServer.listen(5)# tcp的三次握手,写进了这一句话当中tcpClient, tcpClientInfo = tcpServer.accept()# tcpServer.accept(),不需要写ip,可以接收多个客户端的。但事先要绑定端口和接入的客户端的数量# client 表示接入的新的客户端# clientInfo 表示接入的新的客户端的ip和端口portrecvData = tcpClient.recv(1024)print(‘%s: %s‘ % (str(tcpClientInfo), recvData.decode(‘gbk‘)))# tcp的四次握手,写进了这一句话tcpClient.close()tcpServer.close()# tcpServer.accept():等待客户端的接入,自带堵塞功能:即必须接入客户端,然后往下执行# tcpClient.recv(1024): 也是堵塞,不输入数据就一直等待,不往下执行.# tcpServer创建了两个套接字,一个是Server,另一个是tcpClient.Server负责监听接入的Client,再为其创建专门的tcpClient进行通信.
两个小实例:tcpServer开启循环模式,以及udp的多线程聊天室
import socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)Server.bind((‘‘, 9000))Server.listen(10)while True: ???# 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务 ???serverThisClient, ClientInfo = Server.accept() ???print(‘Waiting connect......‘) ???# 如果客户发送的数据是空的,那么断开连接 ???while True: ???????recvData = serverThisClient.recv(1024) ???????if len(recvData) > 1: ???????????print(‘recv: %s‘ % recvData.decode(‘gbk‘)) ???????????sendData = input(‘send: ‘) ???????????serverThisClient.send(sendData.encode(‘gbk‘)) ???????else: ???????????print(‘再见!‘) ???????????break ???serverThisClient.close()
from threading import Threadimport socket# 收数据,然后打印def recvData(): ???while True: ???????recvInfo = udpSocket.recvfrom(1024) ???????print(‘%s:%s‘ % (str(recvInfo[1]), recvInfo[0].decode(‘gbk‘)))# 检测键盘,发数据def sendData(): ???while True: ???????sendInfo = input(‘‘) ???????udpSocket.sendto(sendInfo.encode(‘gbk‘), (destIp, destPort))udpSocket = NonedestIp = ‘‘destPort = 0# 多线程def main(): ???global udpSocket ???global destIp ???global destPort ???destIp = input(‘对方的ip: ‘) ???destPort = int(input(‘对方的端口:‘)) ???udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ???udpSocket.bind((‘‘, 45678)) ???tr = Thread(target=recvData) ???ts = Thread(target=sendData) ???tr.start() ???ts.start() ???tr.join() ???ts.join()if __name__ == ‘__main__‘: ???main()
根据以上socket,可以写一个简单的web应用
import socketdef handle_request(client): ???buf = client.recv(1024) ????# 请求头 ???print(buf.decode(‘utf8‘)) ???client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8")) ??????# 响应头 ???client.send("<h1 style=‘color:red‘>Hello, yuan</h1>".encode("utf8")) ???# body数据 ???# 把上面的html写到当前文件夹下的html里,把上行代码换成下面代码,就是一个web基本的流程. ???# with open(‘hello.html‘, ‘rb‘) as f: ???# ????data = f.read() ???# client.send(data)def main(): ???sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ???sock.bind((‘localhost‘,8001)) ???sock.listen(5) ???while True: ???????connection, address = sock.accept() ???????handle_request(connection) ???????connection.close()if __name__ == ‘__main__‘: ???main()# 在浏览器中输入localhost:8001,显示红色字体的hello,yuan.# 在后台Terminal中,会输出buf的内容:‘‘‘GET / HTTP/1.1Accept: text/html, application/xhtml+xml, image/jxr, */*Accept-Language: zh-CNUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063Accept-Encoding: gzip, deflateHost: 127.0.0.1:8001Connection: Keep-AliveCookie: csrftoken=pW12OxqURLh8VgHwwR1TzlR65ubxDzZgBv9SJtXZZeKuqd38TWGyntT84gX29rtr‘‘‘
二、WSGI
接收HTTP请求,解析HTTP请求,发送HTTP响应(如上面显示的)涉及到TCP连接、HTTP原始请求和响应格式。它涉及到底层较为复杂的构造:
根据网络通信的OSI模型,应用层封装一次数据,包含http参数和数据(数据格式是HTML,CSS,协议是TLS、HTTP等),然后发给传输层;
传输层接收封装后的数据,再封装一次,加上tcp头,变成tcp头、http data, 然后发给网络层;
网络层接收封装后的数据,再封装一次,加上ip头,变成ip头、tcp头、http data,然后发给数据数据链路层;
数据链路层接收封装后的数据,再封装一次,加上以太网头,变成ethernet头、ip头、tcp头、 http data,通过物理层对客户端浏览器进行响应。
这样,web服务端算是完成了一次响应。【详见OSI模型】
python内置了一个WSGI服务器(Web Server Gateway Interface),这个模块就是wigiref。它封装了对tcp、http请求和响应的操作。它负责处理socket的接口(封装了socket对象和准备过程(bind,listen等)),把请求信息(请求头和请求体)封装成键值对的对象,并可以方便地设置响应头和返回请求体。下面通过一个wsgi内置的写一个 web应用来加深理解wsg。i
step1:WSGI的environ和return
from wsgiref.simple_server import make_serverdef application(environ, start_response): ???for key, value in environ.items(): ???????print(key, value) ???# 响应头,告诉浏览器响应的内容是什么格式 ???start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???return [b‘<h1>Hello, web!</h1>‘] ???# 响应体# make_server是一个http对象httpd = make_server(‘‘, 8080, application)print(‘Serving HTTP on port 8000 ...‘)# 开始监听HTTP请求:httpd.serve_forever()‘‘‘application()就是符合wSGI标准的一个HTTP处理函数,它接收两个参数: ???environ:一个包含所有HTTP请求信息的dict对象; ???start_response:一个发送HTTP响应的函数;在applicaiton()函数中调用start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]),就发送了HTTP响应的Header.start_response()函数接收两个参数: ???一个是HTTP响应码 ???一个是一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示. ???通常会把Content-Type头发送给浏览器,其他很多常用的HTTP Header也应该发送。然后,函数的返回值b‘<h1>Hello, web!</h1>‘将作为HTTP响应的Body发送给浏览器.有了WSGI,我们关心的就是如何从environ中拿到解析好的请求信息,从而写自己的处理逻辑了。‘‘‘
ALLUSERSPROFILE C:\ProgramDataAPPDATA C:\Users\admin\AppData\RoamingCOMMONPROGRAMFILES C:\Program Files\Common FilesCOMMONPROGRAMFILES(X86) C:\Program Files (x86)\Common FilesCOMMONPROGRAMW6432 C:\Program Files\Common FilesCOMPUTERNAME DESKTOP-ABISCDMCOMSPEC C:\WINDOWS\system32\cmd.exeFPS_BROWSER_APP_PROFILE_STRING Internet ExplorerFPS_BROWSER_USER_PROFILE_STRING DefaultHOMEDRIVE C:HOMEPATH \Users\adminLOCALAPPDATA C:\Users\admin\AppData\LocalLOGONSERVER \\DESKTOP-ABISCDMMOZ_PLUGIN_PATH C:\其它软件\福昕阅读器\Foxit Reader\pluginsNUMBER_OF_PROCESSORS 4ONEDRIVE C:\Users\admin\OneDriveOS Windows_NTPATH C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Zpythons\python36\Scripts\;C:\Zpythons\python36\;C:\Users\admin\AppData\Local\Microsoft\WindowsApps;PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSCPROCESSOR_ARCHITECTURE AMD64PROCESSOR_IDENTIFIER Intel64 Family 6 Model 142 Stepping 9, GenuineIntelPROCESSOR_LEVEL 6PROCESSOR_REVISION 8e09PROGRAMDATA C:\ProgramDataPROGRAMFILES C:\Program FilesPROGRAMFILES(X86) C:\Program Files (x86)PROGRAMW6432 C:\Program FilesPSMODULEPATH C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\ModulesPT5HOME C:\Cisco Packet Tracer 6.0PT6HOME C:\Cisco Packet Tracer 6.0PUBLIC C:\Users\PublicPYCHARM_HOSTED 1PYTHONIOENCODING UTF-8PYTHONPATH C:\Users\admin\Desktop\pythonNote\网络编程基础PYTHONUNBUFFERED 1SESSIONNAME ConsoleSYSTEMDRIVE C:SYSTEMROOT C:\WINDOWSTEMP C:\Users\admin\AppData\Local\TempTMP C:\Users\admin\AppData\Local\TempUSERDOMAIN DESKTOP-ABISCDMUSERDOMAIN_ROAMINGPROFILE DESKTOP-ABISCDMUSERNAME adminUSERPROFILE C:\Users\adminVS140COMNTOOLS C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\ToolsWINDIR C:\WINDOWSSERVER_NAME DESKTOP-ABISCDMGATEWAY_INTERFACE CGI/1.1SERVER_PORT 8080REMOTE_HOST CONTENT_LENGTH SCRIPT_NAME SERVER_PROTOCOL HTTP/1.1SERVER_SOFTWARE WSGIServer/0.2REQUEST_METHOD GETPATH_INFO /alexQUERY_STRING REMOTE_ADDR 127.0.0.1CONTENT_TYPE text/plainHTTP_HOST 127.0.0.1:8080HTTP_USER_AGENT Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8HTTP_ACCEPT_LANGUAGE zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3HTTP_ACCEPT_ENCODING gzip, deflateHTTP_COOKIE csrftoken=b2CBBrcSjnRT91jAB1KJzUWphnWB28UiLrHQuECAsv6vWPy4vGWIsHXkRHWh0gTy; sessionid=y4l1ybo6t01nhpqsjkh6fyqm44blincaHTTP_CONNECTION keep-aliveHTTP_UPGRADE_INSECURE_REQUESTS 1HTTP_CACHE_CONTROL max-age=0wsgi.input <_io.BufferedReader name=748>wsgi.errors <_io.TextIOWrapper name=‘<stderr>‘ mode=‘w‘ encoding=‘UTF-8‘>wsgi.version (1, 0)wsgi.run_once Falsewsgi.url_scheme httpwsgi.multithread Truewsgi.multiprocess Falsewsgi.file_wrapper <class ‘wsgiref.util.FileWrapper‘>
step2:根据浏览器输入的url,进行逻辑处理:
from wsgiref.simple_server import make_serverdef application(environ, start_response): ???# 响应头,告诉浏览器响应的内容是什么格式 ???start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???# message = start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???# print(message) ???# 打印所有的请求信息 ???for key, value in environ.items(): ???????print(key, value) ???# 从environ中拿到url,键名是‘PATH_INFO‘,然后做一个处理: ???path = environ[‘PATH_INFO‘] ???f1 = open(‘index1.html‘, ‘rb‘) ???data1 = f1.read() ???#当前文件夹下创建index1.html和index2.html,内容随便写. ???f2 = open(‘index2.html‘, ‘rb‘) ???data2 = f2.read() ???if path == ‘/yuan‘: ???????return [data1] ???elif path == ‘/alex‘: ???????return [data2] ???else: ???????return [b‘<h1>Hello, web!</h1>‘] ???# 响应体# make_server是一个http对象httpd = make_server(‘‘, 8080, application)print(‘Serving HTTP on port 8000 ...‘)# 开始监听HTTP请求:httpd.serve_forever()
step3:把上述逻辑处理进行解耦:
from wsgiref.simple_server import make_serverdef f1(): ???f1 = open(‘index1.html‘, ‘rb‘) ???return [f1.read()]def f2(): ???f2 = open(‘index2.html‘, ‘rb‘) ???return [f2.read()]def application(environ, start_response): ???start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???path = environ[‘PATH_INFO‘] ???if path == ‘/yuan‘: ???????return f1() ???elif path == ‘/alex‘: ???????return f2() ???else: ???????return [b‘<h1>Hello, web!</h1>‘]httpd = make_server(‘‘, 8080, application)print(‘Serving HTTP on port 8000 ...‘)httpd.serve_forever()
step4:接近MVC的升级式写法:
import timefrom wsgiref.simple_server import make_serverdef f1(request): ???print(request[‘QUERY_STRING‘]) ???f1 = open(‘index1.html‘, ‘rb‘) ???return [f1.read()]def f2(request): ???f2 = open(‘index2.html‘, ‘rb‘) ???return [f2.read()]def f3(request): ???f3 = open(‘index3.html‘, ‘rb‘) ???localtime = time.strftime(‘%Y-%m-%d %X‘,time.localtime()) ???html = f3.read() ???print(html) ???html = str(html, ‘utf8‘).replace(‘!time!‘, str(localtime)) ???return [html.encode(‘utf8‘)]def routes(): ???urlpatterns = [ ???????(‘/yuan‘, f1), ???????(‘/alex‘, f2), ???????(‘/cur_time‘, f3), ???] ???return urlpatternsdef application(environ, start_response): ???path = environ[‘PATH_INFO‘] ???start_response(‘200 OK‘, [(‘Contentt-Type‘, ‘text/html‘)]) ???urlpatterns = routes() ???func = None ???for item in urlpatterns: ???????if item[0] == path: ???????????func = item[1] ???????????break ???if func: ???????return func(environ) ???else: ???????return [‘<h2>404</h2>‘.encode(‘utf8‘)]httpd = make_server(‘‘, 8001, application)print(‘Serving HTTP on port 8001 ...‘)httpd.serve_forever()# 在浏览器中输入localhost:8001/yuan,或/alex,或/cur_time,或一个不存在的url地址‘‘‘html=b‘<!DOCTYPE html>\r\n<html lang="en">\r\n<head>\r\n ???<meta charset="UTF-8">\r\n ???<title>Title</title>\r\n</head>\r\n<body>\r\n<h2>!time!</h2>\r\n</body>\r\n</html>‘要点一: ???f3中的html,只写了个h2标签,里面写了!time!这个单词。 ???f3跟f1,f2的不同之处在于,它对html进行了修改操作(访问一次,打印一次当前时间戳)。 ???假如定义!time!是一个固定的“语言”,像!vector!,!func!等,都作为“自定义”的python处理方式,那么就可以识别该操作符,然后进行处理。 ???django定义{{ 变量/函数 }} 和{% 变量/函数/简单循环 %},来与html进行交互。 ???????它扩展为django中的render功能,能够支持对html文件传入参数。以及templatetags,用来在后端给html增加一些功能。 ???要点二: ???rountes函数:在list列表中存放每个url和相应的处理函数的tuple,绑定url和处理函数。 ???每当有url访问,就对这个List对象进行遍历,找到处理函数就返回相应的结果,没找到就返回一个404错误。 ???它相当于django中的url.py文件。 ???????applicattion()函数中写了多行代码来处理url和func的映射关系。 ???在django中,它被封装了起来, 用户只需要在urlpatterns中添加url和处理函数。然后在views中写处理逻辑就行了。‘‘‘
如果把f1(),f2(),f3()写在一个models.py中,把routes写进一个urls.py中,把application写在controller.py中,把html写在一个view文件夹下。那就构成了web框架中著名的MVC模式。
三、web框架的MVC模式和django的MTV模式
大部分开发语言中都有MVC框架,MVC框架的核心思想是:解耦。上面WSGI的升级版写法已经体现了这个特点。
所谓MVC,就是指model、view、controller。m主要用于对数据库层的封装,v主要向用户展示结果,c是核心,用于处理请求、获取数据、返回结果。
django是一款python的web开发框架,使用了MVT模式,它本质上与MVC模式没什么差别。MVT是model, template, view。m负责业务对象与数据库的对象(ORM), template负责如何把页面展示给用户,view负责业务逻辑,并在适当的时候调用model和template。此外,还有一个url分发器,它的作用是将一个个url页面请求分发给不同的view处理。
重新解释一遍:model负责与数据库交互,view是核心,负责接收请求,获取数据,返回结果,template负责呈现内容到浏览器。
http://www.cnblogs.com/yuanchenqi/articles/6083427.html# 老男孩python全栈工程师培训著名讲师苑昊博客
django基础一:web、wsgi、mvc、mtv
原文地址:http://www.cnblogs.com/kuaizifeng/p/7618117.html