粘包
- tcp是流式传输,字节流,数据与数据之间是没有边界的
- 流式传输优点:
- 不限定长度
- 可靠传输
- 缺点:
- 慢
- 和一个人的通信连接conn会一直占用我们的通信资源
- 流式传输优点:
- udp协议,面向数据包的传输
- 数据包优点
- 快
- 由于不需要建立连接,所以谁发的消息我都能接受到
- 缺点
- 不能传输过长的数据
- 不可靠
- 数据包优点
粘包现象
- 由于流式传输的特点,产生了数据连续发送的粘包现象。
- 在一个conn建立起来的连接上传输的多条数据是没有边界的
- 数据的发送和接收实际上不是在执行send/recv的时候就立刻被发送和接收,而是需要经过操作系统内核
- Nagle 算法,能够将发送间隔实际很近的短数据合成一个包发送到接收端
- 拆包机制: 当要发送的数据超过了网络上能够传输的最大长度,就会被tcp协议强制拆包
解决粘包问题
struct模块
park("i",len(msg)) 把数字转成4字节
unpack("i",bytes)[0] 把四字节转换成长度
server端import structimport socketsk = socket.socket()sk.bind(('127.0.0.1',9090))sk.listen()conn,addr = sk.accept()while True: s = input('>>>').encode('utf-8') pack_num = struct.pack('i',len(s)) conn.send(pack_num) conn.send(s)conn.close()sk.close()
client端import socketimport structsk = socket.socket()sk.connect(('127.0.0.1',9090))while True: pack_num = sk.recv(4) num = struct.unpack('i',pack_num)[0] ret = sk.recv(num) print(ret.decode('utf-8'))sk.close()
并发的 socketserver
- 实现同一时刻server端可以和多个client端建立连接
验证客户端链接的合法性
server端
import osimport hmacimport socketdef auth(conn): msg = os.urandom(32) # # 生成一个随机的字符串 conn.send(msg) # # 发送到client端 result = hmac.new(secret_key, msg) # 处理这个随机字符串,得到一个结果 client_digest = conn.recv(1024) # 接收client端处理的结果 if result.hexdigest() == client_digest.decode('utf-8'): print('是合法的连接') # 对比成功可以继续通信 return True else: print('不合法的连接') # 不成功 close return Falsesecret_key = b'alex_sb'sk = socket.socket()sk.bind(('127.0.0.1',9000))sk.listen()conn,addr = sk.accept()if auth(conn): print(conn.recv(1024)) # 正常的和client端进行沟通了 conn.close()else: conn.close()sk.close()
client端
import hmacimport socketdef auth(sk): msg = sk.recv(32) result = hmac.new(key, msg) res = result.hexdigest() sk.send(res.encode('utf-8'))key = b'alex_s'sk = socket.socket()sk.connect(('127.0.0.1',9000))auth(sk)sk.send(b'upload')# 进行其他正常的和server端的沟通sk.close()