socket套接字编程、实现通信循环、解决黏包问题与优酷小项目之视频上传与下载(一)
2022-01-12 22:44
656 查看
上期内容回顾
- 面向对象复习(json序列化类)
对象、类、父类的概念 面向对象的三大特征: 封装 继承 多态 双下开头的方法(达到某个条件自动触发): __init__:对象实例化自动触发 __str__:对象执行打印操作自动触发 __call__:对象加括号调用的时候自动触发 ... 反射: 利用字符串操作对象的属性或方法 hasattr getattr json序列化非默认的python数据类型: 常见操作是手动转字符串 不常见操作重写cls指向的类
- 软件开发架构
c/s 架构 b/s 架构 b/s架构本质上也是c/s架构
- 远程传输数据的发展史
所有前言的技术几乎都是诞生于军事 要想实现远程传输首先需要满足的条件是:"物理连接介质"
- OSI七层协议
'应表会传网数物' 应用层 传输层 网络层 数据链路层 网络连接层
- 各种协议及常见硬件介绍
# 物理连接层 网线 网卡 # 数据链路层 电信号分组方式 以太网协议 mac地址 12位16进制数 mac地址只能在局域网内实现数据交互 交换机 路由器 局域网 互联网 '上网其实就是顺着网线访问其他计算机上面的资源(网络只有更安全)' # 网络层 IP协议 IP地址用于表示接入互联网的一台计算机 IPV4与IPV6 PORT协议 端口地址用于表示计算机上面某一个应用程序 动态分配、范围限制(0-65535) '''IP+PORT:唯一标识计算机上面的某一个应用程序''' # 传输层 TCP、UDP # 应用层 HTTP、FTP、HTTPS
- TCP与UDP
# TCP 可靠协议 流式协议 三次握手建链接 四次挥手断链接 # UDP 不可靠协议 数据报协议 """ TCP类似于打电话 UDP类似于发短信 """
今日内容概要
socket套接字编程
掌握基本的客户端与服务端代码编写
通信循环
代码健壮性校验
链接循环
TCP黏包现象(流式协议)
报头制作、struct模块、封装形式
优酷项目
内容详细
1、socket套接字编程
要求:自己想写一款可以数据交互的程序 应用:socket模块 # 测试: 创建两个py文件 分别写好 客户端 服务端代码 交互程序架构启动 肯定是先启动服务端再启动客户端 客户端输入的话 会自动在服务端输出 服务端回复的话 也会自动在客户端输出 """ 导入模块的两种方式 import句式 from...import...句式 第三方模块下载 pip3 install 模块名==版本号 -i 仓库地址 """
1.1、简易版代码(客户端)
import socket # 1.先要有通讯工具 也就是定义传输协议 client = socket.socket() # 2.同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) # 3.开始给服务端说话 client.send(b'I am from client!') # send 说给对方 # 4.也要听对方想说什么 data = client.recv(1024) # recv 听对方说(接收) print(data) # 打印听到的对方说的话 # 5.说完可以关机了 client.close()
1.2、简易版代码(服务端)
import socket # 1.定义传输协议 server = socket.socket() # 2.绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 3.半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) # 4.开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 # 5.开始听客户端说话 data = sock.recv(1024) # recv(1024) 最大接收1024个字节 听对方说(接收) print(data) # 打印听到的对方说的话 # 6.听完对方说话 要回复对方 sock.send(b'I AM FROM SERVER!') # send 说给对方 # 7.说完可以 挂断电话 sock.close() # 8.挂完电话 服务端可以关机了 server.close()
2、通信循环及代码优化
1.客户端校验消息不能为空 2.服务端添加兼容性代码(主要针对:mac linux系统) 3.服务端重启频繁报端口占用错误 from socket import SOL_SOCKET, SO_REUSEADDR server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 4.客户端异常关闭服务端报错的问题 加入异常捕获 5.服务端链接循环 6.半连接池 设置可以等待的客户端数量 """ 优化项: 1.想要可以循环说话 所以从说话开始加入循环 2.说话要自己随机输入 所有加入获取用户输入 3.如果输入为空 不能报错 要重新获取用户输入 4.解决客户端异常关闭 服务端会报错的问题 5.服务端提供循环服务 断开一台客户端 就去服务另一台客户端 服务端不会关闭 6.可以同时连接多位客户端(但是同时只能给一个客户提供服务 其他客户要等待) """
2.1、通信循环及代码优化客户端
import socket # 1.先要有通讯工具 也就是定义传输协议 client = socket.socket() # 2.同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) while True: speak = input('输入想说的话>>>:').strip() if len(speak) == 0 : continue # 3.开始给服务端说话 因为获取的用户输入是字符串 不能直接传输 所以要先转码 client.send(speak.encode('utf8')) # 也可以用手动转码 # client.send(bytes(speak, 'utf8')) # 4.也要听对方想说什么 data = client.recv(1024) print(data.decode('utf8')) # 打印听到的对方说的话
2.2、通信循环及代码优化服务端
import socket from socket import SOL_SOCKET, SO_REUSEADDR # 1.定义传输协议 server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 # 2.绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 3.半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) while True: # 4.开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 print(address) while True: try: # 5.开始听客户端说话 data = sock.recv(1024) # recv(1024) 最大接收1024个字节 if len(data) == 0: continue print(data.decode('utf8')) # 打印听到的对方说的话 # 6.听完对方说话 要回复对方 sock.send(data+b'123') except ConnectionResetError as e: print(e) break
3、黏包问题
''' subprocess模块: 可以将代码命令给到计算机 cmd窗口执行 并拿回执行结果 ''' # 客户端 import socket # 1.先要有通讯工具 也就是定义传输协议 client = socket.socket() # 2.同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) while True: speak = input('输入cmd命令>>>:').strip() if len(speak) == 0 : continue # 3.开始给服务端说话 因为获取的用户输入是字符串 不能直接传输 所以要先转码 client.send(speak.encode('gbk')) # 也可以用手动转码 # client.send(bytes(speak, 'utf8')) # 4.也要听对方想说什么 data = client.recv(1024) # 获取的回复数据如果太大 一次回复不完 那么第二次无论告诉服务端什么请求 也都是继续先把第一次的数据继续传完为止 就导致错乱 print(data.decode('gbk')) # 打印听到的对方说的话 # 服务端 import socket from socket import SOL_SOCKET, SO_REUSEADDR import subprocess # 1.定义传输协议 server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 # 2.绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 3.半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) while True: # 4.开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 print(address) while True: try: # 5.开始听客户端说话 data = sock.recv(1024) # recv(1024) 最大接收1024个字节 if len(data) == 0: continue # print(data.decode('utf8')) # 打印听到的对方说的话 command_cmd = data.decode('utf8') sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = sub.stdout.read() + sub.stderr.read() # cmd执行的正确结果和错误结果全都获取 # 6.将获得的cmd执行结果回复给客户端 sock.send(res) except ConnectionResetError as e: print(e) break 1.例如: 第一次输入指令 tasklist 在客户端打印的结果其实只有服务端真实要传输的结果的其中一部分 第二次输入指令 dir 第三次输入指令 ipconfig 会发现客户端打印的结果依然是 第一次指令tasklist的剩余的数据 这就是所谓的 '黏包问题' """ 原理: 第一次数据管道的数据没有被完全取出 那么后面不管到第几次数据传输 都会先将第一次堆积在传输管道中的数据传输完毕 由此 会发现 只要有某一次数据未能全部传输的 后面的多次获取的结果并不是正确的对应结果 而是那一次未能全部传输的剩下的数据 一句话总结: 数据管道的数据没有被完全取出 """ 2.TCP协议有一个特性: 当数据量比较小 且时间间隔比较短的多次数据 那么TCP会自动打包成一个数据包发送 # TCP客户端 import socket client = socket.socket() client.connect(('192.168.11.70', 8080)) client.send(b'123') client.send(b'456') client.send(b'789') # TCP服务端 import socket server = socket.socket() server.bind(('192.168.11.70', 8080)) server.listen(5) sock, address = server.accept() data = sock.recv(1024) print(data) sock.recv(1024) print(data) data = sock.recv(1024) print(data) 打印结果: b'123456789' b'123456789' b'' # 解决以上两种情况 报头:能够标识即将到来的数据具体信息 比如:数据量多大 报头的长度必须是固定的
3.1、struct模块
'''该模块可以把一个类型 如数字 转成固定长度的bytes''' import struct res = b'13515313513516354140' print(len(res)) # 20 res1 = struct.pack('i', len(res)) print(len(res1)) res2 = b'13515313513516354140dsadasdadada' print(len(res2)) res3 = struct.pack('i', len(res2)) print(len(res3)) 打印结果: 20 4 32 4 # 但是如果数据特别大的时候 是无法打包成固定长度的
# 因此可以使用字典方式进行数据传输(无论真实数据多大) import struct import json d = { 'file_name': '很好看.mv', 'file_size': 1231283912839123123424234234234234234324324912, 'file_desc': '拍摄的很有心 真的很好看!!!', 'file_desc2': '拍摄的很有心 真的很好看!!!' } d = json.dumps(d) res = struct.pack('i',len(d)) print(len(res)) res1 = struct.unpack('i',res)[0] print(res1) # 打印结果 4 186
3.2、简易版本报头
# 客户端 import socket import struct # 1.先要有通讯工具 也就是定义传输协议 client = socket.socket() # 2.同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) while True: speak = input('输入cmd命令>>>:').strip() if len(speak) == 0 : continue # 3.开始给服务端说话 因为获取的用户输入是字符串 不能直接传输 所以要先转码 client.send(speak.encode('utf8')) # 先接收固定长度为4的报头数据 recv_first = client.recv(4) # 解析报头 real_length = struct.unpack('i', recv_first)[0] # 接收真实数据 real_data = client.recv(real_length) print(real_data.decode('gbk')) # 服务端 import socket from socket import SOL_SOCKET, SO_REUSEADDR import subprocess import struct # 1.定义传输协议 server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 # 2.绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 3.半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) while True: # 4.开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 print(address) while True: try: # 5.开始听客户端说话 data = sock.recv(1024) # 接收amd命令 if len(data) == 0: continue # print(data.decode('utf8')) # 打印听到的对方说的话 command_cmd = data.decode('utf8') sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = sub.stdout.read() + sub.stderr.read() # cmd执行的正确结果和错误结果全都获取 但是结果可能很大 # 制作报头 data_first = struct.pack('i', len(res)) # 发送报头 sock.send(data_first) # 发送真实数据 sock.send(res) except ConnectionResetError as e: print(e) break
3.3、利用字典报头上传文件数据(升级版)
''' 从服务端向客户端传输 按照此方法就解决了 黏包问题 ''' # 客户端 import json import socket import struct # 先要有通讯工具 也就是定义传输协议 client = socket.socket() # 同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) while True: speak = input('输入cmd命令>>>:').strip() if len(speak) == 0 : continue # 开始给服务端说话 因为获取的用户输入是字符串 不能直接传输 所以要先转码 client.send(speak.encode('utf8')) # 1.先接收固定长度为4的报头数据 recv_first = client.recv(4) # 2.解析字典报头 dict_length = struct.unpack('i', recv_first)[0] # 3.接收字典数据 real_data = client.recv(dict_length) # 4.解析字典(json格式的bytes数据 loads方法会自动先解码 后反序列化) real_dict = json.loads(real_data) print(real_dict) # 5.获取字典中的各项数据 data_lenfth = real_dict.get('size') data_bytes = client.recv(data_lenfth) print(data_bytes.decode('gbk')) # 服务端 import json import socket from socket import SOL_SOCKET, SO_REUSEADDR import subprocess import struct # 定义传输协议 server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 # 绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) while True: # 开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 print(address) while True: try: # 开始听客户端说话 data = sock.recv(1024) # 接收amd命令 if len(data) == 0: continue # print(data.decode('utf8')) # 打印听到的对方说的话 command_cmd = data.decode('utf8') sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = sub.stdout.read() + sub.stderr.read() # cmd执行的正确结果和错误结果全都获取 但是结果可能很大 # 1.预防数据过大 定义字典传输 data_dict = { 'desc': '这是非常重要的数据', 'size': len(res), 'info':'视频很提神醒脑!' } # 2.将字典做成报头 但是字典不能直接打包成报头 所以先json序列化 data_json = json.dumps(data_dict) # 3.制作字典报头 data_first = struct.pack('i', len(data_json)) # 4.发送字典报头 sock.send(data_first) # 5.发送字典 sock.send(data_json.encode('utf8')) # 6.发送真实数据 sock.send(res) except ConnectionResetError as e: print(e) break
4、优酷项目中 视频的上传与下载
# 从客户端向服务端上传 # 客户端 import json import socket import struct import os # 先要有通讯工具 也就是定义传输协议 client = socket.socket() # 同样绑定IP 端口 client.connect(('192.168.11.70', 8080)) while True: DATA_PATH = r'D:\飞秋下载文件\python\02、python提升\网络并发day01\视频' movie_name_list = os.listdir(DATA_PATH) # ['01 后期课程安排.mp4', '02 面向对象复习扩展.mp4', '03 今日内容概要.mp4', '04 软件开发架构.mp4', '05 网络传输前戏.mp4', '06 七层协议简介.mp4', '07 常见硬件.mp4', '08 网络层.mp4', '09 传输层之端口协议.mp4', '10 TCP与UDP协议.mp4'] for i, j in enumerate(movie_name_list, 1): print(i, j) choice = input('选择想要上传的电影编号>>>:').strip() if choice.isdigit(): choice = int(choice) if choice in range(1, len(movie_name_list)+1): # range顾头不顾尾 # 获取文件名称 movie_name = movie_name_list[choice-1] print(movie_name) # 拼接文件绝对路径 以找到文件 movie_path = os.path.join(DATA_PATH, movie_name) # 1.预防数据过大 定义字典传输 data_dict = { 'file_name': '波多野老师合集.mp4', 'desc': '这是非常重要的数据', 'size': os.path.getsize(movie_path), 'info': '视频很提神醒脑!' } # 2.将字典做成报头 但是字典不能直接打包成报头 所以先json序列化 data_json = json.dumps(data_dict) # 3.制作字典报头 data_first = struct.pack('i', len(data_json)) # 4.发送字典报头 client.send(data_first) # 5.发送字典 client.send(data_json.encode('utf8')) # 6.发送真实数据 with open(movie_path, 'rb') as f: for line in f: client.send(line) # 服务端 import json import socket from socket import SOL_SOCKET, SO_REUSEADDR import struct # 定义传输协议 server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加该行代码 # 绑定IP 端口 server.bind(('192.168.11.70', 8080)) # 半连接池(同时可以有六个客户端访问 五个排队等待 一个接受服务) server.listen(5) while True: # 开始监听客户端说话 三次握手建立连接 sock, address = server.accept() # sock 监听的对象 | address 监听对象的地址 print(address) while True: # 1.先接收固定长度为4的报头数据 recv_first = sock.recv(4) # 2.解析字典报头 dict_length = struct.unpack('i', recv_first)[0] # 3.接收字典数据 real_data = sock.recv(dict_length) # 4.解析字典(json格式的bytes数据 loads方法会自动先解码 后反序列化) real_dict = json.loads(real_data) print(real_dict) # 5.获取字典中的各项数据 data_length = real_dict.get('size') file_name = real_dict.get('file_name') recv_size = 0 with open(file_name, 'ab') as f: while recv_size < data_length: data = sock.recv(1024) recv_size += len(data) f.write(data) ''' 本期只简单完成上传功能 细节之处后续继续优化 '''
知识拓展
在阅读源码的时候: 1.变量名后面跟冒号 表示的意思是该变量名需要指代的数据类型 2.函数后更横杆加大于号表示的意思是该函数的返回值类型
相关文章推荐
- 关于用两块arm实现双视频通信项目所遇到的问题和解决方法
- 解决swift实现的websocket与后台通信问题:websocket is disconnected: masked and rev data is not currently supported
- SmartUpload组件实现上传、下载功能和Unable to compile class for JSP问题的解决
- jsp实现上传和下载 解决smartupload中文乱码问题
- CKEditor使用js结合CKFinder实现上传,解决项目路径问题
- 解决使用python中paramiko库实现本地与服务器文件上传和下载时遇到[Errno 13] Permission denied的问题
- Socket编程创建对话框模式的项目时忘记选择 windows”套接字”的解决办法!
- 网络编程----socketserver多并发实现、FTP上传多并发、udp协议套接字多并发
- weblogic服务上传word等文件直接打开问题解决 博客分类: web应用服务器 在weblogic上发布的web项目,测试中发现出现当上传word、excel、pdf等文件在下载的时候出现
- python socket通信编程实现文件上传代码实例
- Leo仿【DOTA视频站】项目实践【五】---- fragment中嵌套viewpage、fragment实现从优酷中下载视频
- Java Web中使用JSPSmartUpload控件实现文件的上传和下载(解决了中文乱码问题)(JSP页面采用GBK编码)
- Java Web中使用JSPSmartUpload控件实现文件的上传和下载(解决了中文乱码问题)(JSP页面采用GBK编码)
- C++使用socket套接字的通信编程实现
- iTOP-4412开发板实现基于linux下网络通信-套接字 TCP 的 socket 编程
- 网络编程二(套接字Socket、客户端和服务端通信问题)
- 网络编程:TCP协议: 三次握手,四次挥手,socket套接字通信:,粘包问题subprocess模块,struct模块
- java在线聊天项目0.6版 解决客户端关闭后异常问题 dis.readUTF()循环读取已关闭的socket
- 在Ubuntu下实现本地套接字(socket)通信以及遇到的问题!
- 用Struts2更好的实现文件的上传、下载功能以及解决中文名称问题