• 模块scoket小试牛刀


    08.13笔记

    socket(套接字)模块

    # 收发消息的工具
    ​
    # sk.setblocking(False)    设置非阻塞(什么accept,recv,不阻塞,报错就异常处理)
    

      

    1, TCP协议基本语法

    TCP(Transmission Control Protocol)一种面向连接的、可靠的、传输层通信协议(比如:打电话)
    # 三次握手:三次信息的传递
    # 四次挥手:通过与请求不可以合并
    优点:可靠,稳定,传输完整稳定,不限制数据大小
    缺点:慢,效率低,占用系统资源高,一发一收都需要对方确认
    应用:Web浏览器,电子邮件,文件传输,大量数据传输的场景
    

      

    1.1 服务端基本套路

    import socket
    ​
    sk = socket.socket()                                                     # 默认创建TCP协议对象
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)                   # 测试时加上这句话,端口可以重复利用
    sk.bind(("127.0.0.1",3008))                                              # 绑定 ip+端口号
    sk.listen()                                                              # 开始监听
    while True:
        conn,cil_appr = sk.accept()                                          # 三次握手,返回值是(socket字节流,ip+端口号)
        while True:                                                          # 收发消息的逻辑
            # 接收消息
            res = conn.recv(2024).decode()
            print("收到{}消息:".format(cil_appr),res)
            # 客户端主动断开连接
            if res.lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
                break
            # 发送消息
            data = input("发送消息:")
            # 服务端主动断开连接
            conn.send(data.encode())
            if data.lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
    ​
                break
    conn.close()
    sk.close()                                                               # 释放程序
    

      

    1.2 客户端基本套路

    import socket
    ​
    sk = socket.socket()                             # 默认创建TCP协议对象
    sk.connect(("127.0.0.1",3008))                   # 连接("127.0.0.1",3008)服务器
    while True:                                      # 收发消息的逻辑
        # 发送消息
        data1 = input("发送消息:")
        sk.send(data1.encode("utf-8"))
        if data1.lower() == "q":
            print("与服务器连接已断开")
            break
        res = sk.recv(2024).decode()
        if res.lower() == "q":
            print("与服务器连接已断开")
            break
        print("收到服务器消息:", res)
    sk.close()                                       # 关闭程序
    

      

    2, UDP协议基本语法

    UDP(User Datagram Protocol)一种无连接的,不可靠的传输层通信协议(比如:发短信)
    优点:速度快,可以多人同时聊天,耗费资源少,不需要建立连接
    缺点:不稳定,不能保证每次数据都能接收到
    应用:IP电话,实时视频会议,聊天软件,少量数据传输的场景
    

      

    2.1 服务端基本套路

    import socket
    ​
    sk = socket.socket(type=socket.SOCK_DGRAM)                  # 创建UDP协议对象
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)      # 绑定 (ip+端口号)
    sk.bind(("127.0.0.1",9005))
    while True:
        # 服务器永不断开
        while True:
            # 不需要建立连接,有消息就接收,同时获取msg,ip+端口号
            msg,cil_appr = sk.recvfrom(1024)
            # 接收消息
            print("收到{}消息".format(cil_appr),msg.decode())
            if msg.decode().lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
                break
            # 发送消息
            data = input("发送消息:")
            sk.sendto(data.encode(),cil_appr)
            if data.lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
                break
    sk.close()
    

      

    2.2 客户端基本套路

    import socket
    ​
    sk = socket.socket(type=socket.SOCK_DGRAM)
    while True:
        # 不需要建立连接,有ip+端口号就可以直接发送
        data = input("发送消息:")
        sk.sendto(data.encode(),("127.0.0.1",9005))
        if data.lower() == "q":
            print("与服务器连接已断开")
            break
        msg, cil_qppr = sk.recvfrom(1024)
        if msg.decode().lower() == "q":
            print("与服务器连接已断开")
            break
        print("收到{}消息".format(cil_qppr), msg.decode())
    sk.close()
    

      

    3 黏包

    # TCP协议在发送数据时,会出现黏包现象.  
        (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区,
        缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区都会设置的比较大。
        (2)在收发数据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度
        导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包
        
    # 黏包对比
        #tcp协议:
        缺点:接收时数据之间无边界,有可能粘合几条数据成一条数据,造成黏包 
        优点:不限制数据包的大小,稳定传输不丢包
        #udp协议:
        优点:接收时候数据之间有边界,传输速度快,不黏包
        缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能丢包
    ​
            tcp和udp对于数据包来说都可以进行拆包和解包,理论上来讲,无论多大都能分次发送
        但是tcp一旦发送失败,对方无响应(对方无回执),tcp可以选择再发,直到对应响应完毕为止
        而udp一旦发送失败,是不会询问对方是否有响应的,如果数据量过大,易丢包
        
    # 导致黏包的2种情况
        1,发送端发送消息速度太快
        2,接收端接收消息速度太慢
        
    

      

    3.1 struct模块解决黏包问题

    """
    通过UDP接收数据有边界的特点,用struct来解决TCP接收数据无边界来解决黏包问题
    """
    # 服务端
    import socket,struct,time
    ​
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(("127.0.0.1",3008))
    sk.listen()
    conn,cil_appr = sk.accept()
    ​
    num = conn.recv(4)   
    num = struct.unpack("i",num)
    data1 = conn.recv(num[0])
    data2 = conn.recv(1024)
    print(data1.decode())
    print(data2.decode())
    data2.decode()
    conn.close()
    sk.close()
    ​
    # 客户端
    sk = socket.socket()
    sk.connect(("127.0.0.1",3008))
    data = input("请输入发送的消息").encode()
    ​
    num = struct.pack("i",len(data))
    sk.send(num)
    sk.send(data)
    sk.send("黏包了吗".encode())
    ​
    sk.close()
    

      

     

    3.1.1 服务端脚本

    import socket, struct
    ​
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sk.bind(("127.0.0.1", 3008))
    sk.listen()
    while True:
        conn, cil_appr = sk.accept()
        while True:
            num = conn.recv(4)
            num = struct.unpack("i", num)
            res = conn.recv(num[0])
            print("收到{}消息:".format(cil_appr), res.decode())
            res = conn.recv(1024)
            print("收到{}消息:".format(cil_appr), res.decode())
            if res.lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
                break
            data = input("发送消息:")
            conn.send(data.encode())
            if data.lower() == "q":
                print("{}连接已断开
    正在等待连接....".format(cil_appr))
                break
    conn.close()
    sk.close()
    

      

    3.1.2 客户端脚本

    import socket,struct
    ​
    sk = socket.socket()
    sk.connect(("127.0.0.1",3008))
    ​
    while True:
        data = input("发送消息:").encode()
        num = struct.pack("i",len(data))               # 获取任意数字转化后为4字节的字节流
        sk.send(num)                                   # 先告诉对方,我要发多少单位的数据
        sk.send(data)                                  # 再发送实际数据
        sk.send("黏包了吗????".encode())
        res = sk.recv(2024).decode()
        if data.lower() == "q":
            print("与服务器连接已断开")
            break
        if res.lower() == "q":
            print("与服务器连接已断开")
            break
        print("收到服务器消息:", res)
    ​
    sk.close()
     
    

      

  • 相关阅读:
    板子的配置
    检查点队列中未提交的数据块如何管理会减少内存使用
    js求月末new Date(year,month,0)
    js 截取字符串
    es6 x,y 互换值
    android x86 9.0r2 install guide 旧笔记本改造机顶盒
    Linux中常用命令
    在pyspark中调用scala/java代码
    Linux中安装sbt
    IDEA中手动引入JAR包
  • 原文地址:https://www.cnblogs.com/zhoulangshunxinyangfan/p/13498363.html
Copyright © 2020-2023  润新知