本文主要是介绍socket套接字,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录
- socket套接字简介
- socket模块
- 通信循环
- 代码优化及链接循环
- 半连接池
- 黏包问题
- 黏包问题的解决
socket套接字简介
# 需求:编写cs架构的程序 实现数据交互
需要编写OSO七层相当复杂,但是由于OSI七层是所有cs架构程序都需要经历的过程,所以由固定的模块
socket模块:提供了快捷方式不需要自己处理每一层
'''
我们写软件,是看不到socket的,因为都被封装了起来
socket是最低层的原理,很多框架都被封装,不需要深入研究
'''
socket模块
cs架构的软件无论实在编写还是运行,都应该先考虑服务端
# 服务端
import socket
server = socket.socket()
"""
通过查看源码得知
括号内不写参数默认就是基于网络的遵循TCP协议的套接字
"""
server.bind(('127.0.0.1', 8008))
"""
服务端应该具备的特征
固定的地址
127.0.0.1是计算机的本地回环地址 只有当前计算机本身可以访问
"""
server.listen(3)
'''半连接池:可以决定客户端的等待人数'''
sock, addr = server.accept()
"""
listen和accept对应TCP三次握手服务端的两个状态
"""
print(addr)
msg = sock.recv(1024)
print(msg.decode('utf8'))
sock.send('hello client'.encode('utf8'))
sock.close()
server.close()
# 客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8008))
client.send('hello world'.encode('utf8'))
msg = client.recv(1024)
print(msg.decode('utf8'))
client.close()
'''
服务端与客户端的recv与send必须错开,不能同时存在,否则两边,都等待消息或者发送消息,程序阻塞
'''
通信循环
1.解决消息固定的问题
利用input获取用户输入
2.解决通信循环问题
# 服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8008))
server.listen(3)
clint, addr = server.accept()
while True:
msg = clint.recv(1024)
print(msg.decode('utf8'))
msg_send = input('请输入信息:')
clint.send(msg_send.encode('utf8'))
clint.close()
server.close()
# 客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8008))
while True:
msg_send = input('请输入信息:')
client.send(msg_send.encode('utf8'))
msg = client.recv(1024)
print(msg.decode('utf8'))
client.close()
代码优化及链接循环
1.发送消息不能为空
统计长度并判断
2.反复重启之后服务端可能会报错:address in use
这个错误在苹果电脑报的比较频繁 windows频率较少
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 加在bind前
3.链接循环
'''
如果是windows 客户端异常退出之后服务端会直接报错
处理方式:异常处理
如果是mac或者linux服务端会接收到一个空消息
处理方式:len判断
'''
# 服务端
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
server = socket.socket()
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1', 8008))
server.listen(3)
clint, addr = server.accept()
while True:
try:
msg = clint.recv(1024)
print(msg.decode('utf8'))
msg_send = input('请输入信息:')
if len(msg_send) == 0:
continue
clint.send(msg_send.encode('utf8'))
except Exception as f:
print('客户端断开链接')
break
clint.close()
server.close()
# 客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8008))
while True:
msg_send = input('请输入信息:')
if len(msg_send) == 0:
continue
client.send(msg_send.encode('utf8'))
msg = client.recv(1024)
print(msg.decode('utf8'))
client.close()
客户端如果异常断开 服务端代码应该重新回到accept等待新的客人
# 当前服务端只能实现一次服务一个人,不能同时服务多个,学习并发之后可以实现
半连接池
listen(5)
# 在py文件默认同一时间只能运行一次,如果想单独分开运行多次 就需要用半连接池
# 半连接池
设置的最大等待人数:节省资源 提高效率
黏包问题
data1 = conn.recv(1024)
print(data1)
data2 = conn.recv(1024)
print(data2)
data3 = conn.recv(1024)
print(data3)
client.send(b'hello')
client.send(b'jason')
client.send(b'kevin')
'''
三次打印店的结果
b'hellojasonkevin'
b''
b''
'''
# TCP协议特点
会将数据量比较小并且时间间隔比较短的数据整合到一起发送
并且还会受限制与recv括号内数字的大小(核心问题)
流式协议:跟水流一样不间断
'''
问题产生的原因其实是因为recv括号内我们不知道将要接受的数据到底多大
如果每次接收的数据我们都能够精确的知道他的大小 那么肯定不会出现黏包
'''
思路:
核心问题是不知道即将接收的数据多大
如果能够精准的知道数据量多大 那么黏包问题就自动解决了
黏包问题的解决
# struct模块
import struct
msg = 'hello world'
print(len(msg)) # 11
res = struct.pack('i',len(msg)) #第一个参数是格式
print(len(res)) # 4
res1 = struct.unpack('i',res)
print(len(res1)) # (11,)
data2 = 'hello baby baby baby baby baby baby baby baby'
print(len(data2)) # 45
res2 = struct.pack('i', len(data2))
print(len(res2)) # 4
ret2 = struct.unpack('i', res2)
print(ret2) # (45,)
'''
pack可以将任意长度的数据打包成固定长度
unpack可以将固定长度的数据解包成打包之前的长度
解决黏包问题思路:
1.将真是数据打包成固定长度的包
2.将福哦顶长度的包先发给对方
3.对方接收到包之后进行解包获取真实数据长度
4.接收真实数据长度
'''
这篇关于socket套接字的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!