Python selectors实现ftp并发

使用selectors实现了ftp并发,实现多个客户端同时链接,同时上传下载文件,但是不能同时上传下载同一个文件。

只是简单的实现了功能,仅供参考。

目录

在这里插入图片描述

服务端

import selectors,socket,struct,json,os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class SelSocket:
    def __init__(self):
        self.sel = selectors.DefaultSelector()   #根据系统
        self.fileinfo = {}                          #主要是将文件的信息与conn联系起来
        self.has_recv = {}                      #新文件上传时置空
        self.create_ser()
        self.handle()
    def create_ser(self):
        ser = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        ser.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        ser.bind(('192.2.12.00', 8090))     #改为自己的ip
        ser.listen(5)
        ser.setblocking(False)                 # socket对象设置为非阻塞
        self.sel.register(ser, selectors.EVENT_READ, self.accept) #注册

        print('servering..........')
        
    def handle(self):                              #就这么写
        while True:
            events = self.sel.select()               #检测
            for key, mask in events:
                callback = key.data
                callback(key.fileobj, mask)

    def accept(self,ser,mask):
        conn,addr = ser.accept()
        print('receive connect %s from %s' %(conn,addr))
        conn.setblocking(False)                   #链接也设为非阻塞
        self.sel.register(conn,selectors.EVENT_READ,self.read)
        self.fileinfo[conn]={}

    def read(self,conn,mask):  #主要逻辑

        try:
            if not self.fileinfo[conn]:
                msg = conn.recv(1024).decode('utf-8')
                self.fileinfo.update({conn:json.loads(msg)})
            #fileinfo = {conn:{'cmd':put,'filename':'filename','filesize':filesize}}
                if self.fileinfo[conn]['cmd'] == 'put':
                    conn.sendall('True'.encode('utf-8'))
                elif self.fileinfo[conn]['cmd'] == 'download':
                    self.file = os.path.join(BASE_DIR,'download',self.fileinfo[conn]['filename'])
                    if os.path.exists(self.file):
                        filesize = int(os.path.getsize(self.file))
                        fileinfo = json.dumps({'stat':'True','filesize':filesize}).encode('utf-8')
                        conn.sendall(fileinfo)
                    else:
                        fileinfo = json.dumps({'stat':'False','filesize':0}).encode('utf-8')
                        conn.sendall(fileinfo)
                        self.fileinfo[conn] = {}
            else:
                cmd = self.fileinfo[conn]['cmd']
                if hasattr(self,cmd):
                    func = getattr(self,cmd)
                    func(conn)
                else:
                    conn.sendall('cmd error!'.encode('utf-8'))
                    conn.close()
        except Exception as e:                      #当发生错误时异常断开链接时
            print(e)
            self.sel.unregister(conn)
            conn.close()
            if self.has_recv.get(conn):
                del self.has_recv[conn]
    def put(self,conn):
        filename = self.fileinfo[conn]['filename']
        filesize = self.fileinfo[conn]['filesize']
        path = os.path.join(BASE_DIR,'upload',filename)
        data = conn.recv(1024)
        print(data)
        if not data:
            raise Exception
        if not self.has_recv.get(conn):
            self.has_recv[conn] = 0
        self.has_recv[conn] += len(data)
        with open(path,'ab') as f:
            f.write(data)
        if filesize == self.has_recv[conn]:
            self.fileinfo[conn] = {}
            del self.has_recv[conn]
            print('%s 上传完成!'%filename)

    def download(self,conn):
        back_msg = conn.recv(1024).decode('utf-8')
        if back_msg == 'OK':
            with open(self.file,'rb') as f:
                for line in f:
                    conn.sendall(line)
            self.fileinfo[conn] = {}
            print('传输完成')

if __name__ == '__main__':
    SelSocket()

客户端

import socket
import os,sys,json
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

class SelClient:
    def __init__(self):
        self.create_cli()
        self.run()

    def create_cli(self):
        self.cli = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.cli.connect(('192.2.12.00', 8090))
        print('链接成功!')

    def run(self):
        while True:
            try:
                msg = input('>>').strip()
                if not msg:continue
                cmd = msg.split()[0]
                filename = msg.split()[1]
                # filesize = os.path.abspath(filename).split('/')[-1]
                # fileinfo = {'cmd':cmd,'filename':filename,'filesize':filesize}
                if hasattr(self,cmd):
                    func = getattr(self,cmd)
                    func(cmd,filename)
            except Exception as e:
                print(e)

    def put(self,cmd,filename):
        if os.path.exists(filename):
            file_name = os.path.basename(filename)
            file_size = int(os.path.getsize(filename))
            file_info = json.dumps({'cmd':cmd,'filename':file_name,'filesize':file_size})
            self.cli.sendall(file_info.encode('utf-8'))
            backmsg = self.cli.recv(1024).decode('utf-8')
            if backmsg == 'True':
                has_send = 0
                with open(filename,'rb') as f:
                    while has_send < file_size:
                        data = f.read(1024)
                        self.cli.sendall(data)
                        has_send += len(data)
                        percent = str(int(has_send*100/file_size))+'%'
                        print(file_name + " 已经上传:" + percent)
        else:
            print('文件不存在!')

    def download(self,cmd,filename):
        file_path = os.path.join(BASE_DIR,'download',filename)
        file_info = json.dumps({'cmd':cmd,'filename':filename})
        self.cli.sendall(file_info.encode('utf-8'))
        stat = json.loads(self.cli.recv(1024).decode('utf-8'))
        if stat['stat'] == 'True':
            self.cli.sendall('OK'.encode('utf-8'))
            has_download = 0
            with open(file_path,'ab') as f:
                while has_download < stat['filesize']:
                    data = self.cli.recv(8192)
                    f.write(data)
                    has_download += len(data)
                    percent = str(int(has_download*100/stat['filesize']))+'%'
                    print(filename+' 已经下载:'+percent)
                print('文件下载完毕!!!')
        else:
            print('文件不存在!!!')
if __name__ == '__main__':
    SelClient()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Python 中,实现 Socket 的高并发可以使用多种方式,其中 `select` 和 `selectors` 是比较常用的两种方式。 1. 使用 `select` 实现 Socket 并发 使用 `select` 实现 Socket 的高并发,可以按照以下步骤进行: 1) 创建一个 Socket,设置为非阻塞模式,如下所示: ``` import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setblocking(False) ``` 2) 绑定 Socket 地址和端口,并开始监听,如下所示: ``` server_socket.bind(('localhost', 8000)) server_socket.listen(128) ``` 3) 创建一个 `select` 对象,并将监听的 Socket 加入到该对象中,如下所示: ``` import select rlist = [server_socket] wlist = [] xlist = [] while True: rs, ws, xs = select.select(rlist, wlist, xlist) for r in rs: if r is server_socket: conn, addr = server_socket.accept() conn.setblocking(False) rlist.append(conn) else: data = r.recv(1024) if data: # 处理接收到的数据 else: r.close() rlist.remove(r) ``` 4) 在 `select` 对象的 `select()` 方法中,使用 `rlist` 参数来监听读事件,使用 `wlist` 参数来监听写事件,使用 `xlist` 参数来监听异常事件。如果有 Socket 可读、可写或者出现异常,`select()` 方法就会返回对应的列表。 5) 在返回的可读列表中,如果是监听的 Server Socket,说明有新的连接请求,需要调用 `accept()` 方法接收连接,并将新的 Socket 加入到 `rlist` 列表中;如果是普通的 Socket,说明有数据到来,需要调用 `recv()` 方法接收数据,并进行相应的处理。 2. 使用 `selectors` 实现 Socket 并发 使用 `selectors` 实现 Socket 的高并发,可以按照以下步骤进行: 1) 创建一个 Socket,并将其注册到 `selectors` 对象中,如下所示: ``` import selectors import socket sel = selectors.DefaultSelector() server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 8000)) server_socket.listen(128) server_socket.setblocking(False) sel.register(server_socket, selectors.EVENT_READ, data=None) ``` 2) 在 `selectors` 对象的 `select()` 方法中等待事件,如下所示: ``` while True: events = sel.select(timeout=None) for key, mask in events: if key.data is None: # 处理 Server Socket 的连接请求 else: # 处理普通 Socket 的数据读取和写入 ``` 3) 注册 Server Socket 的时候,将 `data` 参数设置为 `None`,在处理连接请求时,可以将 `data` 参数设置为新连接的 Socket 对象;在处理普通 Socket 的数据读取和写入时,可以通过 `key.data` 获取到该 Socket 对应的数据。 4) 在处理普通 Socket 的数据读取和写入时,可以通过 `mask` 参数来判断是读事件还是写事件,如果是读事件,就调用 `recv()` 方法接收数据,如果是写事件,就调用 `send()` 方法发送数据。 以上就是使用 `select` 和 `selectors` 实现 Socket 并发的步骤,具体实现时需要根据具体情况进行修改。 ### 回答2: 在Python中,要实现并发的socket编程,可以使用selectors模块。 selectors模块提供了一种应对I/O多路复用的高效方式,它基于select或者epoll等系统调用,可以同时监视多个文件对象(如socket),并在有数据到来时进行处理。 首先,我们需要创建一个selectors对象,并注册要监视的socket对象。可以使用默认的selectors.DefaultSelector()来创建一个selector对象。 接下来,可以使用selectors对象的register方法来注册要监视的socket对象。register方法接受一个socket对象和一个事件类型(如selectors.EVENT_READ用于读事件),并将其添加到selector对象的监视列表中。 然后,可以使用selectors对象的select方法来等待事件的发生。select方法会一直阻塞直到有事件发生,并返回一个事件列表。 最后,可以遍历事件列表,根据事件的类型来处理不同的操作。例如,如果事件类型为selectors.EVENT_READ,则可以调用socket对象的recv方法来接收数据。 使用selectors模块能够实现并发的socket编程,可以提高程序的性能和效率。同时,selectors模块还提供了一些其他功能,比如超时处理、取消注册等,可以根据实际需要进行使用。 总之,selectors模块是Python实现socket并发的一种高效方式,通过使用其提供的方法和功能,可以轻松实现并发的socket编程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值