zoukankan      html  css  js  c++  java
  • 010-Python-socket编程

    客户端/服务器的架构

    物理层:网卡,光缆,双绞线

    数据链路层:包含源mac地址和目标的mac地址,通过广播通讯

    网络层:跑的IP协议,IP地址可以定义到一个子网;通过ARP协议可以解析为mac地址;

    传输层:提供端口 0-65535 TCP/UDP基于端口传输的

    ====》socket抽象层{抽象一堆接口,只要遵循socket标准,就支持TCP/UDP基于端口传输}

    应用层:软件

    客户端与服务端的连接:

    1.服务端连接关键字注释:

    import socket
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    server.bind(("127.0.0.1", 8080))  # 开启 绑定IP以及端口
    server.listen(5)  # 监听;进程池的大小5
    
    # conn 三次握手建立的连接, addr 客户端地址 端口
    conn, addr = server.accept()
    print("TCP 的链接", conn)
    print("客户端的地址和端口", addr)
    
    # conn.recv 接收消息;1024 代表接收到 1024个字节 (二进制格式) secv
    data = conn.recv(1024)
    print("来自于客户端的消息: %s" % data)  # 打印输出内容
    
    # 发消息 给 客户端 send  (将接收的消息转换为大写返回给客户端 二进制)
    conn.send(data.upper())
    
    conn.close()  # 关闭连接
    server.close()   # 结束套接字
    

    2.客户端连接关键字注释:

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    client.connect(("127.0.0.1", 8080))    # 与服务端进行连接;
    client.send("Hello".encode("utf-8"))    # 客户端发消息给 服务端 send 需要二进制 encode
    
    # 客户端收取 服务端发过来的消息
    data = client.recv(1024)   
    print("来自于服务端的数据:%s" % data)
    
    client.close()   # 关闭客户端
    

    输出内容:

    # 服务端内容:
    TCP 的链接 <socket.socket fd=284, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 61799)>
    客户端的地址和端口 ('127.0.0.1', 61799)
    来自于客户端的消息: b'Hello'
    
    # 客户端内容:
    来自于服务端的数据:b'HELLO'
    

    解决粘包问题:

    1.服务端解决粘包问题:

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Date: 2017/3/4
    
    import socket
    import subprocess   # 远程执行命令模块
    import struct
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 重用ip和端口,在绑定IP 端口之前 bind 之前
    server.bind(("127.0.0.1", 8080))  # 开启 绑定IP以及端口
    server.listen(5)  # 监听;进程池的大小5
    
    while True:         # 循环连接  循环
        # conn 三次握手建立的连接, addr 客户端地址 端口
        conn, addr = server.accept()
        print("客户端地址:", addr)
    
        while True:     # 消息传输 循环
            # conn.recv 接收消息;1024 代表接收到 1024个字节 (二进制格式) secv
    
            try:        # 捕捉异常
                cmd = conn.recv(1024)   # recv 会导致堵塞
    
                if not cmd:
                    break  # 针对Linux 客户端断开后 进入死循环现象;
    
                print("来自于客户端的消息: %s" % cmd)  # 打印输出内容
                # 发消息 给 客户端 send  (将接收的消息转换为大写返回给客户端 二进制)
    
                res = subprocess.Popen(cmd.decode("utf-8"),    # 使用 utf-8 解码
                                 shell=True,                 # 使用shell 解析
                                 stdout=subprocess.PIPE,     # 标准输出定向到 管道
                                 stderr=subprocess.PIPE)     # 标准错误输出 定向到 管道
    
                err = res.stderr.read()
                if err:
                    back_msg = err
                else:
                    back_msg = res.stdout.read()
    
    #####解决粘包部分#####
                conn.send(struct.pack("i", len(back_msg)))  # 计算出 返回值的长度
                conn.sendall(back_msg)   # sendall将数据循环的返回 将得到的数据 输出返回
    #####↑↑↑↑↑↑↑↑↑↑↑#####
    
            except Exception:   # 如果发现 异常,直接break
                break
    
        conn.close()  # 关闭连接
    server.close()   # 结束套接字
    

    2.客户端解决粘包问题

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Date: 2017/3/4
    
    import socket
    import struct
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    
    client.connect(("127.0.0.1", 8080))    # 与服务端进行连接;
    
    
    while True:
        cmd = input("输入你的命令:").strip()
        if not cmd:
            continue
    
        client.send(cmd.encode("utf-8"))
    #####解决粘包部分#####
        data = client.recv(4)
        data_size = struct.unpack("i", data)[0]
    
        recv_size = 0       # 收取数据的尺寸
        recv_bytes = b""    # 收到的bytes 用于拼接
        while recv_size < data_size:
            res = client.recv(1024)
    
            recv_bytes += res
            recv_size += len(res)
    #####↑↑↑↑↑↑↑↑↑↑↑#####
    
        print(recv_bytes.decode("gbk"))
    

    当返回值过大时,通过自定义报头方式解决粘包

    1.自定义报头,发送方服务端配置

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Date: 2017/3/4
    
    import socket
    import subprocess   # 远程执行命令模块
    import struct
    import json
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 重用ip和端口,在绑定IP 端口之前 bind 之前
    server.bind(("127.0.0.1", 8080))  # 开启 绑定IP以及端口
    server.listen(5)  # 监听;进程池的大小5
    
    while True:         # 循环连接  循环
        # conn 三次握手建立的连接, addr 客户端地址 端口
        conn, addr = server.accept()
        print("客户端地址:", addr)
    
        while True:     # 消息传输 循环
            # conn.recv 接收消息;1024 代表接收到 1024个字节 (二进制格式) secv
    
            try:        # 捕捉异常
                cmd = conn.recv(1024)   # recv 会导致堵塞
    
                if not cmd:
                    break  # 针对Linux 客户端断开后 进入死循环现象;
    
                print("来自于客户端的消息: %s" % cmd)  # 打印输出内容
                # 发消息 给 客户端 send  (将接收的消息转换为大写返回给客户端 二进制)
    
                res = subprocess.Popen(cmd.decode("utf-8"),    # 使用 utf-8 解码
                                 shell=True,                 # 使用shell 解析
                                 stdout=subprocess.PIPE,     # 标准输出定向到 管道
                                 stderr=subprocess.PIPE)     # 标准错误输出 定向到 管道
    
                err = res.stderr.read()
                if err:
                    back_msg = err
                else:
                    back_msg = res.stdout.read()
    # ########将返回长度记录到字典当中,分三次将所有数据发出
                # 制作字典
                head_dic = {
                    "data_size": len(back_msg)
                }
    
                head_json = json.dumps(head_dic)            # 将字典依照 json 格式存储
                head_bytes = head_json.encode("utf-8")      # 将json 格式 解码为 二进制bytes格式
    
                conn.send(struct.pack("i", len(head_bytes)))   # 第一次发:将计算的报头head_bytes长度发送给客户端
                conn.send(head_bytes)    # 第二次发:发送报头的数据
    
                conn.sendall(back_msg)   # 第三次发:sendall将数据循环的返回 将得到的数据 输出返回
    
            except Exception:   # 如果发现 异常,直接break
                break
    # ######↑↑↑↑↑↑↑↑↑↑↑↑↑
    
        conn.close()  # 关闭连接
    server.close()   # 结束套接字
    

    2.自定义报头,接收客户端配置

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Date: 2017/3/4
    
    import socket
    import struct
    import json
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 基于网络通信,是TCP协议的通讯
    client.connect(("127.0.0.1", 8080))    # 与服务端进行连接;
    
    while True:
        cmd = input("输入你的命令:").strip()
        if not cmd:
            continue
    
        client.send(cmd.encode("utf-8"))
    
    # 收取 服务端返回的数据:分三次收取
        # 1.收报头的长度
        head = client.recv(4)                     # 收取报头 二进制格式"b'x11x00x00x00'"
        head_size = struct.unpack("i", head)[0]  # 取出报头长度 int "17"
    
        # 2.收报头:根据报头的长度
        head_bytes = client.recv(head_size)       # 根据报头长度head_size 收取报头 二进制格式"b'{"data_size": 61}'"
        head_json = head_bytes.decode("utf-8")    # 将bytes的格式解码为 json的字符串 "'str'{"data_size": 61}"
    
        head_dic = json.loads(head_json)          # 将json字符串转换为 字典 "'dict'{'data_size': 61}"
        data_size = head_dic["data_size"]         # 取出真实数据的长度 "61"
    
        # 3.根据长度-收取真实数据
        recv_size = 0       # 收取数据的尺寸
        recv_bytes = b""    # 收到的bytes 用于拼接
        while recv_size < data_size:
            res = client.recv(1024)
    
            recv_bytes += res
            recv_size += len(res)
        print(recv_bytes.decode("gbk"))
    
  • 相关阅读:
    福大软工1816 · 第一次作业
    Python学习
    实验12——指针的基础应用2
    实验11——指针的基础应用
    实验十——一维数组的定义及引用
    实验九——基本数据类型存储及应用总结
    实验八——函数定义及调用总结
    实验七——函数定义及调用总结
    实验六——循环结构程序练习总结
    实验五——循环结构学习总结
  • 原文地址:https://www.cnblogs.com/baolin2200/p/6529926.html
Copyright © 2011-2022 走看看