V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
taoing
V2EX  ›  Python

新手实现的 Python web 框架的代码在 win7 运行报错, Ubuntu 正常运行,求助大佬

  •  
  •   taoing · 2018-05-21 19:55:10 +08:00 · 2629 次点击
    这是一个创建于 2379 天前的主题,其中的信息可能已经有所发展或是发生改变。

    服务器代码:

    import socket
    from multiprocessing import Process
    import re
    
    class HTTPServer(object):
        def __init__(self, application):
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.app = application
        def bind(self, port):
            print('port:',port)
            self.server_socket.bind(('', port))
        def start(self):
            self.server_socket.listen(128)
            print('HTTP server is running...')
    
            while True:
                conn_socket, client_addr = self.server_socket.accept()
                print('connection from %s : %s' % client_addr)
                # 创建新进程处理客户端连接
                p = Process(target = self.handle_request, args = (conn_socket,))
                p.start()
                conn_socket.close()
        def start_response(self, status, headers):
            server_headers = [
            ('Server', 'MyWebServer 1.0')
            ]
            server_headers = headers + server_headers
            # 构造响应首部行
            response_headers = 'HTTP/1.1 ' + status + '\r\n'
            for header in server_headers:
                response_headers = response_headers + '%s:%s' % header + '\r\n'
            self.response_headers = response_headers
    
        def handle_request(self, conn_socket):
            '''处理客户端请求'''
            env = {}
            request_data = conn_socket.recv(2048).decode('utf-8')
            print('request from client:')
            print(request_data)
            '''解析请求返回响应'''
            # 请求行
            reuqestmethodline = request_data.split('\r\n')[0]
            # 请求方法
            requestmethod = reuqestmethodline.split(' ', 1)[0]
            # 请求资源
            requestpath = re.match(r'\w+\s+(/[a-zA-Z0-9\_\.]*)',reuqestmethodline).group(1)
            env['Method'] = requestmethod
            env['PATH_INFO'] = requestpath
            # 返回响应体
            response_body = self.app(env, self.start_response)
            response = self.response_headers + '\r\n' + response_body
            print('response from server:')
            print(response)
            conn_socket.send(response.encode('utf-8'))
            conn_socket.close()
    

    web 框架代码:

    import time
    from mywebserver import HTTPServer
    
    class Application(object):
        # 框架的核心部分
        def __init__(self, urls):
            self.urls = urls
    
        def __call__(self, env, start_response):
            path = env.get('PATH_INFO', '/')
            for url, handler in self.urls:
                if path == url:
                    response_body = handler(env, start_response)
                    return response_body
            # 请求路径不存在,返回 404 响应码
            status = '404 Not Found'
            headers = [('Content-Type', 'text/html')]
            start_response(status, headers)
            response_body = 'The file not found!'
            return response_body
    
    def ctime(env, start_response):
        status = '200 OK'
        headers = [('Content-Type', 'text/html')]
        start_response(status, headers)
        return time.ctime()
    
    def hello(env, start_response):
        status = '200 OK'
        headers = [('Content-Type', 'text/html')]
        start_response(status, headers)
        return 'Hello, World!'
    
    urls = [
    ('/', hello),
    ('/ctime', ctime),
    ('/hello', hello)
    ]
    app = Application(urls)
    http_server = HTTPServer(app)
    http_server.bind(8000)
    http_server.start()
    

    在 win7 python3.5.2 上运行报错:

    λ python mywebframe.py
    port: 8000
    HTTP server is running...
    connection from 127.0.0.1 : 50094
    connection from 127.0.0.1 : 50095
    port: 8000
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 106, in spawn_main
        exitcode = _main(fd)
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 115, in _main
        prepare(preparation_data)
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 226, in prepare
        _fixup_main_from_path(data['init_main_from_path'])
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 278, in _fixup_main_from_path
        run_name="__mp_main__")
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\runpy.py", line 254, in run_path
        pkg_name=pkg_name, script_name=fname)
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\runpy.py", line 96, in _run_module_code
        mod_name, mod_spec, pkg_name, script_name)
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\runpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "D:\Documents\python\code\2.2 wsgi_server\mywebframe.py", line 40, in <module>
    port: 8000
        http_server.bind(8000)
    Traceback (most recent call last):
      File "D:\Documents\python\code\2.2 wsgi_server\mywebserver.py", line 11, in bind
      File "<string>", line 1, in <module>
      File "C:\Users\taomian\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 106, in spawn_main
        self.server_socket.bind(('', port))
    OSError: [WinError 10048] 通常每个套接字地址(协议 /网络地址 /端口)只允许使用一次。
    

    在 ubuntu 上 python3.5.2 正常运行:

    allen@ubuntu:~/test/code$ python3 mywebframe.py 
    port: 8000
    HTTP server is running...
    connection from 127.0.0.1 : 45618
    request from client:
    GET / HTTP/1.1
    Host: 127.0.0.1:8000
    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-GB,en;q=0.5
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    
    
    response from server:
    HTTP/1.1 200 OK
    Content-Type:text/html
    Server:MyWebServer 1.0
    
    Hello, World!
    connection from 127.0.0.1 : 45620
    request from client:
    GET /favicon.ico HTTP/1.1
    Host: 127.0.0.1:8000
    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-GB,en;q=0.5
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    
    
    response from server:
    HTTP/1.1 404 Not Found
    Content-Type:text/html
    Server:MyWebServer 1.0
    
    The file not found!
    
    
    12 条回复    2018-05-21 23:45:15 +08:00
    taoing
        1
    taoing  
    OP
       2018-05-21 20:04:09 +08:00
    在 win7 上看报错提示,我理解是端口 8000 被绑定了两次导致出错,不知道为什么会出现这种情况,想请教大佬可以给我解答。
    ycfung
        2
    ycfung  
       2018-05-21 20:05:32 +08:00 via Android
    你这代码贴得太可怕了…
    wwqgtxx
        3
    wwqgtxx  
       2018-05-21 20:05:47 +08:00
    在 windows 下不要用“创建新进程处理客户端连接”这种方式,会出各种诡异的问题,应该使用“新线程”
    wwqgtxx
        4
    wwqgtxx  
       2018-05-21 20:07:54 +08:00
    另外这三行代码
    http_server = HTTPServer(app)
    http_server.bind(8000)
    http_server.start()
    应该包裹在
    if __name__ == '__main__':
    中,否则容易出现重复 start 你的 http_server 的情况
    taoing
        5
    taoing  
    OP
       2018-05-21 20:08:19 +08:00
    @wwqgtxx 好,我试试用线程看看
    DevNet
        6
    DevNet  
       2018-05-21 20:10:25 +08:00 via Android
    可能是前一次运行已经占用了这个端口,然后一直没释放? kill 试试,
    或者管理员权限问题?
    wwqgtxx
        7
    wwqgtxx  
       2018-05-21 20:10:38 +08:00
    根本原因只在于 windows 不支持 fork 模式,他的 process 是重新起一个新的 Python.exe 实现的,所以会再次 import 一次你的 mywebframe.py,而不过你没有把启动代码包裹在 if __name__ == '__main__': 中就会导致这一段代码再一次在子进程中被执行
    taoing
        8
    taoing  
    OP
       2018-05-21 20:13:12 +08:00
    @wwqgtxx 谢谢你的解答,原来系统间有差异,然后试了 if __name__ == '__main__':就没有报错了
    iConnect
        9
    iConnect  
       2018-05-21 20:21:55 +08:00 via Android
    如果你生产环境是 Ubuntu,就不用管 win 上报错了,我们经理都要求所有人环境统一,减少系统差异带来额外的 bug
    taoing
        10
    taoing  
    OP
       2018-05-21 20:31:14 +08:00
    @iConnect 我是个新手在学,只好在自己电脑上试
    so1n
        11
    so1n  
       2018-05-21 21:04:16 +08:00 via Android
    用 linux 吧,win 不能 fork 也没有 epoll 事件循环
    lidongyx
        12
    lidongyx  
       2018-05-21 23:45:15 +08:00 via iPhone
    学技术就不要在 windows 环境下折腾了,你折腾出来的这点经验都是浪费时间,因为生产实践中还是要用 linux 的,用虚拟机或者开发板、云服务器、双系统吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4373 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 05:32 · PVG 13:32 · LAX 21:32 · JFK 00:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.