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

HTTP协议?

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

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

默认HTTP的端口号为80,HTTPS的端口号为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()

原文地址:https://www.cnblogs.com/ParisGabriel/p/9491157.html

时间: 2024-10-16 02:29:19

Python网络编程(socketserver、TFTP云盘、HTTPServer服务器模型)的相关文章

python网络编程-----&gt; socketserver实现并发

1.socketserver的作用: 基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环 socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题) 2.代码实现: 服务端: import socketserver class MyTcphandler(socketserver.BaseRequestHandler): def handle(self): while True: #通信循环 data=self.request.recv(

Python 网络编程

Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发. 什么是 Socket? Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. socket()函数 Pyt

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python 网络编程(二)

Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端 服务端 1 2 3 4 5 6 7 8

python 网络编程:socket

在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一些概念和结构,还包括一系列的协议. TCP/IP四层模型:既然有OSI七层模型,为什么我们还要定义一个TCP/IP的四层模型呢,那是因为OSI七层模型对应面过于宽泛,很多概念实现不了,也没必要实现,因此,实际生产中广泛应用的是TCP/IP四层结构,他们的对应关系如下表: TCP/IP OSI 应用层

python 网络编程(Socket)

# from wsgiref.simple_server import make_server## def RunServer(environ,start_response):# start_response(status='200 OK',headers=[('Content-Type','text/html')])# url=environ['PATH_INFO']## return "guozhendong"## if __name__=='__main__':# httpd=m

python 网络编程 (二)---tcp

异常 python的socket模块实际上定义了4种可能出现的异常: 1)与一般I/O 和通信问题有关的socket.error; 2)与查询地址信息有关的socket.gaierror; 3)与其他地址错误有关的socket.herror; 4)与在一个socket上调用settimeout()后,处理超时有关的socket.timeout; import socket, sys, time host = sys.argv[1] textport = sys.argv[2] filename

[Python网络编程] DNS缓存解决方案

记得以前写爬虫的时候为了防止dns多次查询,是直接修改/etc/hosts文件的,最近看到一个优美的解决方案,修改后记录如下: import socket _dnscache={} def _setDNSCache(): """ Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests. """ def _getaddrinfo(*args, **

[Python网络编程]gevent httpclient以及网页编码

之前看到geventhttpclient这个项目,https://github.com/gwik/geventhttpclient,官方文档说非常快,由于响应使用了C的解析,所以我一直想把这玩意用到项目中, 这两天一直在纠结这玩意,说实在一句话,比较难用,封装的不给力,最大缺陷如下: 1.不支持重定向,重定向需要自己来写,很费事 2.新建的httpclient对象只能发送同域名的请求 这相当的蛋疼,我花了一点时间封装了一下,解决了上面的两个问题,还增加了自动编解码问题,代码如下: #!/usr/

python 网络编程(五)---DNS域名系统

1.域名系统定义 DNS计算机域名系统由域名服务器和域名解析器组成.通常输入的是网址就是一个域名. 2.域名查询 查询方式包括: 1)正向查询:由域名查找对应的IP(如:119.75.218.77">www.baidu.com->119.75.218.77 ) 2)反向查询:由IP查找域名(如:119.75.218.77 –> www.baidu.com) 查询方式包括: 1)递归查询:当DNS服务器接收到客户端的查询请求时,会做出相应的反应(本地DNS服务器查询.其他服务器查