网络编程(二)
socket套接字简介
# socket套接字是一门技术
socket套接字可以帮我们实现C/S架构的程序两端交互
socket套接字可以通过python内置的socket模块快速实现OSI七层操作
"""
以后我们写软件连socket的影子都看不到 因为被封装起来
socket是最底层的原理 很多框架都封装了 其实我们不需要深入研究
"""
socket模块
# 编程思想
对于C/S架构的程序,无论编写还是运行,都要遵循:
'优先考虑服务端,有了服务才能有客户'
# 服务端代码
import socket
server = socket.socket() # 获取连接对象
server.bind(('127.0.0.1',8080)) # 给连接对象绑定地址
server.listen(5) # 允许最大排队数 这个数加上正在连接数就是最大连接数
sock, addr = server.accept() # 阻塞程序,没有获取到连接的客户端会阻塞程序运行
print(addr)
while True: # 循环通信
data = sock.recv(1024) # 从连接里获取客户端的数据
print(data.decode('utf8'))
msg = input('>').strip()
sock.send(msg.encode('utf8')) # 通过连接把数据发给客户端
sock.close() # 关闭与客户端连接
server.close() # 关闭连接对象
# 客户端代码
import socket
client = socket.socket() # 获取连接对象
client.connect(('127.0.0.1',8080)) # 绑定连接服务端的地址
while True: # 循环通信
msg = input('>').strip()
client.send(msg.encode('utf8')) # 发数据到服务端
data = client.recv(1024) # 获取服务端发来的数据
print(data.decode('utf8'))
client.close() # 关闭连接
###########################
服务端与客户端首次交互
一边是recv那么另一边必须是send 两边不能相同 否则就'冷战'了
###########################
通信循环
# 思路
1.所有通信消息要用户事实输入
利用input获取用户输入信息
2.接收一次消息之后不能结束程序,应当获取输入,返回消息,然后等待下一次接发消息
利用While循环实现
# 代码编写
详见上段代码
代码优化及链接循环
1.不能发空消息
搞个if判断len(用户输入即可)
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判断
"""
客户端如果异常断开 服务端代码应该重新回到accept等待新的客人
# 目前我们的服务端只能实现一次服务一个人 不能做到同事服务多个 学了并发才可以实现
半连接池
listen(5)
# py文件默认同一时间只能运行一次 如果想单独分开运行多次
# 半连接池
设置的最大等待人数 >>>: 节省资源 提高效率
黏包问题
# 客户端
data1 ='aaa'
data2 ='aaa'
data3 ='aaa'
client.send(data1.encode('utf8'))
client.send(data2.encode('utf8'))
client.send(data3.encode('utf8'))
# 服务端
data1 = sock.recv(1024)
print(data1)
data2 = sock.recv(1024)
print(data2)
data3 = sock.recv(1024)
print(data3)
# 客户端输出
b'aaaaaaaaa'
b''
b''
为什么发了三次数据,结果全都在第一次数据里?
"""
# TCP协议的特点
会将数据量比较小并且时间间隔比较短的数据整合到一起发送
并且还会受制于recv括号内的数字大小(核心问题!!!)
流式协议:跟水流一样不间断
"""
"""
问题产生的原因其实是因为recv括号内我们不知道即将要接收的数据到底多大
如果每次接收的数据我们都能够精确的知道它的大小 那么肯定不会出现黏包
"""
eg:
data1 = sock.recv(3)
print(data1)
data2 = sock.recv(3)
print(data2)
data3 = sock.recv(3)
print(data3)
# 客户端输出
b'aaa'
b'aaa'
b'aaa'
解决黏包问题
解决思路:
我们可以发送固定大小的数据,客户端接收就不会出现黏包
# struct模块
import struct
data1 = 'Hello world!'
data2 = 'afdkjhfdafklajflkhaslfjahfjkahflksafjl'
print(len(data1), len(data2)) # 12 38
ret1 = struct.pack('i', len(data1))
ret2 = struct.pack('i', len(data2))
print(len(ret1), len(ret2)) # 4 4
res1 = struct.unpack('i',ret1)
res2 = struct.unpack('i',ret2)
print(res1, res2) # (12,) (38,)
# 具体解决黏包思路
1.先将用struct模块打包成固定长度
2.把打包后的长度发过去
3.对方用unpack解析获取真实长度
4.根据真实长度用recv(真实长度)接收数据
代码演示
1.编写一个cs架构的软件
就两个功能
一个视频下载:从服务端下载视频
一个视频上传:从客户端上传视频
"""
只要实现就可以 无需整合(如果能做到更好)
"""
# 代码
服务端
import socket
# 获取连接对象
import struct
server_packer = socket.socket()
server_packer.bind(('127.0.0.1',8081))
server_packer.listen(1)
sock,addr = server_packer.accept()
# 读取视频文件
with open('1.mp4','rb') as f:
data = f.read()
# 打包
ret = struct.pack('i', len(data))
# 发送打包后的数据
sock.send(ret)
# 发送真实数据
sock.send(data)
sock.close()
server_packer.close()
客户端
import struct
import socket
# 获取连接对象
client = socket.socket()
client.connect(('127.0.0.1',8081))
# 获取打包后长度并解析
ret = client.recv(4)
res = struct.unpack('i',ret)
# 根据解析的长度获取真实数据
data = client.recv(res[0])
# 写入文件
with open('2.mp4','wb') as f:
f.write(data)
client.close()
原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/244374.html