一 socket 实现大文件上传(基于TCP协议)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import socket,os, json,struct client = socket.socket() client.connect(('127.0.0.1',8080)) # 文件大小 file_size = os.path.getsize(r'D:\fullstack_s\day32\巴拉巴拉.mp4') # 文件名字 file_name = '性感荷官在线发牌.mp4' # 定义一个字典 d = { "file_name":file_name, "file_size":file_size, 'msg':'注意身体哦!' } data_bytes = json.dumps(d).encode('utf-8') # 制作字典的报头 header = struct.pack('i',len(data_bytes)) # 发送报头 client.send(header) # 发送字典 client.send(data_bytes) # 发送真实数据 with open(r'D:\fullstack_s\day32\巴拉巴拉.mp4','rb') as f: for line in f: client.send(line)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import socket,struct,json,os server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: # 先接受报头 header = conn.recv(4) # 解析报头 获取字典长度 header_len = struct.unpack('i',header)[0] # 接收字典 header_bytes = conn.recv(header_len) header_dic = json.loads(header_bytes.decode('utf-8')) print(header_dic) # 循环接收文件 存储到本地 file_size = header_dic.get('file_size') file_name = header_dic.get('file_name') recv_size = 0 # 文件操作 with open(file_name,'wb') as f: # 循环接收文件数据 while recv_size < file_size: data = conn.recv(1024) f.write(data) recv_size += len(data) print(header_dic.get('msg')) except ConnectionResetError: break conn.close()
socket基于TCP协议实现并发通信:socketserver模块(实现思路,TCP协议的特点是必须接客与服务是一个人,所以下一位服务的前提是上次服务完成,而socketserver的思路是,安排个老鸨专门接客)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
服务端 import socketserver class MyBaby(socketserver.BaseRequestHandler): def handle(self): # 通信循环 while True: # self.request相当于你的conn通信对象 data,sock = self.request # 收消息 print(data) sock.sendto(data.upper(),self.client_address) # self.client_address客户端地址 if __name__ == '__main__': server = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyBaby) server.serve_forever() 客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024) print(data)
二 socket 聊天功能(基于UDP协议)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: msg,addr = server.recvfrom(1024) print(msg.decode('utf-8')) data = input('>>>:').encode('utf-8') server.sendto(data,addr) 客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '客户端1的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_addr) data,addr = client.recvfrom(1024) print(data)
基于UDP协议的通信特点:不粘包,支持并发。
TCP/UDP 区别:TCP,像打电话;UDP,像是发短信。
为什么会出现粘包现象
"""
首先只有在TCP协议中才会出现粘包现象,因为TCP协议是流式协议
它的特点是将数据量小并且时间间隔比较短的数据一次性打包发送出去
本质其实还是因为我们不知道需要接收的数据的长短
"""
# 如何解决粘包问题?
# 1 发送数据直接先告诉对方数据量的大小
# 2 利用struct模块定制我们自己的消息传输协议
socket发送大文件示例
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# 客户端 import struct import json import socket import os client = socket.socket() client.connect(('127.0.0.1', 8080)) file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/aboutsocket/10 解决粘包问题终极版.mp4') file_path = r'/Users/jiboyuan/PycharmProjects/aboutsocket/10 解决粘包问题终极版.mp4' data_dic = { 'file_name': '澳门最大线上赌场开业啦.mp4', 'file_size': file_size } header_json = json.dumps(data_dic) header_bytes = header_json.encode('utf-8') # 制作字典的报头 header = struct.pack('i', len(header_bytes)) # 发送报头 client.send(header) # 发字典数据 client.send(header_bytes) # 打开文件发送文件数据 with open(file_path,'rb') as f: for line in f: client.send(line) # 服务端 import socket import json import struct server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: header = conn.recv(4) if len(header) == 0:break dic_len = struct.unpack('i', header)[0] real_dic = json.loads(conn.recv(dic_len).decode('utf-8')) print(real_dic) file_name = real_dic.get('file_name') file_size = real_dic.get('file_size') recv_size = 0 with open(file_name, 'wb') as f: while recv_size < file_size: recv_data = conn.recv(1024) f.write(recv_data) recv_size += len(recv_data) except ConnectionResetError: break
UDP协议
"""
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议服务端不存在的情况下,客户端照样不会报错
4.udp协议支持并发
UDP叫数据报协议,意味着发消息都带有数据报头
udp的server不需要就行监听也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来,客户端发送消息的时候,要带上服务端的地址
服务端在回复消息的时候,也需要带上客户端的地址
"""
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# 服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1', 8080)) msg, addr = server.recvfrom(1024) print(msg.decode('utf-8')) server.sendto(b'hello', addr) server.close() #客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8080) client.sendto(b'hello server baby!', server_addr) msg, addr = client.recvfrom(1024) print(msg, addr) # udp特点 >>> 无链接,类似于发短信,发了就行对方爱回不回,没有任何关系 # 将服务端关了,客户端起起来照样能够发数据。因为不需要考虑服务端能不能收到 # 验证udp协议有无粘包问题 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) print(server.recvfrom(1024)) print(server.recvfrom(1024)) print(server.recvfrom(1024)) import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1',8080) client.sendto(b'hello',server_addr) client.sendto(b'hello',server_addr) client.sendto(b'hello',server_addr)
基于UDP实现简易版本的qq
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# 服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1', 8080)) while True: msg, addr = server.recvfrom(1024) print(addr) print(msg.decode('utf-8')) info = input('>>>:').encode('utf-8') server.sendto(info, addr) server.close() # 多个客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8080) while True: info = input('>>>:') info = ('来自客户端1的消息:%s'%info).encode('utf-8') # 改中文备注即可 client.sendto(info, server_addr) msg, addr = client.recvfrom(1024) print(msg.decode('utf-8'), addr) client.close()
小知识点补充:
windows电脑和max电脑的时间同步功能,其实就是基于udp朝windows,max服务器发送请求获取标准时间
总结:
TCP协议就类似于打电话
UDP协议就类似于发短信
SocketServer模块介绍(让tcp也能支持并发)
并发
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# TCP socketserver使用 import socketserver class MyTcpServer(socketserver.BaseRequestHandler): def handle(self): while True: try: data = self.request.recv(1024) # 对于tcp,self.request相当于conn对象 if len(data) == 0:break print(data) self.request.send(data.upper()) except ConnectionResetError: break if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTcpServer) server.serve_forever() # UDP socketserver使用 import socketserver class MyUdpServer(socketserver.BaseRequestHandler): def handle(self): while True: data, sock = self.request print(data) sock.sendto(data.upper(), self.client_address) if __name__ == '__main__': server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyUdpServer) server.serve_forever()
并发编程
操作系统的发展史
输入输出设备>>>:IO操作即(input和output)
-
手工操作穿孔卡片
-
批处理(磁带)
-
脱机批处理系统
一步步的优化,其实都是在提高计算机CPU利用率的问题(问题在于时串行并且没有空间上的复用)
多道技术的产生
解决cpu在执行程序,遇到io时,不干活的情况
串行:一个程序完完整整的运行完毕,才能运行下一个程序
并发:看上去像同时运行
多道技术:
-
空间上的复用(多个程序共一套硬件设备,它是多道技术实现时间上的复用的基础,不然还要去硬盘读数据)
-
时间上的复用(单个cpu的电脑上,起多个应用程序。cpu快速切换,给人的感觉是同时运行)
-
一个任务占用cpu时间过长或被操作系统强行剥夺走cpu的执行权限(比起串行效率反而降低)
-