基于TCP的大文件发送
#server服务端
import struct
import json
import os
import socket
server = socket.socket() # 默认TCP协议
server.bind(('127.0.0.1',8080)) # 传入IP和端口
server.listen(5) # 限制半连接池的数量
while True:
conn,addr = server.accept() # conn是连接,addr是地址,accept就是阻塞,等待一下,等客户端连接
while True:
try:
header_dict = conn.recv(4) # 接收一个字典的报头
dict_size = struct.unpack('i',header_dict)[0] # 解包得到字典的长度
dict_bytes = conn.recv(dict_size) # 接收一个字典
dict_json = json.loads(dict_bytes.decode('utf-8')) # 将字典解码并反序列化
total_size = dict_json.get('file_size') #拿出字典中真实数据的大小
recv_size = 0
with open(dict_json.get('file_name'),'wb') as f:
while recv_size < total_size:
data = conn.recv(1024) # 接收真实的数据
f.write(data) # 将数据写入文件
recv_size += len(data)
print('上传成功')
except ConnectionResetError as e: # 捕获客户端断开产生的错误
print(e)
break
conn.close() # 关闭客户端套接字
#client客户端
import socket
import struct
import json
import os
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
MOVIE_DIR = r'D:Program Filesfeiqworkuntitledworksp' # 找到文件路径
movie_list = os.listdir(MOVIE_DIR) # 将文件夹下的文件名放到列表里
for i,movie in enumerate(movie_list,1): # 给列表中的文件名加上序号,从1开始
print(i,movie)
choice = input('请输入电影序号>>>:')
if choice.isdigit():
choice = int(choice) - 1
if choice in range(0,len(movie_list)):
path = movie_list[choice] # 根据用户的选择,拿到文件名
file_path = os.path.join(MOVIE_DIR,path) # 拼接文件名的路径
file_size = os.path.getsize(file_path) # 获得该文件的大小
res_d = {
'file_name':path,
'file_size':file_size,
'msg':'注意身体健康'
} # 将文件名,大小以及一些其他信息存放在字典里
json_d = json.dumps(res_d) # 将字典转换成json格式的数据
json_bytes = json_d.encode('utf-8') # 将这个数据变成二进制
header = struct.pack('i',len(json_bytes)) # 打包一个报头,报头的内容是字典的长度
client.send(header) # 发送一个报头
client.send(json_bytes) # 发送这个字典
with open(file_path,'rb') as f: # 找到给文件并准备发送
for line in f:
client.send(line) # 循环取出数据并发送
else:
print('not in range')
else:
print('must be a number')
UDP通信
特点:数据报协议(自带报头),没有双向通道,通信过程类似于发短信。
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议支持并发
***允许发空和不会粘包的原因都是因为基于数据报协议,自带了报头
UDP协议的基本使用:
#服务端
import socket
server = socket.socket(type=socket.SOCK_DGRAM) # UDP协议
server.bind(('127.0.0.1',8080))
#UDP不需要设置半连接池,它也没有半连接池的概念
#因为没有双向通道,不需要accept,直接就到通信循环
while True:
data,addr = server.recvfrom(1024)
print('数据:',data) # 客户端发来的消息
print('地址:',addr) # 客户端的地址
server.sendto(data.upper(),addr)
#客户端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
# 不需要建立连接,直接进入通信循环
server_address = ('127.0.0.1',8080)
while True:
client.sendto(b'hello',server_address)
data,addr = client.recvfrom(1024)
print('服务端发来的数据',data)
print('服务端的地址',addr)
简易版本的QQ
#服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
while True:
data,addr = server.recvfrom(1024)
print(data.decode('utf-8'))
msg = input('>>>:')
server.sendto(msg.encode('utf-8'),addr)
#客户端:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
msg = input('>>>:')
res = '来自客户端1的消息:%s'%msg
client.sendto(res.encode('utf-8'),server_address)
data,server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
利用socketserver完成并发
#服务器
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
data, sock = self.request
print(self.client_address) # 客户端地址
print(data.decode('utf-8'))
sock.sendto(data.upper(), self.client_address)
if __name__ == '__main__':
"""只要有客户端连接,会自动交给自定义类中的handl方法去处理"""
server = socketserver.ThreadingUDPServer(('127.0.0.1',8080), MyServer)
server.serve_forever() # 启动该服务对象
#客户端
import socket
import time
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
client.sendto(b'hello',server_address)
data,addr = client.recvfrom(1024)
print(data.decode('utf-8'),addr)
time.sleep(1) # 睡眠1秒,让服务端有反馈的时间
并发:看起来像同时运行的
并行:真正意义上的同时运行