python-sockertserver并发编程思想

  • Post author:
  • Post category:python


听了林海峰老师讲的sockertserver的总结

一、首先在socketserver模块中分两大类:

server类(解决链接问题)和request类(解决通信问题)

1、server类

2、request类:

二、我将先为大家讲解基于TCP的sockertserver的基本使用

服务端代码:

import socketserver


class MyRequestHandle(socketserver.BaseRequestHandler):
    def handle(self):
        print(self.request)  # 如果是tcp协议,self.request=>conn
        print(self.client_address)

        while True:
            try:
                msg=self.request.recv(1024)
                if len(msg)==0:break
                self.request.send(msg.upper())
            except Exception:
                break
        self.request.close()


'''
s.serve_forever()=>等同于以下代码
while True:
    conn,client_addr=server.accept()
    启动一个线程(conn,client_addr)
'''
# 服务端应该做两件事
# 1、第一件事:循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8888), MyRequestHandle)
s.serve_forever()

# 2、拿到链接对象,与其进行通信循环==>handle

客户端1代码:

from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8888))

while True:

    msg=input('请输入命令>>>>').strip()
    if len(msg)==0:
        continue
    client.send((msg.encode('utf-8')))

    recv_data=client.recv(1024)
    print(recv_data.decode('utf-8'))


客户端2代码:

from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8888))

while True:

    msg=input('请输入命令>>>>').strip()
    if len(msg)==0:
        continue
    client.send((msg.encode('utf-8')))

    recv_data=client.recv(1024)
    print(recv_data.decode('utf-8'))


运行结果:

# 服务端结果:
<socket.socket fd=812, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8888), raddr=('127.0.0.1', 63110)>
('127.0.0.1', 63110)
<socket.socket fd=692, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8888), raddr=('127.0.0.1', 63115)>
('127.0.0.1', 63115)

# 客户端1结果:
请输入命令>>>>asasa
ASASA
请输入命令>>>>ds
DS
请输入命令>>>>

# 客户端2结果:
请输入命令>>>>asas
ASAS
请输入命令>>>>

可以看出客户端1和客户端2是同时进行操作的,服务端同时接收到了两个消息

三、下面来为大家介绍基于UDP的sockertserver的并发思想

服务端代码:

import socketserver

class MyRequestHandle(socketserver.BaseRequestHandler):
    def handle(self):
        client_data=self.request[0]
        server=self.request[1]
        client_addr=self.client_address
        print('客户端发来的数据%s'%client_data)
        server.sendto(client_data.upper(),client_addr)

s=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyRequestHandle)
s.serve_forever()
# 相当于只负责循环的收
'''
s.serve_forever()=>等同于以下代码
while True:
    data,client_addr=server.accept()
    启动一个线程处理后续的事情(data,client_addr)
    
'''




客户端1代码:

import socket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 数据报协议=》udp协议
while True:
    msg=input('>>>>')
    client.sendto(msg.encode('utf-8'),("127.0.0.1",8080))
    data,server_addr=client.recvfrom(1024)
    print(data.decode('utf-8'),server_addr)
client.close()

客户端2代码:

import socket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 数据报协议=》udp协议
while True:
    msg=input('>>>>')
    client.sendto(msg.encode('utf-8'),("127.0.0.1",8080))
    data,server_addr=client.recvfrom(1024)
    print(data.decode('utf-8'),server_addr)
client.close()

运行结果为:

# 服务端代码:
客户端发来的数据b'asa'
客户端发来的数据b'asasa'
客户端发来的数据b'asasasa'

# 客户端1代码:
>>>>asa
ASA ('127.0.0.1', 8080)
>>>>asasa
ASASA ('127.0.0.1', 8080)
>>>>

#客户端2代码:
>>>>asasasa
ASASASA ('127.0.0.1', 8080)
>>>>

四、用sockertserver基于TCP实现多客户远程执行命令

服务端:

import socketserver
import subprocess
import struct
import json

class MyRequestHandle(socketserver.BaseRequestHandler):
    def handle(self):
        print(self.request)  # 如果是tcp协议,self.request=>conn
        # print(self.client_address)

        while True:
            try:
                cmd = self.request.recv(1024)
                if len(cmd) == 0:
                    break

                obj = subprocess.Popen(cmd.decode('utf-8'),  # pycharm默认格式为utf-8
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE
                                       )
                stdout_res = obj.stdout.read()
                stderr_res = obj.stderr.read()
                total_size = len(stdout_res) + len(stderr_res)
                header_dic = {

                    'total_size': total_size,
                    'client_address':self.client_address
                }
                print('客户端:%s 命令长度为:%d'%(header_dic['client_address'],header_dic['total_size']))
                json_str = json.dumps(header_dic)
                json_str_bytes = json_str.encode('utf-8')
                # 2、先发头的长度
                x = struct.pack('i', len(json_str_bytes))  # 将整形转换为bytes类型
                self.request.send(x)
                # 3、发头信息
                self.request.send(json_str_bytes)
                # 4再发真实的数据
                self.request.send(stdout_res + stderr_res)

            except Exception:
                break

        self.request.close()


'''
s.serve_forever()=>等同于以下代码
while True:
    conn,client_addr=server.accept()
    启动一个线程(conn,client_addr)
'''
# 服务端应该做两件事
# 1、第一件事:循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandle)
s.serve_forever()

# 2、拿到链接对象,与其进行通信循环==>handle

客户端1

from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))

while True:

    msg=input('请输入命令>>>>').strip()
    if len(msg)==0:
        continue
    client.send((msg.encode('utf-8')))
    # 解决粘包问题:
    # 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
    # 1、先接收4个字节,从中提取接下来要收的头的长度
    x = client.recv(4)
    header = struct.unpack('i', x)[0]
    # 2、接收头并解析
    json_str_bytes = client.recv(header)
    json_str = json_str_bytes.decode('utf-8')
    header_dic =json.loads(json_str)
    total_size = header_dic['total_size']
    # 3、接收真实数据

    recv_size=0
    while recv_size<total_size:
        recv_data=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
        recv_size+=len(recv_data)
        print(recv_data.decode('gbk'),end='')# windows默认是gbk格式,解码应为gbk格式
    else:
        print()

客户端2

from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))

while True:

    msg=input('请输入命令>>>>').strip()
    if len(msg)==0:
        continue
    client.send((msg.encode('utf-8')))
    # 解决粘包问题:
    # 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
    # 1、先接收4个字节,从中提取接下来要收的头的长度
    x = client.recv(4)
    header = struct.unpack('i', x)[0]
    # 2、接收头并解析
    json_str_bytes = client.recv(header)
    json_str = json_str_bytes.decode('utf-8')
    header_dic =json.loads(json_str)
    total_size = header_dic['total_size']
    # 3、接收真实数据

    recv_size=0
    while recv_size<total_size:
        recv_data=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
        recv_size+=len(recv_data)
        print(recv_data.decode('gbk'),end='')# windows默认是gbk格式,解码应为gbk格式
    else:
        print()

运行结果:

# 服务端结果:
<socket.socket fd=380, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 64118)>
客户端:('127.0.0.1', 64118) 命令长度为:20828
<socket.socket fd=752, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 64121)>
客户端:('127.0.0.1', 64121) 命令长度为:583

# 客户端1结果:
请输入命令>>>>tasklist

映像名称                       PID 会话名              会话#       内存使用 
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0          8 K
System                           4 Services                   0         84 K
Registry                       124 Services                   0     63,832 K
smss.exe                       520 Services                   0      1,036 K
csrss.exe                      736 Services                   0      5,152 K
wininit.exe                    824 Services                   0      5,752 K
csrss.exe                      836 Console                    1      6,856 K
services.exe                   904 Services                   0      9,748 K
lsass.exe                      924 Services                   0     24,384 K
svchost.exe                    528 Services                   0     33,208 K
fontdrvhost.exe                576 Services                   0      2,452 K
svchost.exe                    936 Services                   0     16,768 K
svchost.exe                   1044 Services                   0     10,024 K
winlogon.exe                  1212 Console                    1     12,444 K
fontdrvhost.exe               1276 Console                    1     10,464 K
dwm.exe                       1356 Console                    1     92,164 K
svchost.exe                   1416 Services                   0     10,700 K
svchost.exe                   1516 Services                   0      9,760 K
svchost.exe                   1528 Services            
                。。。。。。。。。。。。。。。。。。。。。。。
conhost.exe                   3528 Console                    1     11,800 K
cmd.exe                       8540 Console                    1      5,092 K
tasklist.exe                 15936 Console                    1      9,652 K

请输入命令>>>>

# 客户端2
请输入命令>>>>dir
 驱动器 D 中的卷是 所有文件软件丢到这里来
 卷的序列号是 C0F9-5D0A

 D:\新建文件夹\pythonProject1\shujialianxi\基于socketserver模板实现并发\基于TCP 的目录

2021/11/25  00:59    <DIR>          .
2021/11/25  00:59    <DIR>          ..
2021/11/25  00:01    <DIR>          sockertserver基本使用
2021/11/24  11:46                 0 __init__.py
2021/11/24  11:44               328 客户端.py
2021/11/24  11:44               328 客户端2.py
2021/11/25  00:59             2,171 服务端.py
               4 个文件          2,827 字节
               3 个目录 37,787,205,632 可用字节

请输入命令>>>>



版权声明:本文为m0_51734025原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。