Python学习第36天(socketserver模块导入)

  今天的内容其实不多,主要是因为后面讲到底层部分涉及了一些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的内容更新。

猜你喜欢

转载自www.cnblogs.com/xiaoyaotx/p/12595984.html