一、利用tcp协议上传大文件
服务端:
import socket
import json
import struct
import os
server = socket.socket()
server.bind(('127.0.0.1', 8082))
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()
#存在问题,无法解决 struct.error: unpack requires a buffer of 4 bytes
客户端
import socket
import os
import json
import struct
client = socket.socket()
client.connect(('127.0.0.1', 8082))
#文件大小
file_size = os.path.getsize(r'D:学习python上课相关day32代码day32 1 上节课复习.mp4')
#文件重新命名
file_name = '上传文件.mp4'
#定义一个字典
dic = {
'file_name':file_name,
'file_size':file_size,
'msg':'上传成功!'
}
#将字典序列化后编码为字节
data_bytes = json.dumps(dic).encode('utf-8')
#制作字典的报头
header = struct.pack('i', len(data_bytes))
#发送报头
client.send(header)
#发送字典
client.send(data_bytes)
#发送真实数据(上传的文件)
with open(r'D:学习python上课相关day32代码day32 1 上节课复习.mp4','rb') as f:
for line in f:
client.send(line)
二、UDP协议
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议服务端不存在的情况下,客户端照样不会报错
4.udp协议支持并发
UDP叫数据报协议,意味着发消息都带有数据报头
udp的server不需要就行监听也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来,客户端发送消息的时候,要带上服务端的地址
服务端在回复消息的时候,也需要带上客户端的地址
服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8083 ))
while True: #服务端需要始终运行
data,addr = server.recvfrom(1024)
print(data,addr) #b'hello' ('127.0.0.1', 54457)
#data是接收的数据,addr是客户端的地址
server.sendto(data.upper(),addr)
#将数据发送过去,同时需要加上客户端的地址
客户端:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8083)
client.sendto(b'hello', server_addr)
#发送数据,需要加上接收端的地址
msg,addr = client.recvfrom(1024)
#msg是接收的数据,addr是发送方的地址
print(msg,addr) #b'HELLO' ('127.0.0.1', 8083)
udp协议补充点:
1、无链接,类似于发短信,发了就行对方爱回不回,没有任何关系。
2、将服务端关了,客户端起起来照样能够发数据。因为不需要考虑服务端能不能收到。
三、基于UDP实现简单版本的qq
服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8086))
while True:
data,addr = server.recvfrom(1024)
print(data.decode('utf-8'))
data = input('>>>:').encode('utf-8') #angel
server.sendto(data,addr)
存在多个客户端:
客户端1:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8086))
while True:
data,addr = server.recvfrom(1024)
print(data.decode('utf-8'))
data = input('>>>:').encode('utf-8') #hello
server.sendto(data,addr)
客户端2:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8086)
while True:
data = input('>>>:') #逼格
data = '来自客户2的消息:%s' %data
client.sendto(data.encode('utf-8'),server_addr)
data,addr = client.recvfrom(1024)
print(data,addr)
当启动服务端,启动客户端1后,在客户端1里输入数据,服务端会有响应,再在服务端输入数据,客户端2输入的数据会与服务端响应。
来自客户1的消息:hello
>>>:angel
来自客户2的消息:逼格
小点补充:
windows电脑和max电脑的时间同步功能,其实就是基于udp朝windows,max服务器发送请求获取标准时间。
四、SocketServer 模块 (可以让tcp也支持并发)
TCP:
服务端:
import socketserver
class Base(socketserver.BaseRequestHandler):
def handle(self): 此处的方法是固定的,不然就会报错!!! 用别人的模块,只能按照别人的方法去写。
#通信循环
while True:
try:
data = self.request.recv(1024) #收消息
#self.request相当于通道conn
print(data)
self.request.send(data.upper()) #发消息
except ConnectionResetError:
break
if __name__ == "__main__":
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080),Base)
#socketserver.ThreadingTCPServer(('127.0.0.1') 为建立连接,后者的Base为一个类
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)
对UDP同样适用
服务端:
import time
import socketserver
class Base(socketserver.BaseRequestHandler):
def handle(self):
#通信循环
while True:
data,sock = self.request #data是接收的数据,sock是套接字对象
print(data,sock) #此处的sock不只是一个地址
#b'hello' <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>
time.sleep(1)
sock.sendto(data.upper(),self.client_address) #self.client_address 客户端地址
if __name__ == '__main__':
server = socketserver.ThreadingUDPServer (('127.0.0.1', 8080),Base)
server.serve_forever()
客户端:
import time
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8080)
while True:
client.sendto(b'hello',server_addr)
data,addr = client.recvfrom(1024)
print(data,addr) #b'HELLO' ('127.0.0.1', 8080)
time.sleep(1)
SocketServer 模块补充
基于tcp的socketserver我们自己定义的类中的
- self.server即套接字对象
- self.request即一个链接
- self.client_address即客户端地址
基于udp的socketserver我们自己定义的类中的
- self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
- self.client_address即客户端地址
1、能够实现并发效果
并发:看起来像同时运行就称为并发 并行:同时运行 注:单核计算机无法实现并行
2、udp在使用的时候,多个客户端需要存在一些io操作,不然容易卡死
并发编程: 后续补充!