一、socket套接字
二、通信循环
三、代码优化和链接循环
四、黏包问题
一、socket套接字
1、socket套接字简介
socket套接字是一门技术
它为我们提供了快捷方式,不需要我们自己去处理OSI七层的每一层

2、实际应用
cs架构的软件都应该考虑到服务端,因为只有有了东西,才能提供东西
# 服务端
import socket
# 购买手机
server = socket.socket()
# 默认值是基于网络的遵循TCP协议的套接字AF_INET
# 插入手机卡
server.bind(('127.0.0.1', 8080)) # '127.0.0.1'是计算机本地回环地址,只有当前计算机本身可以访问
"""服务端应该具备固定的地址的特征"""
# 开机
server.listen(5)
# 等待并接听电话,没有人回复的时候就原地等待(程序阻塞)
sock, addr = server.accept() # listen和accept对应TCP三次握手服务端的两个状态
print(addr) # 客户端的地址
data = sock.recv(1024) # 获取别人说的什么
print(data.decode('utf8')) # 解码别人说的啥
sock.send('嘿喽哇'.encode('utf8')) # 回复别人的话,因为基于网络传输,使用二进制编码
"""
recv 和send接收和发送都是bytes类型的数据
"""
sock.close() # 不想听了,挂电话了
server.close() # 直接关机了

# 客户端
import socket
client = socket.socket() # 产生一个socket对象
client.connect(('127.0.0.1', 8080)) # 根据服务端的地址链接
client.send(b'hello, i am superman') # 给服务端发送信息
data = client.recv(1024) # 接收服务端回复消息
print(data.decode('utf8'))
client.close() # 关闭客户端

服务端和客户端首次交互,一边是recv另一边就要是send,两边不能相同。否则就一直等着
二、通信循环
# 1.解决信息固定的问题
利用input获取用户输入
# 2.解决通信循环的问题
将双方用于数据交互的代码循环起来
# 服务端
while True:
data = sock.recv(1024) # 获取别人说的什么
print(data.decode('utf8')) # 解码别人说的啥
msg = input('请回复>>>:').strip()
sock.send(msg.encode('utf8')) # 回复别人的话,因为基于网络传输,使用二进制编码
# 客户端
while True:
msg = input('请输入你要发送的信息>>>:').strip()
client.send(msg.encode('utf8')) # 给服务端发送信息
data = client.recv(1024) # 接收服务端回复消息
print(data.decode('utf8'))
三、代码优化及链接循环
1、发送信息不能为空
统计长度然后进行判断(len)
2、反复重启服务端可能会发生报错: address in use
解决方法:
# 在最上面放一个
from socket import SOL_SOCKET,SO_REUSEADDR
# 在bind上面放
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
3、链接循环
"""
如果在windows客户端异常处理退出之后服务端就会直接报错
处理方式: 异常处理
while True:
try:
data = sock.recv(1024) # 获取别人说的什么
print(data.decode('utf8')) # 解码别人说的啥
msg = input('请回复>>>:').strip()
sock.send(msg.encode('utf8')) # 回复别人的话,因为基于网络传输,使用二进制编码
except Exception:
break
如果是mac或linux服务端,就会接收到一个空的信息
处理方式: len判断
"""
1、半连接池
listen(5)
py文件默认只能同时只能运行一次,如果想要单独分开运行多次:
Edit Configuration选择all
# 半连接池
设置最大等待人数 >>>: 节省资源,提升效率
四、黏包问题
# 服务端
sock, addr = server.accept()
print(addr)
data1 = sock.recv(1024)
print(data1)
data2 = sock.recv(1024)
print(data2)
data3 = sock.recv(1024)
print(data3)
# 客户端
client.connect(('127.0.0.1', 8080))
client.send(b'hello')
client.send(b'bbbbb')
client.send(b'aaaaa')
打印结果:
('127.0.0.1', 52055)
b'hellobbbbbaaaaa'
b''
b''
# TCP协议的特点
将数据量比较小并且时间间隔比较短的数据整合到一起发送,并且还会受制于recv括号内的数字大小
流式协议:跟水流一样不间断
就是因为我们不知道recv括号不知道要接收多大的数据,使用才会出现黏包问题,那么我们只要能够判断大小,就可以避免黏包问题
# 能够精准确定数据的大小
使用struct模块
import struct
data1 = 'i am superman'
print(len(data1))
res1 = struct.pack('i', len(data1)) # 第一个参数是格式,写i就可以,第一个参数就是要放一个数字
print(len(res1))
data2 = 'hello, wuwa wuwa wuwa'
print(len(data2))
res2 = struct.pack('i', len(data2)) # 第一个参数是格式,写i就可以,第一个参数就是要放一个数字
print(len(res2))
pack可以将任意长度的数字打包成固定长度
unpack可以将固定长度的数字解包成打包之前数据真实的长度


# 发送方
1.先构造一个字典,里面放真实数据的信息
eg: 数据大小、名称、简介、、、
2、对字典做打包处理pack
3、将固定长度的数据发送给对方
4、发送真实的字典数据
5、发送真实的真正数据
# 接收方
1、先接收包装好固定长度的字典
2、解析出字典的真实长度unpack
3、接收字典数据
4、从字典数据中解析出各种信息
5、接收真实的数据
