Python网络编程(socketserver、TFTP云盘、HTTPServer服务器模型)

简介: Python网络编程 Python小项目 Python网盘 Python HTTP请求服务端

HTTP?

HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。

通常承载于TCP协议之上,有时也承载于TLSSSL协议层之上,这个时候,就成了我们常说的HTTPS

默认HTTP的端口号为80HTTPS的端口号为443

    what?  无状态什么鬼?
        HTTP无状态协议是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,
        则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快
        由于web等客户端与服务器交互的应用程序出现后HTTP的无状态严重阻碍了这些应用的实现效率 说以就产生了cookie和Session
cookie:
当用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;
接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体(Response Body)中的,
而是存放于HTTP响应头(Response Header);当客户端浏览器接收到来自服务器的响应之后,
浏览器会将这些信息存放在一个统一的位置,对于Windows操作系统而言,
我们可以从: [系统盘]:\Documents and Settings\[用户名]\Cookies目录中找到存储的Cookie;自此,客户端再向服务器发送请求的时候,
都会把相应的Cookie再次发回至服务器。而这次,Cookie信息则存放在HTTP请求头(Request Header)了。
Session :
所谓session就是指客户端与服务端之间的一种交互过程的状态信息(数据) 这个状态的定界以及生命期是应用本身的事情
当一个用户向服务器发送第一个请求时,服务器为其建立一个session 并且会给这个session创建一个标识号
这个用户随后的请求都应该包括这个标识好服务器会对这个标识判断请求属于哪一个session
这种机制不使用IP作为标识,因为很多机器是代理服务器上网,无法区分主机 可以用cookie和URL重写来实现session标识号(sessionID)
URL只是一个统称 实际上是URI包含URL和URN由于URN用的非常少 几乎说有的URI都是URL所以人们更喜欢叫URL

os.listdir(path)
          获取文件 列表

os.path.isfile() : 
           判断一个 文件是否为 普通文件
os.path.isdir() :
           判断一个文件是否为 目录 
TFTP 文件服务器

项目功能 : 

    * 客户端有简单的页面命令提示
    * 功能包含:
               1. 查看服务器文件库中的文件列表(普通文件)
               2. 可以下载其中的某个文件到本地
               3. 可以上传客户端文件到服务器文件库

     * 服务器需求 :
               1. 允许多个客户端同时操作
               2.每个客户端可能回连续发送命令

技术分析:
       1. tcp套接字更适合文件传输
       2. 并发方案  ---》 fork 多进程并发
       3. 对文件的读写操作
       4. 获取文件列表 ----》 os.listdir() 
       粘包的处理

整体结构设计
       1. 服务器功能封装在类中(上传,下载,查看列表)
       2. 创建套接字,流程函数调用  main()
       3. 客户端负责发起请求,接受回复,展示
       服务端负责接受请求,逻辑处理

编程实现
       1. 搭建整体结构,创建网络连接
       2. 创建多进程和类的结构
       3. 每个功能模块的实现


服务器端:


from socket import *
import os 
import signal 
import sys 
import time 

# 文件库
FILE_PATH = "/home/tarena/"

# 实现功能模块
class TftpServer(object):
    def __init__(self,connfd):
        self.connfd = connfd 

        # 查询
    def do_list(self):
        # 获取列表
        file_list = os.listdir(FILE_PATH)
        if not file_list:
            self.connfd.send("文件库为空".encode())
            # 服务器目录无文件
            return
        else:
            # 有文件
            self.connfd.send(b'OK')
            time.sleep(0.1)

        files = ""
        for file in file_list:
            # 发送所有普通文件的文件名并且不是隐藏文件
            if os.path.isfile(FILE_PATH+file) and file[0] != '.':
                # 文件名间隔符 用于客户端解析
                files = files + file + '#'
            # 一次全部发送 简单粗暴
        self.connfd.send(files.encode())

        # 下载
    def do_get(self,filename):
        # 判断文件是否纯在
        try:
            fd = open(FILE_PATH + filename,'rb')
        except:
            self.connfd.send("文件不存在".encode())
            return
        self.connfd.send(b'OK')
        time.sleep(0.1)
        # 发送文件
        try:
            while True:
                data = fd.read(1024)
                if not data:
                    break
                self.connfd.send(data)
        except Exception as e:
            print(e)
        time.sleep(0.1)
        self.connfd.send(b'##')  # 表示文件发送完成
        print("文件发送完毕")

        # 上传
    def do_put(self,filename):
        # 限制文件命重复导致覆盖源文件
        try:
            fd = open(FILE_PATH+filename,'xb')
        except:
            self.connfd.send("无法上传".encode())
            return 
        except FileExistsError:
            self.connfd.send("文件已存在".encode())
            return 
        self.connfd.send(b'OK')
        # 上传文件
        while True:
            data = self.connfd.recv(1024)
            if data == b'##':
                break
            fd.write(data)
        fd.close()
        print("文件上传完毕")

# 流程控制,创建套接字,创建并发,方法调用
def main():
    HOST = '0.0.0.0'
    PORT = 8888
    ADDR = (HOST,PORT)
    # 创建套接字
    sockfd = socket()
    sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sockfd.bind(ADDR)
    sockfd.listen(5)
    # 忽略子进程退出
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    # 循环等待客户端链接
    while True:
        try: 
            connfd, addr = sockfd.accept()
        except KeyboardInterrupt:
            sockfd.close()
            sys.exit("服务器退出")
        except Exception as e:
            print(e)
            continue
        print("客户端登录:",addr)

        # 创建父子进程
        pid = os.fork()
        # 进入子进程
        if pid == 0:
            # 关闭子进程内无用套接字
            sockfd.close()
            tftp = TftpServer(connfd)  # __init__传参
            while True:
                data = connfd.recv(1024).decode()
                    # 断开连接
                if (not data) or data[0] == 'Q':
                    print("客户端退出")
                    sys.exit(0)
                elif data[0] == "L":
                    # 申请查询
                    tftp.do_list()
                elif data[0] == 'G':
                    # 解析文件名
                    filename = data.split(' ')[-1]
                    # 申请下载
                    tftp.do_get(filename)
                elif data[0] == 'P':
                    filename = data.split(' ')[-1]
                    # 申请上传
                    tftp.do_put(filename)       
                else:
                    print("客户端发送错误指令")
        else:
            # 关闭父进程内无用套接字 
            connfd.close()
            # 父进程只用来做客户端链接
            continue


if __name__ == "__main__":
    main()


客户端:

from socket import *
import sys 
import time 

# 实现各种功能请求
class TftpClient(object):
    def __init__(self,sockfd):
        self.sockfd = sockfd 

    def do_list(self):
        self.sockfd.send(b'L')  # 发送请求类型
        # 接收服务器回应
        data = self.sockfd.recv(1024).decode()
        if data == "OK":
            data = self.sockfd.recv(4096).decode()
            files = data.split('#')
            for file in files:
                print(file)
            print("文件展示完毕")
        else:
            # 请求失败原因
            print(data)

        # 下载指定文件
    def do_get(self,filename):
        self.sockfd.send(('G ' + filename).encode())
        data = self.sockfd.recv(1024).decode()
        # 请求成功
        if data == 'OK':
            fd = open(filename,'wb')
            while True:
                data = self.sockfd.recv(1024)
                # 结束符
                if data == b'##':
                    break
                fd.write(data)
            fd.close()
            print("%s 下载完成\n"%filename)
        else:
        # 请求失败原因
            print(data)

    def do_put(self,filename):
        # 判断本地是否有要上传的文件
        try:
            fd = open(filename,'rb')
        except:
            print("上传文件不存在")
            return 
        self.sockfd.send(("P "+filename).encode())
        data = self.sockfd.recv(1024).decode()
        # 请求成功
        if data == 'OK':
            while True:
                data = fd.read(1024)
                if not data:
                    break
                self.sockfd.send(data)
            fd.close()
            # 发送结束符并防止粘包
            time.sleep(0.1)
            self.sockfd.send(b'##')
            print("%s 上传完毕"%filename)
        else:
            # 请求失败原因
            print(data)


# 创建套接字并建立连接
def main():
    # 终端输入地址
    if len(sys.argv) < 3:
        print("argv is error")
        return
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
    ADDR = (HOST,PORT)

    sockfd = socket()
    sockfd.connect(ADDR)
    # 创建对象
    tftp = TftpClient(sockfd)   

    while True:
        print("")
        print("==========命令选项===========")
        print("**********  list  *********")
        print("********** get file  ******")
        print("********** put file  ******")
        print("**********  quit  *********")
        print("=============================")

        cmd = input("输入命令>>")
        # 去除空格判断命令
        if cmd.strip() == "list":
            # 查询
            tftp.do_list()
        # 获取文件上传或下载命令
        elif cmd[:3] == "get":
            # 拆分命令获取文件名
            filename = cmd.split(' ')[-1]
            # 下载
            tftp.do_get(filename)
        elif cmd[:3] == "put":
            filename = cmd.split(' ')[-1]
            # 上传
            tftp.do_put(filename)
            # 退出
        elif cmd.strip() == "quit":
            sockfd.send(b'Q')
            sockfd.close()
            sys.exit("欢迎使用")
        else:
            print("请输入正确命令!")


if __name__ == "__main__":
    main()





多线程并发

threading模块完成多线程并发

对比多进程并发
     优势 :
            资源消耗少
     缺点 : 
            需要注意对共享资源的操作

实现步骤:
       1. 创建套接字,绑定,监听
       2. 接收客户端连接请求,创建新的线程
       3. 主线程继续等待其他客户端连接,分支线程执行客户端具体请求
       4. 处理完客户端请求后分支线程自然退出,关闭客户端套接字
示例:

from socket import * 
import os,sys 
from threading import * 

HOST = "0.0.0.0"
PORT = 8888
ADDR = (HOST,PORT)

#客户端处理函数
def handler(connfd):
    print("Connect from",connfd.getpeername())
    while True:
        data = connfd.recv(1024).decode()
        if not data:
            break
        print(data)
        connfd.send(b'Receive your msg')
    connfd.close()


def main(ADDR):
    s = socket()
    s.bind(ADDR)
    s.listen(5)

    while True:
        try:
            connfd,addr = s.accept()
            # 处理 Ctrl + C 
        except KeyboardInterrupt:
            s.close()
            sys.exit("服务器退出")
            # 其他异常
        except Exception as e:
            print(e)
            continue 
            # 创建子线程用于处理客户端请求
        t = Thread(target=handler,args= (connfd,))
        t.setDaemon(True)
        t.start()

if __name__ == __main__:
    main()




socket并发集成模块

python2  SocketServer
python3  socketserver

          功能 : 
                 通过模块提供的接口组合可以完成多进程/多线程  tcp/udp的         并发程序

StreamRequestHandler       处理tcp请求
DatagramRequestHandler  处理udp请求

ForkingMixIn       创建多进程
ThreadingMixIn  创建多线程
 
TCPServer   创建tcp  server
UDPServer  创建udp  server

ForkingTCPServer       等于    ForkingMixIn  +  TCPServer 
ForkingUDPServer      等于    ForkingMixIn  +  UDPServer 
ThreadingTCPServer   等于   ThreadingMixIn  +  TCPServer 
ThreadingUDPServer  等于   ThreadingMixIn  +  UDPServer 

示例:
#多进程 tcp server
from socketserver import * 

#创建server类
# class Server(ForkingMixIn,TCPServer):
# class Server(ForkingTCPServer):
#     pass 

#多线程tcp并发
class Server(ThreadingTCPServer):
    pass


#具体的请求处理类
class Handler(StreamRequestHandler):
    def handle(self):
        # self.request ==> accept返回的套接字
        print("Connect from",self.request.getpeername())
        while True:
            data = self.request.recv(1024).decode()
            if not data:
                break
            print(data)
            self.request.send(b'Receive')


if __name__ == __main__:
    #创建server对象
    server = Server(("0.0.0.0",8888),Handler)

    #启动服务器
    server.serve_forever()




基于多线程并发的HTTPServer

       1. 接收浏览器http请求
       2. 对请求进行一定的解析
       3. 根据解析结果返回对应内容
       4. 如果没有请求内容则返回404
       5. 组织Response格式进行回发

升级:
       * 使用多线程并发
       * 增加了具体的请求解析和404情况
       * 使用类进行代码封装
       * 增加一定的数据获取功能

技术点 : threading并发
                tcp socket 传输
        HTTP请求和响应格式

相比上次升级了一点点

from socket import * 
from threading import Thread 
import time 

# 存放静态页面的目录
STATIC_DIR = "./static"
ADDR = ('0.0.0.0', 8000)

# HTTPServer类,封装具体功能
class HTTPServer(object):
    def __init__(self, address):
        # 创建套接字
        self.sockfd = socket()
        # 设置端口重用
        self.sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        self.sockfd.bind(address)
        self.sockfd.listen(5)
        # 为对象增加属性变量
        self.name = "HTTPServer"
        self.port = address[1]
        self.address = address 

    # 启动服务器
    def serve_forever(self):
        print("Listen the port %d"%self.port)
        while True:
            # 循环接收客户端请求并创建新的套接字
            connfd, addr = self.sockfd.accept()
            # 创建线程并运行处理具体请求
            clientThread = Thread(target = self.handleRequest,args = (connfd,))
            # 主线程结束时结束线程
            clientThread.setDaemon(True)
            clientThread.start()

    def handleRequest(self, connfd):
        # 接收客户端请求
        request = connfd.recv(4096)
        # 按行切割 字符串
        requestHeadlers = request.splitlines()
        # 获取请求行
        print(connfd.getpeername(), ":" , requestHeadlers[0]) 
        # 获取请求内容并解析
        getRequest = str(requestHeadlers[0]).split(' ')[1]
        # 并判断请求类型
        if getRequest == '/' or getRequest[-5:] == '.html':
            # 请求行为网页请求
            data = self.get_html(getRequest)
        else:
            # 请求指定数据内容
            data = self.get_data(getRequest)
            # 响应请求并返还内容
        connfd.send(data.encode())
        connfd.close()

        # 用于处理网页请求
    def get_html(self,page):
        # 判断是否为主页请求
        if page == "/":
            filename = STATIC_DIR + "/index.html"
        else:
            filename = STATIC_DIR + page

        try:
            f = open(filename)
        except Exception:
            # 没有找到页面
            responseHeadlers = "HTTP/1.1 404 Not Found\r\n"
            responseHeadlers += "Content-Type: text/html\r\n"
            responseHeadlers += '\r\n'
            responseBody = "<h1>Sorry,not found the page</h1>"
        else:
            responseHeadlers = "HTTP/1.1 200  OK\r\n"
            responseHeadlers += "Content-Type: text/html\r\n"
            responseHeadlers += '\r\n'
            for i in f:
                responseBody += i
        # 页面存不存在否响应
        finally:
            return responseHeadlers + responseBody

        # 用于处理数据内容请求
    def get_data(self,data):
        responseHeadlers = "HTTP/1.1 200 OK\r\n"
        responseHeadlers += "\r\n"

        if data == "/time":
            responseBody = time.ctime()
        elif data == "/ParisGabriel":
            responseBody = "Welcome to ParisGabriel"
        else:
            responseBody = "The data not found"
        return responseHeadlers + responseBody


if __name__ == "__main__":
    # 生成服务器对象
    httpd = HTTPServer(ADDR)
    # 启动服务器
    httpd.serve_forever()


相关文章
|
2月前
|
人工智能 JavaScript API
零基础构建MCP服务器:TypeScript/Python双语言实战指南
作为一名深耕技术领域多年的博主摘星,我深刻感受到了MCP(Model Context Protocol)协议在AI生态系统中的革命性意义。MCP作为Anthropic推出的开放标准,正在重新定义AI应用与外部系统的交互方式,它不仅解决了传统API集成的复杂性问题,更为开发者提供了一个统一、安全、高效的连接框架。在过去几个月的实践中,我发现许多开发者对MCP的概念理解透彻,但在实际动手构建MCP服务器时却遇到了各种技术壁垒。从环境配置的细节问题到SDK API的深度理解,从第一个Hello World程序的调试到生产环境的部署优化,每一个环节都可能成为初学者的绊脚石。因此,我决定撰写这篇全面的实
537 67
零基础构建MCP服务器:TypeScript/Python双语言实战指南
|
2月前
|
JSON 监控 API
在线网络PING接口检测服务器连通状态免费API教程
接口盒子提供免费PING检测API,可测试域名或IP的连通性与响应速度,支持指定地域节点,适用于服务器运维和网络监控。
|
3月前
|
存储 弹性计算 固态存储
阿里云服务器ESSD Entry和ESSD等云盘解析:区别、应用场景与选择参考
阿里云服务器提供了包括ESSD Entry、ESSD、SSD云盘、高效云盘等丰富多样的云盘类型,以满足不同用户在不同业务场景下的需求。每种云盘都有其独特的性能特点和适用场景,为了帮助用户更好地理解和选择云盘,本文将详细解析阿里云服务器各个云盘的定义、区别、选择参考以及常见问题。让大家对阿里云服务器不同云盘的性能和适用场景有更全面的了解,尤其是ESSD Entry云盘和ESSD云盘,并能够根据自己的需求做出最适合自己的选择。
|
2月前
|
机器学习/深度学习 存储 监控
内部文件审计:企业文件服务器审计对网络安全提升有哪些帮助?
企业文件服务器审计是保障信息安全、确保合规的关键措施。DataSecurity Plus 是由卓豪ManageEngine推出的审计工具,提供全面的文件访问监控、实时异常告警、用户行为分析及合规报告生成功能,助力企业防范数据泄露风险,满足GDPR、等保等多项合规要求,为企业的稳健发展保驾护航。
|
3月前
|
存储 运维 API
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
75 1
|
4月前
|
存储 弹性计算 安全
阿里云服务器ECS实例选购参考:vCPU到云盘IOPS等指标详解
阿里云服务器ECS实例可以分为多种实例规格族,而根据CPU、内存等配置的不同,一种实例规格族又进一步细分为多种实例规格。这些实例规格包含了众多关键的性能指标,如 vCPU、处理器、内存、vTPM、本地存储、网络带宽、网络收发包 PPS、连接数、弹性网卡、云盘带宽、云盘 IOPS 等。深入理解这些性能指标,对于用户在阿里云服务器购买过程中选择最适合自己业务需求的实例规格至关重要。
|
5月前
|
存储 缓存 监控
阿里云服务器配置与云盘容量选择参考:实例规格、云盘等相关配置选择解析
对于初次接触云服务器的用户来说,面对众多配置选项和云盘容量选择,可能会不知道如何选择。有些用户甚至不清楚云服务器应该购买多大容量的云盘,也不知道哪一款配置的云服务器更适合自己的业务。本文将详细探讨这两个问题,并结合阿里云服务器的特点,为您提供一份云服务器配置与云盘容量选择指南,以供了解和选择参考。
|
1月前
|
存储 弹性计算 网络协议
阿里云服务器ECS实例规格族是什么?不同规格CPU型号、处理器主频及网络性能参数均不同
阿里云ECS实例规格族是指具有不同性能特点和适用场景的实例类型集合。不同规格族如计算型c9i、通用算力型u1、经济型e等,在CPU型号、主频、网络性能、云盘IOPS等方面存在差异。即使CPU和内存配置相同,性能参数和价格也各不相同,适用于不同业务需求。
|
1月前
|
人工智能 自然语言处理 安全
Python构建MCP服务器:从工具封装到AI集成的全流程实践
MCP协议为AI提供标准化工具调用接口,助力模型高效操作现实世界。
401 1
|
1月前
|
存储 监控 Linux
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
40 0

推荐镜像

更多