zoukankan      html  css  js  c++  java
  • Python网络编程

    Socket是什么

    • Socket是 计算机 网络中进程间数据流的端点
    • Socket是操作系统的通信机制
    • 应用程序通过Socket进行网络数据的传输

    简单TCP过程

    客户端服务器端发送SYN报文,设置序号XSYN = 1 Seq = XACK = 0设置SYN+ACK报文,设置序号YSYN = 1 Seq = YACK = X+1发送ACK报文,设置序号XSeq = X + 1 ACK =Y+1客户端服务器端

    Scoket通信过程

    客户端服务器端socket,connectscoket,bind,listen发送SYN报文,设置序号Xaccept设置SYN+ACK报文,设置序号Yconnect发送ACK报文,设置序号Xaccept客户端服务器端

    Socket通信方式

    • Socket分为UDPTCP两种不同的通信方式。
    • 下面是socket的实现过程:

    Socket参数

    • family:地址簇(cu)

      • socket.AF_INET IPv4(默认)
      • socket.AF_INET6 IPv6
      • socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
    • type:类型

      • socket.SOCK_STREAM 流式socket,for TCP(默认)
      • socket.SOCK_DGRAM 数据报文方式socket,for UDP
      • socket.SOCK_RAM 原始套接字
      • socket.SOCK_RDM 可靠UDP形式
      • socket.SOCK_SEQPACKET 可靠的连续数据包服务
    • proto:协议号

      • 0:默认,可以省略
      • CAN_RAM或CAN_BCM:地址簇为AF_CAN时

    TCP/IPv4的一个例子

    以下两个程序:分别充当服务器端和客户端的角色:

    • socket_server.py
    • socket_client.py
    socket_server.py代码
    import socket
    import random
    
    sk = socket.socket()  # 创建实例
    ip_port = ("127.0.0.1", 8000)  # 定义&绑定ip和端口
    sk.bind(ip_port)  # 绑定监听
    sk.listen(5)  # 最大连接数
    while True:  # 不断循环不断接收数据
        # 提示信息
        print("正在等待一次连接...")
    
        # 接收数据
        conn, address = sk.accept()
        # 定义消息
        msg = "你好呀,初次建立连接。"
    
        # 返回信息
        # python3.x中,网络数据的发送和接收都是byte类型
        # 如果发送的数据是str型的则需要进行编码
        conn.send(msg.encode())
    
        while True:  # 不断接收客户端发来的消息,exit通知
            # 接收客户端的消息
            try:
                data = conn.recv(1024)
            except Exception as result:
                print("出现错误1:", result)
                break
            # 退出指令
            if data == b'exit':
                print("client主动关闭了连接.")
                break
            # 测试:打印出数据
            print("接收的数据是:", data.decode())
    
            # 处理客户端的数据并返回
            try:
                conn.send(("回声:[" + data.decode() + "],随机数[%s]" % random.randint(1, 1000)).encode())
            except Exception as result:
                print("出现错误2:", result)
                print("此次连接关闭")
                break
        # 主动关闭连接
        conn.close()
    
        # 提示信息
        print("这次连接已结束!")
    
    socket_client.py代码
    import socket
    
    # 实例初始化
    client = socket.socket()
    # 要访问的服务器ip和port
    ip_port = ("127.0.0.1", 8000)
    # 连接主机
    client.connect(ip_port)
    print("已和主机", ip_port[0], ':', ip_port[1], "建立连接信息...")
    while True:
        # 接收主机返回的消息
        data = client.recv(1024)
        # 打印接收的数据,在Python3.x中此处是byte型数据
        print("接收到的数据是:", data.decode())
    
        # 不断和主机发送信息
        # msg_input = input("输入要发送的信息:").strip()
        msg_input = ""
        while msg_input.isspace() or msg_input == "":  # 解决换行的问题
            msg_input = input("输入要发送的信息:")
        # 发送信息
        client.send(msg_input.encode())
        # 结束会话连接条件
        if msg_input == 'exit':
            break
        # 接收到的回复
        # print(client.recv(1024).decode())
    
    print("断开主机", ip_port[0], ':', ip_port[1], "的连接!")
    

    UDP/IPv4的一个例子

    • socket_server_udp.py
    • socket_client_udp.py

    socket_server_udp.py代码

    import socket
    
    # 创建实例:指定IPv4, UDP方式
    sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    ip_port = ("127.0.0.1", 8000)
    # 绑定监听
    sk.bind(ip_port)
    while True:
        # 接收数据
        data = sk.recv(1024)
        # 打印数据
        print(data.decode())
    

    socket_client_udp.py代码

    import socket
    
    ip_port = ("127.0.0.1", 8000)
    sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while True:
        msg_input = input("请输入发送的消息:")
        # 退出循环条件
        if msg_input == "exit":
            break
        # 数据发送
        sk.sendto(msg_input.encode(), ip_port)
    
    # 发送关闭信息...推荐主动关闭
    sk.close()
    

    socketserver的一个例子

    socket_server_tcp2.py代码

    import socketserver
    # 原因:多线程
    import random
    
    
    class MyServer(socketserver.BaseRequestHandler):
        # 下面三个方法被重写了, 三个方法的执行顺序从上到下
        # 如果handle方法出现报错,则会进行跳过
        # 但setup方法和finish方法无论如何都会被执行,所以只对handle方法重写
        def setup(self):
            pass
    
        def handle(self):
            # 定义连接变量
            conn = self.request
    
            # 发送消息定义
            msg = "Hello world"
            # 消息发送
            conn.send(msg.encode())
    
            # 不断接收客户端的消息
            while True:
                # 接收客户端消息
                data = conn.recv(1024)
                # 打印消息
                print(data.decode())
                if data == b"exit".strip():
                    break
    
                conn.send((data.decode() + '[' + str(random.randint(1, 1000)) + ']').encode())
    
            print("客户端主动断开连接!")
            conn.close()
    
        def finish(self):
            pass
    
    
    # def main():
    #     pass
    
    
    if __name__ == '__main__':
        # main()
        # 创建多线程实例
        server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), MyServer)
        # 开启异步多线程,连续等待
        server.serve_forever()
    

    Web端的一个例子

    socket实现:

    socket_browser.py文件

    # -*- coding:utf-8 -*-
    
    import socket
    
    
    def main():
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(("127.0.0.1", 8000))
        # host = socket.gethostname()
        # print("host:", host)
        # sock.bind((host, 8000))
        sock.listen(5)
    
        while True:
            # 等待浏览器访问
            conn, addr = sock.accept()
            # print("conn:", conn)  # <class 'socket.socket'>
            # print(type(conn))
            # # conn: <socket.socket fd=544, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 50395)>
            # print("addr:", addr)  # <class 'tuple'>
            # print(type(addr))
            # # addr: ('127.0.0.1', 50395)
    
            # 接收浏览器发送来的请求内容
            data = conn.recv(1024)
    
            # print("data:", data)
            # # data: b'GET / HTTP/1.1
    Host: 127.0.0.1:8000
    Connection: keep-alive
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    
    '
            # print(type(data))  # <class 'bytes'>
            # 给浏览器回复内容
            conn.send(b"HTTP/1.1 200 OK
    Content-Type:text/html; charset=utf-8
    
    ")
            conn.send("hello world".encode("utf-8"))
    
            #  关闭和浏览器创建的socket连接
            conn.close()
    
    
    if __name__ == '__main__':
        main()
    
    
    wsgi实现

    目录结构:

    D:.
    │  wsgi_server.py
    │
    └─static_data
        └─imgs
                haha.png
    

    wsgi_server.py文件

    # -*- coding:utf-8 -*-
    
    from wsgiref.simple_server import make_server
    import os
    import re
    
    # 基于WSGI开发一个web服务器
    
    
    # 1. 路由的分发器,负责把url匹配到对应的函数
    # 2. 开发好对应的业务函数
    # 3. 一个请求来了之后,先走路由分发器,如果找到对应的function就执行,如果没有找到就执行404
    
    def f1(environ, start_response):
        environ
        print("f1 Page")
        start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
        return [bytes('<h2>F1 Page</h2>', encoding='utf-8'), ]
    
    
    def f2(environ, start_response):
        environ
        print("f2 Page")
        start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
        return [bytes('<h2>F2 Page</h2>', encoding='utf-8'), ]
    
    
    def f3(environ, start_response):
        environ
        print("f3 Page")
        data = """
               <h1>图片展示:</h1>
               <img src='/static/imgs/haha.png' />
               <p>上面是一张图片</p>
           """
    
        start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
        return [bytes(data, encoding='utf-8'), ]
    
    
    def url_dispacher():
        """
        负责把url与对应的方法关联起来
        :return:
        """
        urls = {
            '/f1': f1,
            '/f2': f2,
            '/f3': f3,  # 注意f3是与定义的函数对应的,要搞懂,,,
        }
        return urls
    
    
    def img_handler(request_url):
        """
    
        :param request_url:  /static_data/imgs/haha.png
        :return:
        """
        # BASEE_DIR = os.path.abspath(__file__)  # BASEE_DIR: D:JetBrainsProjectPythonPySocketTest4wsgi_server.py
        # BASEE_DIR = os.path.dirname(BASEE_DIR)  # BASEE_DIR: D:JetBrainsProjectPythonPySocketTest4
        # base_dir = os.path.dirname(os.path.abspath(__file__))
        img_path = re.sub('/static', 'static_data', request_url)
        # print("img_path:", img_path)
        if os.path.isfile(img_path):
            # print("haha来了")
            f = open(img_path, "rb")
            data = f.read()
            f.close()
            # print("data:", data)
            return [data, 0]  # 0代表正确, 1代表错误
        return [None, 1]  # 发生错误
    
    
    def run_server(environ, start_response):
        """
        当有用户在浏览器上访问:http://127.0.0.1:8000/,立即执行该函数并将函数的返回值返回给用户浏览器
        :param environ:请求相关内容,比如浏览器类型、版本、来源地址、url等
        :param start_response:响应相关
        :return:
        """
        # print("environ:", environ)
        # print("start_response:", start_response)
        url_list = url_dispacher()  # 屎一样的代码
        request_url = environ.get("PATH_INFO")
        print("request_url:", request_url)  # /hhh
    
        if request_url in url_list:
            func_data = url_list[request_url](environ, start_response)
            return func_data  # 返回路由指定的内容
        elif request_url.startswith("/static/"):  # 图片
            # /static_data/imgs/haha.png
            # /static_data/代表是图片
            # print("有图片啦!")
            img_data, img_status = img_handler(request_url)
            if img_status == 0:  # 图片正确
                start_response('200 OK ', [('Content-Type', 'text/jpeg;charset=utf-8')])
                return [img_data, ]
            # print("但是图片没找到!!")
        else:
            start_response('404 ', [('Content-Type', 'text/html;charset=utf-8')])
            return [bytes('<h1>我是 Onefine.哈哈哈页面不存在!</h1>', encoding='utf-8'), ]
    
    
    if __name__ == '__main__':
        httpd = make_server('127.0.0.1', 8000, run_server)  # 注意第二个参数没搞懂
        httpd.serve_forever()
    

    总结

    虽然自己写Web Server比较麻烦,但是我们也从中了解到Web框架的本质:

    1. 浏览器是socket客户端,网站是socket服务端
    2. wsgi是一个规范,wsgiref实现了这个规范并在其内部实现了socket服务端
    3. 根据url的不同执行不同函数,即:路由系统
    4. 函数,即:处理业务逻辑
    5. 图片、css、js文件统一称为静态文件,需要读取内存直接返回给用户浏览器
  • 相关阅读:
    魅族多机房部署方案-tech_meizu-ChinaUnix博客
    环信首席架构师:一个单元化架构的例子-CSDN.NET
    双活数据中心解决方案(最新)_图文_百度文库
    阿里巴巴分布式数据库服务DRDS研发历程
    OpenDigg
    Qcon
    有赞应用层网关剖析
    Enterprise Architect-工具-火龙果软件
    (80 条消息) 哪些管理类的书籍值得推荐?
    【图文】拉姆查兰-领导梯队_百度文库
  • 原文地址:https://www.cnblogs.com/onefine/p/10499380.html
Copyright © 2011-2022 走看看