如何用Socket和Wsgiref实现一个Web服务器

8561e90f0f824ddfb98789a75c86a48b.jpeg

 

 

目录

 

1、用Socket实现一个简单的web服务器 

2、用Wsgi实现一个简单的web服务器 

3、用Wsgi实现支持多url的web服务器 

三、用Wsgi实现支持多url和图片的web服务器 


 

1、用Socket实现一个简单的web服务器 

首先大概讲述一下,Socket创建服务器端和客户端的一个大致流程:

客户端调用 socket() 函数创建套接字后,因为没有建立连接,所以套接字处于CLOSED状态;服务器端调用 listen() 函数后,套接字进入LISTEN状态,开始监听客户端请求
这时客户端发起请求:
  1) 当客户端调用 connect() 函数后,TCP协议会组建一个数据包,并设置 SYN 标志位,表示该数据包是用来建立同步连接的。同时生成一个随机数字 1000,填充“序号(Seq)”字段,表示该数据包的序号。完成这些工作,开始向服务器端发送数据包,客户端就进入了SYN-SEND状态。
  2) 服务器端收到数据包,检测到已经设置了 SYN 标志位,就知道这是客户端发来的建立连接的“请求包”。服务器端也会组建一个数据包,并设置 SYN 和 ACK 标志位,SYN 表示该数据包用来建立连接,ACK 用来确认收到了刚才客户端发送的数据包
  服务器生成一个随机数 2000,填充“序号(Seq)”字段。2000 和客户端数据包没有关系。
  服务器将客户端数据包序号(1000)加1,得到1001,并用这个数字填充“确认号(Ack)”字段。
  服务器将数据包发出,进入SYN-RECV状态
  3) 客户端收到数据包,检测到已经设置了 SYN 和 ACK 标志位,就知道这是服务器发来的“确认包”。客户端会检测“确认号(Ack)”字段,看它的值是否为 1000+1,如果是就说明连接建立成功。
  接下来,客户端会继续组建数据包,并设置 ACK 标志位,表示客户端正确接收了服务器发来的“确认包”。同时,将刚才服务器发来的数据包序号(2000)加1,得到 2001,并用这个数字来填充“确认号(Ack)”字段。
  客户端将数据包发出,进入ESTABLISED状态,表示连接已经成功建立。
  4) 服务器端收到数据包,检测到已经设置了 ACK 标志位,就知道这是客户端发来的“确认包”。服务器会检测“确认号(Ack)”字段,看它的值是否为 2000+1,如果是就说明连接建立成功,服务器进入ESTABLISED状态。
  至此,客户端和服务器都进入了ESTABLISED状态,连接建立成功,接下来就可以收发数据了。

 

77fa77f06c6a41999ce1889d97ee4199.png

我们可以简单的利用该流程去实现应该简单的socket web服务器:

代码也很简洁:

import socket
def main():
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('localhost',8000))
    s.listen(5)

    while True:
        #等待浏览器连接服务器
        conn,conn_adr=s.accept()
        #接收浏览器发送的内容
        data=conn.recv(1024)
        print(data)

        #给浏览器返回一个内容,在发送内容之前,首先告诉我们的浏览器发生的是文本内容,超链接,还是图片
        conn.send(b"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n")

        conn.send('<h1>Hello!我是一个Socket Web服务器!</h1>'.encode('utf-8'))

        conn.close()

if __name__=='__main__':
    main()

 注意:不要用edge去打开8000端口,我试了很多次,用谷歌的话是可以正常使用的!不要把问题遗留在一些没有意义的事情上面!!!

 

714fbbba64784441aa6e874969c805ab.png

 

2、用Wsgi实现一个简单的web服务器 

    wSGI (Web Server Gateway Interface)是一种规范﹐它定义了使用python编写的web app(应用程序)与web server (socket服务端)之间接口格式﹐实现web app与web server间的解耦
    通俗的说︰当规范建立后﹐程序就不再重复编写web server (socket服务端)﹐
而是直接使用现成的实现WSGI的模块(例如︰wsgiref · uwsgi· werkzeug)﹐从而让程序员更加专注与业务代码
与其重复造轮子﹐不如直接用现成的·

这里的代码也是比较简单的:

"""
    wSGI (Web Server Gateway Interface)是一种规范﹐它定义了使用python编写的web app(应用程序)
与web server (socket服务端)之间接口格式﹐实现web app与web server间的解耦
    通俗的说︰当规范建立后﹐程序就不再重复编写web server (socket服务端)﹐
而是直接使用现成的实现WSGI的模块(例如︰wsgiref · uwsgi· werkzeug)﹐从而让程序员更加专注与业务代码
与其重复造轮子﹐不如直接用现成的·
"""
#首先导入模块接口,接入一个创建简单服务器的模块功能
from wsgiref.simple_server import make_server

def run_server(environ,start_response):
    #print('Hello',environ)
    #start_response 应该是一个函数用于给客户端传输数据使用
    start_response("200 OK",[('Content-Type','text/html;charset=utf-8')])#这里的是语法,是死的!!
    '''
    先写好状态码,在写要传输的数据类型(超文本,超链接,还是图片)
    '''
    return [bytes("<h2>Hello 运用wsgi去实现应该web服务器!!</h2>",encoding='utf-8')]

#在make_server功能中调用run_server接口,我们需要知道的是运用该模块构件一个服务器端,需要传入两个参数
#一个是environ 有关浏览器内容的一个参数
#一个是        给客户端返回数据
s=make_server('localhost',8000,run_server)
s.serve_forever()   #代表一个死循环,一直运行该服务器

代码运行结果如下:

       29a1b4bf897849ac9fe0bee5abab8e5f.png

 在上述的程序中,我们只是实现应该url,那么我们如何实现支持多url的web服务器?

3、用Wsgi实现支持多url的web服务器 

1、路由的分发器,负责把url 匹配到对应的函数中去
2、开发好对应的 业务函数
3、当一个请求来了之后,先走路由分发器,如果找到对应的function,就执行,如果没有找到,就返回404

代码也是比较简单的:


'''
1、路由的分发器,负责把url 匹配到对应的函数中去
2、开发好对应的 业务函数
3、当一个请求来了之后,先走路由分发器,如果找到对应的function,就执行,如果没有找到,就返回404
'''
from wsgiref.simple_server import make_server

def Pig(environ,start_response):
    print('你是头猪!')
    start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
    return [bytes("<h2>你是猪吗?小盆友! </h2>", encoding='utf-8')]

def Book(environ,start_response):
    print('你喜欢看书吗?')
    start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
    return [bytes("<h2>你喜欢看书吗?小盆友! </h2>", encoding='utf-8')]

#用户访问的url在environ中
#写一个路由分发器,用于获取路由地址
def url_dispach():
    urls={
        '/Pig':Pig,
        '/Book':Book
    }
    return urls


def run_server(environ,start_response):
    print('我又来了!',environ)
    #该语句是需要输出的内容!

    #首先我们要拿到所有的url
    url_list=url_dispach()
    #获取用户指定的url接口,是从众多environ中获取PATH_INFO变量下的url接口
    request_url=environ.get('PATH_INFO')
    print('request url',request_url)

    if request_url in url_list:
        #这样子写是因为方法在我们上面所设置的列表当中,通过列表调用键值对的形式来调用函数
        fun_data=url_list[request_url](environ,start_response)
        #上面这种形式因为return的是一种
        return fun_data
    else:
        start_response("404 OK", [('Content-Type', 'text/html;charset=utf-8')])
        return [bytes("<h1>404!! 找不到该网页!! </h1>", encoding='utf-8')]







    # start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])
    # return [bytes("<h2>Hello </h2>",encoding='utf-8')]
s=make_server('localhost',8000,run_server)
s.serve_forever()

运行结果图如下:
33d968fdaca94776b2c852b932bfc2d8.png

cf27adde955f40aeb0047469cb2a05b9.png 97bc56ab0a68430a93895064925e955d.png

三、用Wsgi实现支持多url和图片的web服务器 

难点主要在于,文件的读写操作!!和图片路径的获取!

主要代码也是比较简单的!

import re
import os
from wsgiref.simple_server import make_server
#获取文件的绝对路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

def Pig(envirson,response):
    print('hello pig')
    response("200 OK",[('Content-Type','text/html;charset=utf-8')])
    data="""
    <h1>欢迎来到小猪乐园!!</h1>
        <img src="/static/imgs/pig.gif" />
         
    """


    return [bytes(data, encoding='utf-8'),]

def Book(envirson,response):
    print('hello book')
    response('200 OK',[('Content-Type', 'text/html;charset=utf-8')])
    return [bytes("<h2>Hello 多读书呀小盆友!!</h2>", encoding='utf-8')]


def url_dispatch():
    urls={
        '/Pig':Pig,
        '/Book':Book
    }
    return urls

#找到文件并且打开文件
def img_handler(request_url):
    #获取文件路径
    img_path=re.sub('/static','/static_data', request_url)
    img_abs_path="%s%s" %(BASE_DIR,img_path)
    print('BASE',BASE_DIR)
    print(img_abs_path)

    #导入os模块,并且调用os模块打开和获取相应文件
    if os.path.isfile(img_abs_path):
        print('---有内容')
        f=open(img_abs_path,'rb')#以rb形式来打开文件
        data=f.read()#读取和展示文件
        #返回给浏览器
        return [data,0]#创建一个列表,如果文件存在,则返回0

    return [None,1]#如果不存在则返回1

def  run_server(environ,start_response):
    print('hahhahah',environ)


    url_list = url_dispatch() #拿到所有的url
    request_url = environ.get("PATH_INFO")
    print('request url',request_url)

    if request_url in url_list:
        func_data  = url_list[request_url](environ,start_response)

        return func_data #真正返回数据给用户

    elif request_url.startswith("/static/"):# 代表 是图片
        img_data,img_status = img_handler(request_url)
        if img_status == 0 : #图片有内容
            start_response("200 OK ", [('Content-Type', 'text/jpeg;charset=utf-8')])
            return [img_data,]
    else:
        start_response("404 ",[('Content-Type','text/html;charset=utf-8')])

        return [bytes('<h1 style="font-size:50px">404, Page not found!</h1>',encoding="utf-8"),]

    #书写返回浏览器的文本格式(文本,超链接,图片)
    # response('200 OK',[('Content-Type','text/html;charset=utf-8')])
    # return [bytes("<h2>Hello 运用wsgi去实现应该web服务器!!</h2>",encoding='utf-8')]

s=make_server('localhost',8000,run_server)
s.serve_forever()

展示图如下:

166dad803004471eba93f5cf0cde9642.png

 

 

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的学习日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值