• django基础一:web、wsgi、mvc、mtv


    一、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, str
    sendData = 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()
    udp客户端
    import socket
    
    udpSocket = 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和端口的元组.
    udp服务端

      tcp客户端和服务端

    import socket
    
    tcpClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    serverAddr = ('192.168.10.247', 8899)
    
    # tcp的三次握手,写进了这一句话
    tcpClient.connect(serverAddr)
    
    sendData = input('')
    
    # 直接用send就行了,udp是用sendto
    tcpClient.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来接收数据
    tcp客户端
    import socket
    tcpServer = 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和端口port
    
    recvData = 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进行通信.
    tcp服务端

      两个小实例:tcpServer开启循环模式,以及udp的多线程聊天室

    import socket
    
    Server = 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 Thread
    import 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 = None
    destIp = ''
    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 socket
    
    def handle_request(client):
        buf = client.recv(1024)     # 请求头
        print(buf.decode('utf8'))
        client.send("HTTP/1.1 200 OK
    
    ".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.1
    Accept: text/html, application/xhtml+xml, image/jxr, */*
    Accept-Language: zh-CN
    User-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.15063
    Accept-Encoding: gzip, deflate
    Host: 127.0.0.1:8001
    Connection: Keep-Alive
    Cookie: csrftoken=pW12OxqURLh8VgHwwR1TzlR65ubxDzZgBv9SJtXZZeKuqd38TWGyntT84gX29rtr
    '''
    最简单的web小程序

    二、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_server
    
    def 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中拿到解析好的请求信息,从而写自己的处理逻辑了。
    '''
    WSGI的simple_server
    ALLUSERSPROFILE C:ProgramData
    APPDATA C:UsersadminAppDataRoaming
    COMMONPROGRAMFILES C:Program FilesCommon Files
    COMMONPROGRAMFILES(X86) C:Program Files (x86)Common Files
    COMMONPROGRAMW6432 C:Program FilesCommon Files
    COMPUTERNAME DESKTOP-ABISCDM
    COMSPEC C:WINDOWSsystem32cmd.exe
    FPS_BROWSER_APP_PROFILE_STRING Internet Explorer
    FPS_BROWSER_USER_PROFILE_STRING Default
    HOMEDRIVE C:
    HOMEPATH Usersadmin
    LOCALAPPDATA C:UsersadminAppDataLocal
    LOGONSERVER \DESKTOP-ABISCDM
    MOZ_PLUGIN_PATH C:其它软件福昕阅读器Foxit Readerplugins
    NUMBER_OF_PROCESSORS 4
    ONEDRIVE C:UsersadminOneDrive
    OS Windows_NT
    PATH C:Program Files (x86)InteliCLS Client;C:Program FilesInteliCLS Client;C:WINDOWSsystem32;C:WINDOWS;C:WINDOWSSystem32Wbem;C:WINDOWSSystem32WindowsPowerShellv1.0;C:Program Files (x86)IntelIntel(R) Management Engine ComponentsDAL;C:Program FilesIntelIntel(R) Management Engine ComponentsDAL;C:Program Files (x86)IntelIntel(R) Management Engine ComponentsIPT;C:Program FilesIntelIntel(R) Management Engine ComponentsIPT;C:Program Files (x86)Windows Kits8.1Windows Performance Toolkit;C:Program FilesMySQLMySQL Utilities 1.6;C:Program FilesIntelWiFiin;C:Program FilesCommon FilesIntelWirelessCommon;C:pythonspython36Scripts;C:pythonspython36;C:UsersadminAppDataLocalMicrosoftWindowsApps;
    PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    PROCESSOR_ARCHITECTURE AMD64
    PROCESSOR_IDENTIFIER Intel64 Family 6 Model 142 Stepping 9, GenuineIntel
    PROCESSOR_LEVEL 6
    PROCESSOR_REVISION 8e09
    PROGRAMDATA C:ProgramData
    PROGRAMFILES C:Program Files
    PROGRAMFILES(X86) C:Program Files (x86)
    PROGRAMW6432 C:Program Files
    PSMODULEPATH C:Program FilesWindowsPowerShellModules;C:WINDOWSsystem32WindowsPowerShellv1.0Modules
    PT5HOME C:Cisco Packet Tracer 6.0
    PT6HOME C:Cisco Packet Tracer 6.0
    PUBLIC C:UsersPublic
    PYCHARM_HOSTED 1
    PYTHONIOENCODING UTF-8
    PYTHONPATH C:UsersadminDesktoppythonNote网络编程基础
    PYTHONUNBUFFERED 1
    SESSIONNAME Console
    SYSTEMDRIVE C:
    SYSTEMROOT C:WINDOWS
    TEMP C:UsersadminAppDataLocalTemp
    TMP C:UsersadminAppDataLocalTemp
    USERDOMAIN DESKTOP-ABISCDM
    USERDOMAIN_ROAMINGPROFILE DESKTOP-ABISCDM
    USERNAME admin
    USERPROFILE C:Usersadmin
    VS140COMNTOOLS C:Program Files (x86)Microsoft Visual Studio 14.0Common7Tools
    WINDIR C:WINDOWS
    SERVER_NAME DESKTOP-ABISCDM
    GATEWAY_INTERFACE CGI/1.1
    SERVER_PORT 8080
    REMOTE_HOST 
    CONTENT_LENGTH 
    SCRIPT_NAME 
    SERVER_PROTOCOL HTTP/1.1
    SERVER_SOFTWARE WSGIServer/0.2
    REQUEST_METHOD GET
    PATH_INFO /alex
    QUERY_STRING 
    REMOTE_ADDR 127.0.0.1
    CONTENT_TYPE text/plain
    HTTP_HOST 127.0.0.1:8080
    HTTP_USER_AGENT Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
    HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    HTTP_ACCEPT_LANGUAGE zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    HTTP_ACCEPT_ENCODING gzip, deflate
    HTTP_COOKIE csrftoken=b2CBBrcSjnRT91jAB1KJzUWphnWB28UiLrHQuECAsv6vWPy4vGWIsHXkRHWh0gTy; sessionid=y4l1ybo6t01nhpqsjkh6fyqm44blinca
    HTTP_CONNECTION keep-alive
    HTTP_UPGRADE_INSECURE_REQUESTS 1
    HTTP_CACHE_CONTROL max-age=0
    wsgi.input <_io.BufferedReader name=748>
    wsgi.errors <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
    wsgi.version (1, 0)
    wsgi.run_once False
    wsgi.url_scheme http
    wsgi.multithread True
    wsgi.multiprocess False
    wsgi.file_wrapper <class 'wsgiref.util.FileWrapper'>
    查看environ封装的所有信息

      step2:根据浏览器输入的url,进行逻辑处理:

    from wsgiref.simple_server import make_server
    
    def 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_server
    
    def 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 time
    from wsgiref.simple_server import make_server
    
    def 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 urlpatterns
    
    def 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>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h2>!time!</h2>
    </body>
    </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全栈工程师培训著名讲师苑昊博客
    原文链接地址
  • 相关阅读:
    Uva673 Parentheses Balance
    cordforce Educational Codeforces Round 47 补题笔记 <未完>
    cordforce 495 补题 <未完>
    linux 基本命令笔记
    app审核相关
    CATransform3D
    UITableView点击切换状态分析
    iOS大转盘抽奖
    被忽视的控件UIToolbar
    AVPlayerViewController视频播放器
  • 原文地址:https://www.cnblogs.com/kuaizifeng/p/7618117.html
Copyright © 2020-2023  润新知