AID_python_网络并发通信

网络并发通信

1.常见网络通信模型

1.循环服务器模型 :循环接收客户端请求,处理请求。同一时刻只能处理一个请求,处理完毕后再处理下一个。
优点:实现简单,占用资源少
缺点:无法同时处理多个客户端请求

适用情况:处理的任务可以很快完成,客户端无需长期占用服务端程序。udp比tcp更适合循环。

2.多进程/线程网络并发模型:每当一个客户端连接服务器,就创建一个新的进程/线程为该客户端服务,客户端退出时再销毁该进程/线程。
优点:能同时满足多个客户端长期占有服务端需求,可以处理各种请求。
缺点: 资源消耗较大

适用情况:客户端同时连接量较少,需要处理行为较复杂情况。

3**.IO并发模型:利用IO多路复用,异步IO等技术,同时处理多个客户端IO请求**。

优点 : 资源消耗少,能同时高效处理多个IO行为
缺点 : 只能处理并发产生的IO事件,无法处理cpu计算

适用情况:HTTP请求,网络传输等都是IO行为。

2.基于fork的多进程网络并发模型

"""
    基于fork的多进程并发
    步骤:
        创建监听套接字
        等待接收客户端请求
        客户端连接创建新的进程处理客户端请求
        原进程继续等待其他客户端连接
        如果客户端退出,则销毁对应的进程
"""
import os
from socket import *
import signal

# 全局变量
HOST = "192.168.1.6"
POST = 8844
ADDR = (HOST, POST)


# 处理客户端请求函数
def handle(c, addr):
    while True:
        data = c.recv(1024)
        if not data:
            break
        print(data.decode())
        c.send(b"OK")
    c.close()


# 创建监听套接字
s = socket()

# 端口立即重用 (在bind之前)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

s.bind(ADDR)
s.listen(3)

print("Listen the port 8844")

# 处理僵尸进程
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

# 循环等待客户端链接
while True:
    c, addr = s.accept()
    print("Connect from", addr)

    # 创建一个新的进程处理客户端请求
    pid = os.fork()
    if pid == 0:
        # 处理客户端请求

        # 处理客户端请求函数
        handle(c, addr)

        # 子进程处理完客户端请求则退出
        os._exit(0)
    else:
        # 出错或者父进程都继续等待接受客户端链接
        continue

# 服务端退出
s.close()




"""
tcp_client.py
tcp客户端演示: 重点代码
"""

from socket import *

# 服务器地址
server_addr = ("192.168.1.6", 8844)

# 创建tcp套接字
sockfd = socket()  # 默认值就是tcp

# 连接服务器
sockfd.connect(server_addr)

# 发送接收消息
while True:
    data = input(">>")
    if not data:
        break
    sockfd.send(data.encode())
    # 输入##表示退出
    # if data == '##':
    #     break
    data = sockfd.recv(1024)
    print("From server:", data.decode())

sockfd.close()

2.基于threading的多线程网络并发

"""
创建监听套接字
循环接收客户端连接请求
当有新的客户端连接创建线程处理客户端请求
主线程继续等待其他客户端连接
当客户端退出,则对应分支线程退出
"""
from socket import *
from threading import Thread, Lock

HOST = "192.168.1.6"
POST = 8844
ADDR = (HOST, POST)


# 处理客户端请求函数
def use(c):
    while True:
        data = c.recv(1024)
        if not data:
            break
        print(data.decode())
        c.send(b"OK")
    c.close()


s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(4)
print("Listen the port 8844")

while True:
    c, addr = s.accept()
    print("Connect from", addr)
    t = Thread(target=use, args=(c,))
    t.setDaemon(True)
    t.start()
    # t.join()

s.close()


"""
tcp_client.py
tcp客户端演示: 重点代码
"""

from socket import *

# 服务器地址
server_addr = ("192.168.1.6", 8844)

# 创建tcp套接字
sockfd = socket()  # 默认值就是tcp

# 连接服务器
sockfd.connect(server_addr)

# 发送接收消息
while True:
    data = input(">>")
    if not data:
        break
    sockfd.send(data.encode())
    # 输入##表示退出
    # if data == '##':
    #     break
    data = sockfd.recv(1024)
    print("From server:", data.decode())

sockfd.close()

3.ftp 文件服务器

"""
FTP 文件处理
多线程并发和套接字练习
"""

from socket import *
from threading import Thread
import os
from time import sleep

# 全局变量
HOST = '0.0.0.0'
PORT = 8845
ADDR = (HOST, PORT)
# 文件库
FTP = "FTP/"


# 处理客户端请求 (自定义线程类)
class FTPServer(Thread):
    def __init__(self, connfd):
        self.connfd = connfd
        super().__init__()
        self.dict_func = {"E": self.f4, "L": self.f1, "P": self.f2, "G": self.f3}

    #  循环接收请求,分发任务
    def run(self):
        while True:
            data = self.connfd.recv(1024).decode()  # 接收请求
            print(data)
            if not data or data == 'E':
                # run函数结束对应线程结束
                break
            elif data == 'L':
                self.list()
            elif data[0] == 'G':
                self.get_file(data.split(" ", 1)[-1])
            elif data[0] == 'P':
                self.put(data.split(" ", 1)[-1])

    # 查看文件
    def list(self):
        # 判断文件库是否为空
        file_list = os.listdir(FTP)
        if not file_list:
            self.connfd.send('文件库为空'.encode())
            return
        else:
            self.connfd.send(b'OK')
            sleep(0.1)

        # 发送文件列表
        # 讲文件列表以‘\n’拼接,进行添加消息边界以处理沾包
        data = '\n'.join(file_list)
        self.connfd.send(data.encode())

    # 下载文件
    def get_file(self, file_name):
        file_path = FTP + file_name
        try:
            f = open(file_path, "rb")
        except:
            self.connfd.send("文件不存在".encode())
            return
        else:
            self.connfd.send(b"OK")
            sleep(0.1)
        while True:
            data = f.read(1024)
            if not data:
                sleep(0.1)
                self.connfd.send(b"##")
                break
            self.connfd.send(data)
        f.close()

    # 处理上传
    def put(self, filename):
        if os.path.exists(FTP + filename):
            self.connfd.send("文件已存在".encode())
            return
        else:
            self.connfd.send(b'OK')
        # 接收文件
        # 循环接收文件写入本地
        f = open(FTP + filename, 'wb')
        while True:
            data = self.connfd.recv(1024)
            # 文件接收完毕的标志
            if data == b'##':
                break
            f.write(data)
        f.close()


# 框架结构,启动函数
def main():
    # 创建监听套接字
    s = socket()
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind(ADDR)
    s.listen(3)

    print("Listen the port 8845")

    # 循环等待客户端链接
    while True:
        c, addr = s.accept()
        print("Connect from", addr)

        # 创建线程处理客户端请求
        t = FTPServer(c)  # 通过自定义线程类创建线程
        t.setDaemon(True)  # 分支线程随主线程退出
        t.start()

    s.close()


if __name__ == '__main__':
    main()



"""
    ftp_client
    【1】 分为服务端和客户端,要求可以有多个客户端同时操作。
    【2】 客户端可以查看服务器文件库中有什么文件。
    【3】 客户端可以从文件库中下载文件到本地。
    【4】 客户端可以上传一个本地文件到文件库。
    【5】 使用print在客户端打印命令输入提示,引导操作
"""
from socket import socket
import os, sys
from time import sleep

# 全局变量
ADDR = ("127.0.0.1", 8845)
FTP = "Use/"


class FTPClient:
    def __init__(self, sockfd):
        self.sockfd = sockfd

    def quit(self):
        self.sockfd.send(b'E')
        self.sockfd.close()
        sys.exit("谢谢使用!")

    def list(self):
        self.sockfd.send(b'L')
        # 等待回复
        data = self.sockfd.recv(128).decode()
        if data == 'OK':
            # 接收文件
            data = self.sockfd.recv(4096)
            print(data.decode())
        else:
            # 打印原因
            print(data)

    def get_file(self, file_name):
        data = "G " + file_name
        # 下载文件    G              filename
        self.sockfd.send(data.encode())
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            # 接收文件
            f = open(FTP + file_name, "wb")
            while True:
                data = self.sockfd.recv(1024)
                # print(data)
                # 文件接收完毕的标志
                if data == b"##":
                    print("OK")
                    break
                f.write(data)
            f.close()

    # 上传文件
    def put(self, filename):
        try:
            f = open(FTP + filename, 'rb')
        except:
            print("文件不存在")
            return
        data = "P " + filename
        self.sockfd.send(data.encode())  # 发送请求
        # 等待回复
        data = self.sockfd.recv(128).decode()
        if data == 'OK':
            while True:
                data = f.read(1024)
                if not data:
                    sleep(0.1)
                    self.sockfd.send(b'##')  # 结束标志
                    break
                self.sockfd.send(data)
            f.close()
        else:
            print(data)


# 启动函数
def main():
    # 链接服务端
    s = socket()
    s.connect(ADDR)

    # 实例化对象,用于调用类中的方法,实现与服务端的交互
    ftp = FTPClient(s)

    # 循环发送请求
    while True:
        print("===============命令选项===============")
        print("***             list              ***")
        print("***           get file            ***")
        print("***           put file            ***")
        print("***             quit              ***")
        print("=====================================")
        cmd = input("输入命令:")
        # s.send(cmd.encode())
        if cmd == 'quit':
            ftp.quit()
        elif cmd == 'list':
            ftp.list()
        elif cmd[:3] == 'get':
            file_name = cmd.split(" ", 1)[-1]
            ftp.get_file(file_name)
        elif cmd[:3] == 'put':
            file_name = cmd.split(" ", 1)[-1]
            ftp.put(file_name)
        else:
            pass


if __name__ == '__main__':
    main()

项目技术分析

FTP文件服务

  1. 技术点分析

    • 并发模型 : 多线程并发
    • 网络传输  : tcp网络

    客户端请求流程: 
    1.客户端发起请求
    2.服务端判断能否满足客户端请求,给出回复
    3.根据回复,确定下一步操作

  2. 功能模块划分 (结构设计)

    • 框架搭建
    • 查看文件
    • 下载文件
    • 上传文件

    封装: 核心功能封装为类

  3. 通信协议设定

          请求类型         参量
    

    查看文件 L

    上传文件 P filename

    下载文件 G filename

    退出 E

  4. 分模块设计功能逻辑流程

    • 框架搭建

    • 查看文件
      客户端: 发送请求
      等待接收反馈结果
      Y 接收文件列表
      N 结束

       服务端: 接收请求
            判断能否满足请求,将结果回复 (看文件库是否为空)
            Y 发送文件列表
               N  结束
      
    • 下载文件
      客户端 : 发送请求 (请求包含文件名字)
      等待接收反馈结果
      Y 接收文件
      N 结束

      服务端 : 接收请求,提取出文件名
               判断能否满足请求,将结果回复 (看文件库中是否有这个文件)
            Y 发送文件
               N  结束
      
    • 上传文件

    • 退出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值