今天的内容其实不多,主要是因为后面讲到底层部分涉及了一些Linux知识,我这部分没看,后面的内容就没法听了,所以就先复习一下昨天关于粘包的处理方式的知识,自己重新写了一下这部分内容,然后大致的看了一下socketserver模块如何实现并发的一个过程,明天正式开始Linux的学习,已经到了不得不进行的程度了。
一、粘包问题的解决
low版处理:
服务端:
import socket import subprocess ip_port = ('127.0.0.1',8080) backlog = 5 buffer_size = 1024 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 用于解决time out占用问题 tcp_server.bind(ip_port) tcp_server.listen(backlog) while True: conn,addr = tcp_server.accept() print('客户端地址是:',addr) while True: msg = conn.recv(buffer_size) if not msg: break res = subprocess.Popen(msg.decode('utf-8'),shell = True, stdin = subprocess.PIPE, stderr = subprocess.PIPE, stdout = subprocess.PIPE) err = res.stderr.read() if err: # 表示收到了报错信息的情况下 ret = err else: ret =res.stdout.read() data_lengeth = len(ret) conn.send(str(data_lengeth).encode('utf-8')) data = conn.recv(buffer_size).decode('utf-8') # 客户反馈收到的确认信息 if data == 'recv_ready': conn.sendall(ret) conn.close()
客户端:
import socket ip_port = ('127.0.0.1',8080) buffer_size = 1024 tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcp_client.connect(ip_port) while True: msg = input('>>>').strip() if len(msg) == 0: continue if msg == 'quit': break tcp_client.send(msg.encode('utf-8')) length = int(tcp_client.recv(buffer_size).decode('utf-8')) tcp_client.send('recv_ready'.encode('utf-8')) send_size = 0 data = '' while send_size < length: data += tcp_client.recv(buffer_size) send_size = len(data) print(data.decode('utf-8'))
相对于昨天的还是进行了一些优化的,
然后是利用stuck模块实现的操作
服务端:
from socket import * import subprocess import struct ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_server=socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn,addr=tcp_server.accept() print('新的客户端链接',addr) while True: #收 try: cmd=conn.recv(buffer_size) if not cmd:break print('收到客户端的命令',cmd) #执行命令,得到命令的运行结果cmd_res res=subprocess.Popen(cmd.decode('utf-8'),shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) err=res.stderr.read() if err: cmd_res=err else: cmd_res=res.stdout.read() #发 if not cmd_res: cmd_res='执行成功'.encode('gbk') length=len(cmd_res) data_length=struct.pack('i',length) conn.send(data_length) conn.send(cmd_res) except Exception as e: print(e) break
客户端:
from socket import * import struct from functools import partial ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_client=socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd=input('>>: ').strip() if not cmd:continue if cmd == 'quit':break tcp_client.send(cmd.encode('utf-8')) #解决粘包 length_data=tcp_client.recv(4) length=struct.unpack('i',length_data)[0] recv_size=0 recv_data=b'' while recv_size < length: recv_data+=tcp_client.recv(buffer_size) recv_size=len(recv_data) print('命令的执行结果是 ',recv_data.decode('gbk')) tcp_client.close()
这样看起来就清晰的多了
然后就要开始今天内容了,上面这些操作很显然存在一个问题,在基于tcp的时候每次只能进行一个连接,那么这样就造成了很不现实的问题,一次只能服务一个客户端,基于此种需求,这个时候就引入了socketserver模块
先看一下,下面这个图就是大致的服务器socketserver模块的书写形式吧
下面实际测试的部分:
服务端
import socket import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print('链接信息:',self.request) # 相当于之前的conn print('地址信息:',self.client_address) # 相当于之前的addr while True: try: #收消息 data=self.request.recv(1024) if not data:break print('收到客户端的消息是',data,self.client_address) #发消息 self.request.sendall(data.upper()) except Exception as e: print(e) break if __name__ == '__main__': s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) #多线程 # s=socketserver.ForkingTCPServer(('127.0.0.1',8080),MyServer) #多进程 # self.server_address = server_address # self.RequestHandlerClass = RequestHandlerClass # print(s.server_address) # print(s.RequestHandlerClass) # print(MyServer) # print(s.socket) # print(s.server_address) s.serve_forever()
客户端:
from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_client=socket(AF_INET,SOCK_DGRAM) #数据报 while True: msg=input('>>: ').strip() udp_client.sendto(msg.encode('utf-8'),ip_port) data,addr=udp_client.recvfrom(buffer_size) # print(data.decode('utf-8')) print(data)
通过socketserver模块的导入,我们在服务端建立了一个新的类MyServer,而每个与其连接的客户端都是由MyServer生成的实例化对象,而收发消息的过程则会通过调用类的handle方法来实现。
Linux真的不学不行了,明天正式开始Linux的内容更新。