模块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()
 

  

原文地址:https://www.cnblogs.com/zhoulangshunxinyangfan/p/13498363.html