美文网首页动态语言Ruby Python Python
Python 开发web服务器,socket非堵塞模式

Python 开发web服务器,socket非堵塞模式

作者: Devops海洋的渔夫 | 来源:发表于2018-12-28 20:19 被阅读85次

    仅供学习参考,转载请注明出处

    需求描述

    在开发web服务器接受http请求的时候,socket在recv等待接受数据的时候,服务端是堵塞的。
    用于等待http发送过来的数据。
    那么这个等待,其实也是会占用服务端的资源的。
    为了节省这个资源,可以采用非堵塞的方式来进行socket等待监听,就是每次轮询监听一下,并不会堵塞等待。

    修改为非堵塞方式

    首先修改socket为非堵塞模式,并且创建一个专门用来存放client_socket的list

       # 设置非堵塞模式
       server_socket.setblocking(False)
       # 创建用来存放子进程的list
       client_socket_list = list() # 因为是非堵塞的方式,所以需要存放接受到要处理的client_socket
    

    设置tcp的主socket等待接受http请求到来,因为没有堵塞,所以循环的时候必定会报错,所以需要进行异常抛出处理。

    运行效果如下:

    那么再来将client_socket的list循环处理

    运行如下:

    那么这个是什么意思呢?

    这是因为在循环非堵塞的过程中,client_socket执行之后就会关闭,但是由于是提前存入list组中,然后再次循环的时候,就会重复执行同一个client_socket,导致client_socket关闭错误(因为第一次已经关闭了,再次recv的时候当然报错)。

    那么只要处理一下这个关闭即可。

    还有另外一个问题,那就是在什么时候才能进行client_socket的关闭呢?

    来思考一下,是不是当client_socket在堵塞recv的时候,只要来了数据,就不会抛出异常,只要不抛出异常,那这时候肯定就是要关闭client_socket、同时将该client_socket剔除list中,避免重复循环。

    处理循环调用client_socket

    运行效果如下:

    那么到了这里已经完成了非堵塞的web服务了。但是这里面还有一个比较不好的性能问题,下次再讨论吧。

    可以提前思考一下,如果当这个存放client_socket的list越来越大,然后轮询的时候就会很久,那么就是导致访问一个页面很慢,这个问题该怎么处理呢?

    本次完整代码如下

    #coding=utf-8
    from socket import *
    import re
    import threading
    import time
    
    def handle_client(client_socket):
        """为一个客户端服务"""
        # 接收对方发送的数据
        recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
        # 打印从客户端发送过来的数据内容
        #print("client_recv:",recv_data)
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)
         
        # 返回浏览器数据
        # 设置内容body
        # 使用正则匹配出文件路径
        print("------>",request_header_lines[0])
        print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
        ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
        if ret:
           file_path = "./html/" + ret.group(1)
           if file_path == "./html/":
              file_path = "./html/index.html"
           print("file_path *******",file_path)
    
        try:
           # 设置返回的头信息 header
           response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
           response_headers += "\r\n" # 空一行与body隔开
           # 读取html文件内容
           file_name = file_path # 设置读取的文件路径
           f = open(file_name,"rb") # 以二进制读取文件内容
           response_body = f.read()
           f.close()   
           # 返回数据给浏览器
           client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
           client_socket.send(response_body)   #转码utf-8并send数据到浏览器
        except:
           # 如果没有找到文件,那么就打印404 not found
           # 设置返回的头信息 header
           response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
           response_headers += "\r\n" # 空一行与body隔开
           response_body = "<h1>sorry,file not found</h1>"
           response = response_headers + response_body
           client_socket.send(response.encode("utf-8"))
    
    #    client_socket.close()
    
    def main():
       # 创建套接字
       server_socket = socket(AF_INET, SOCK_STREAM)
       # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
       server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
       # 设置服务端提供服务的端口号
       server_socket.bind(('', 7788))
       # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
       server_socket.listen(128) #最多可以监听128个连接
    
       # 设置非堵塞模式
       server_socket.setblocking(False)
       # 创建用来存放子进程的list
       client_socket_list = list() # 因为是非堵塞的方式,所以需要存放接受到要处理的client_socket
    
       # 开启while循环处理访问过来的请求 
       while True:
          #time.sleep(0.5)    
          # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
          # client_socket用来为这个客户端服务
          # server_socket就可以省下来专门等待其他新的客户端连接while True:
          try:
             client_socket, clientAddr = server_socket.accept()
          except Exception as e:
             print("----1 暂无http请求到来------",e) # 因为是非堵塞,所以有可能会出现socket异常的情况
          else:
             print("----2 有http请求到来了!!------")
             client_socket.setblocking(False) # 设置client_socket为非堵塞模式
             client_socket_list.append(client_socket) # 将子进程加入list中
    
          # 循环处理client_socket_list
          for client_socket in client_socket_list:
              try:
                  handle_client(client_socket) 
              except Exception as e:
                  print("----3 client_socket并无收到http请求到来",e)
              else:
                  print("----4 client_socket收到http请求到来,并进行数据处理")
                  client_socket.close()
                  client_socket_list.remove(client_socket)
    
    if __name__ == "__main__":
       main()
    

    关注微信公众号,回复【资料】、Python、PHP、JAVA、web,则可获得Python、PHP、JAVA、前端等视频资料。

    相关文章

      网友评论

        本文标题:Python 开发web服务器,socket非堵塞模式

        本文链接:https://www.haomeiwen.com/subject/poujlqtx.html