一、利用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操作,不然容易卡死
并发编程: 后续补充!