socket套接字、通信循环、链接循环和黏包问题

一、socket套接字

二、通信循环

三、代码优化和链接循环

四、黏包问题

一、socket套接字

1、socket套接字简介

socket套接字是一门技术
它为我们提供了快捷方式,不需要我们自己去处理OSI七层的每一层

LGARkF.jpg

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()  # 直接关机了

LGmQM9.jpg

# 客户端
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()  # 关闭客户端

image-20220415193614077

服务端和客户端首次交互,一边是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可以将固定长度的数字解包成打包之前数据真实的长度

LG0NHP.jpg

LG0B9g.jpg

  • 黏包问题终极解决问题

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

# 接收方
1、先接收包装好固定长度的字典
2、解析出字典的真实长度unpack
3、接收字典数据
4、从字典数据中解析出各种信息
5、接收真实的数据

LGyo0s.jpg

posted @ 2022-04-15 22:10  未月  阅读(94)  评论(0)    收藏  举报