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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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月前
|
机器学习/深度学习 人工智能 运维
企业内训|LLM大模型在服务器和IT网络运维中的应用-某日企IT运维部门
本课程是为某在华日资企业集团的IT运维部门专门定制开发的企业培训课程,本课程旨在深入探讨大型语言模型(LLM)在服务器及IT网络运维中的应用,结合当前技术趋势与行业需求,帮助学员掌握LLM如何为运维工作赋能。通过系统的理论讲解与实践操作,学员将了解LLM的基本知识、模型架构及其在实际运维场景中的应用,如日志分析、故障诊断、网络安全与性能优化等。
88 2
|
3天前
|
缓存 负载均衡 监控
HTTP代理服务器在网络安全中的重要性
随着科技和互联网的发展,HTTP代理IP中的代理服务器在企业业务中扮演重要角色。其主要作用包括:保护用户信息、访问控制、缓存内容、负载均衡、日志记录和协议转换,从而在网络管理、性能优化和安全性方面发挥关键作用。
17 2
|
4天前
|
监控 安全 BI
什么是零信任模型?如何实施以保证网络安全?
随着数字化转型,网络边界不断变化,组织需采用新的安全方法。零信任基于“永不信任,永远验证”原则,强调无论内外部,任何用户、设备或网络都不可信任。该模型包括微分段、多因素身份验证、单点登录、最小特权原则、持续监控和审核用户活动、监控设备等核心准则,以实现强大的网络安全态势。
|
9天前
|
弹性计算 安全 开发工具
灵码评测-阿里云提供的ECS python3 sdk做安全组管理
批量变更阿里云ECS安全组策略(批量变更)
|
1月前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
1月前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
83 2
|
1月前
|
运维 网络协议 算法
7 层 OSI 参考模型:详解网络通信的层次结构
7 层 OSI 参考模型:详解网络通信的层次结构
119 1
|
2月前
|
存储 安全 数据可视化
提升网络安全防御有效性,服务器DDoS防御软件解读
提升网络安全防御有效性,服务器DDoS防御软件解读
56 1
提升网络安全防御有效性,服务器DDoS防御软件解读
|
1月前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
1月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
602 2
下一篇
DataWorks