当前位置: 首页 > news >正文

响应式 购物网站模板下载建设公司网站内容

响应式 购物网站模板下载,建设公司网站内容,软件开发过程包括,微信公众号怎么制作网页Part1: What is a Web server? 一个位于物理服务器上的网络服务器#xff08;服务器里的服务器#xff09;#xff0c;等待客户端去发送request#xff0c;当服务器接收到request#xff0c;就会生成一个response发送回客户端#xff1b; 客户端与服务器使用HTTP协议进… Part1:   What is a Web server?   一个位于物理服务器上的网络服务器服务器里的服务器等待客户端去发送request当服务器接收到request就会生成一个response发送回客户端   客户端与服务器使用HTTP协议进行通信客户端可以是浏览器或者其他使用HTTP协议的软件。   一个简单的WEB服务器实现 import socketHOST,PORT ,8899listen_socket socket.socket(socket.AF_INET,socket.SOCK_STREAM) listen_socket.setblocking(1) listen_socket.bind((HOST,PORT)) listen_socket.listen(1)print(Serving HTTP on port %s ... % PORT)while True:client_connection,client_address listen_socket.accept()request client_connection.recv(1024)print(request)http_response HTTP/1.1 200 OKHello, World!client_connection.sendall(bytes(http_response,encodingutf-8))client_connection.close() 保存为webserver1.py 并且 命令行运行 $ python webserver1.py Serving HTTP on port 8899 … 浏览器输入 http://localhost:8899/hello 刚才输入的WEB地址它叫URL这是它的基本结构      它表示了浏览器要查找和连接的WEB服务器地址和你要获取的服务器上的页面路径。   在浏览器发送HTTP request之前他需要先与服务端建立TCP连接然后浏览器在TCP连接上发送HTTP request然后等待服务器回发HTTP response。当浏览器接收到响应后显示响应在本次例子中浏览器显示“Hello, World!”。   在建立连接时使用到了socket我们可以用命令行下的telnet模拟浏览器进行测试   在运行WEB服务器的同一台电脑上命令行启动一个telnet session指定连接到localhost主机连接端口为8899然后按回车 $ telnet localhost 8899 Trying 127.0.0.1 … Connected to localhost.   此时你已经和运行在你本地主机的服务器建立了TCP连接已经准备好发送并接收HTTP消息了。   下图中你可以看到一个服务器要经过的标准步骤然后才能接受新的TCP连接。    $ telnet localhost 8899 Trying 127.0.0.1 … Connected to localhost. GET /hello HTTP/1.1HTTP/1.1 200 OK Hello, World!   通过这个流程模拟了浏览器发送http request获得http response HTTP Request      HTTP请求由行组成。标明了HTTP方法GET我们要服务器返回给我们东西我们想要的服务器上的“页面”路径/hello 和 协议版本   为了简单起见此时我们的WEB服务器完全忽略了上面的请求行。你也可以输入任何字符取代“GET /hello HTTP/1.1”你仍然会得到“Hello, World!”响应。   一旦你输入了请求行敲了回车客户端就发送请求给服务器服务器读取请求行打印出来然后返回相应的HTTP响应。 HTTP Response   以下是服务器回发给客户端这个例子中是telnet的HTTP响应      Response 包含了状态行 HTTP/1.1 200 OK , 紧接着是一个必须的空白行然后是HTTP response 内容   状态行 HTTP/1.1 200 OK 包含了HTTP版本HTTP状态码200HTTP状态码短语OK当浏览器获得获得response就显示response里body的内容。 总的来说   Web Server 创建一个 listening socket 和 在循环里 accepting 新连接客户端初始化一个TCP连接建立成功后客户端发送HTTP request 给服务端然后服务端响应 HTTP reponse客户端和服务端都使用socket建立TCP连接。   现在你有了一个非常基础的WEB服务器你可以用浏览器或其他的HTTP客户端测试它。 Question   How do you run a Django application, Flask application, and Pyramid application under your freshly minted Web server without making a single change to the server to accommodate all those different Web frameworks ?   怎样在你刚完成的WEB服务器下运行 Django 应用、Flask 应用和 Pyramid  应用在不单独修改服务器来适应这些不同的 WEB 框架的情况下   Part2:   过去你所选择的一个Python Web框架会限制你选择可用的Web服务器反之亦然。如果框架和服务器设计的是可以一起工作的那就很好      但是当你试着结合没有设计成可以一起工作的服务器和框架时你可能要面对可能你已经面对了下面这种问题:      基本上你只能用可以在一起工作的部分而不是你想用的部分。   那么怎样确保在不修改Web服务器和Web框架下用你的Web服务器运行不同的Web框架   答案就是Python Web Server Gateway Interface或者缩写为WSGI读作“wizgy”。      WSGI允许开发者把框架的选择和服务器的选择分开。现在你可以真正地混合、匹配Web服务器和Web框架了。   你可以运行 Django, Flask, or Pyramid, 在 Gunicorn or Nginx/uWSGI or Waitress. 上。      你的Web服务器必须是实现WSGI接口的服务器所有的现代Python Web框架已经实现了WSGI接口的框架端了这就让你可以不用修改服务器代码适应某个框架。   现在你了解了Web服务器和WEb框架支持的WSGI允许你选择一对合适的服务器和框架其他语言也有相似的接口例如Java有Servlet APIRuby有Rack。 简单的WSGI Server 代码 #!/usr/bin/env python # -*-coding:utf-8 -*-import socket from io import StringIO import sysclass WSGIServer(object):address_family socket.AF_INETsocket_type socket.SOCK_STREAMrequest_queue_siez 1def __init__(self,server_address):self.listen_socket listen_socket socket.socket(self.address_family,self.socket_type)listen_socket.setblocking(1)listen_socket.bind(server_address)listen_socket.listen(self.request_queue_siez)# get server host name and porthost,port self.listen_socket.getsockname()[:2]self.server_name socket.getfqdn(host)self.server_port port# return headersself.headers_set []def set_app(self,application):self.application applicationdef server_forever(self):listen_socket self.listen_socketwhile True:self.client_connection,client_address listen_socket.accept()self.handle_one_request()def handle_one_request(self):self.request_data request_data str(self.client_connection.recv(1024),encodingutf-8)# request lineself.parse_request(request_data)# get environenv self.get_environ()# Its time to call our application callable and get# back a result that will become HTTP response bodyresult self.application(env,self.start_response)self.finish_response(result)def parse_request(self,text):request_line text.splitlines()[0](self.request_method,self.path,self.request_version,) request_line.split()def get_environ(self):env {}# Required WSGI variablesenv[wsgi.version] (1, 0)env[wsgi.url_scheme] httpenv[wsgi.input] StringIO(self.request_data)env[wsgi.errors] sys.stderrenv[wsgi.multithread] Falseenv[wsgi.multiprocess] Falseenv[wsgi.run_once] False# Required CGI variablesenv[REQUEST_METHOD] self.request_method # GETenv[PATH_INFO] self.path # /helloenv[SERVER_NAME] self.server_name # localhostenv[SERVER_PORT] str(self.server_port) # 8888return envdef start_response(self,status,respnse_headers,exc_infoNone):# Add necessary server headersserver_headers [(Date, Tue, 31 Mar 2017 12:54:48 GMT),(Server, WSGIServer 0.2),]self.headers_set [status , respnse_headers server_headers]def finish_response(self,result):result str(result[0], encodingutf8)try:status,response_headers self.headers_setresponse HTTP/1.1 {status}\r\n.format(statusstatus)for header in response_headers:response {0}:{1}\r\n.format(*header)response \r\nfor date in result:response dateprint(.join( {line}\n.format(lineline)for line in response.splitlines()))self.client_connection.sendall(bytes(response,encodingutf-8))finally:self.client_connection.close()SERVER_ADDRESS (HOST, PORT) , 8899def make_server(server_address,application):server WSGIServer(server_address)server.set_app(application)return serverif __name__ __main__:if len(sys.argv) 2:sys.exit(Provide a WSGI application object as module:callable)app_path sys.argv[1]module, application app_path.split(:)module __import__(module)application getattr(module, application)httpd make_server(SERVER_ADDRESS, application)print(WSGIServer: Serving HTTP on port {port} ...\n.format(portPORT))httpd.server_forever()     它可以运行你喜欢的Web框架写的基本的Web应用可以是PyramidFlaskDjango或者其他的Python WSGI框架。   安装pyramid、flask、django $ [sudo] pip install virtualenv $ mkdir ~/envs $ virtualenv ~/envs/lsbaws/ $ cd ~/envs/lsbaws/ $ ls bin include lib $ source bin/activate (lsbaws) $ pip install pyramid (lsbaws) $ pip install flask (lsbaws) $ pip install django pyramid   创建一个pyramid的工程保存为pyramidapp.py from pyramid.config import Configurator from pyramid.response import Responsedef hello_world(request):return Response(Hello world from Pyramid!\n,content_typetext/plain,)config Configurator() config.add_route(hello, /hello) config.add_view(hello_world, route_namehello) app config.make_wsgi_app()   命令行输入 (lsbaws) $ python webserver2.py pyramidapp:app WSGIServer: Serving HTTP on port 8888 ... Flask from flask import Flask from flask import Response flask_app Flask(flaskapp)flask_app.route(/hello) def hello_world():return Response(Hello world from Flask!\n,mimetypetext/plain)app flask_app.wsgi_app Django import sys sys.path.insert(0, ./helloworld) from helloworld import wsgiapp wsgi.application      WSGI可以让你把Web服务器和Web框架结合起来。   WSGI提供了Python Web服务器和Python Web框架之间的一个最小接口在服务器和框架端都可以轻易实现。   下面的代码片段展示了WSGI接口的服务器和框架端 def run_application(application):Server code.# This is where an application/framework stores# an HTTP status and HTTP response headers for the server# to transmit to the clientheaders_set []# Environment dictionary with WSGI/CGI variablesenviron {}def start_response(status, response_headers, exc_infoNone):headers_set[:] [status, response_headers]# Server invokes the ‘application callable and gets back the# response bodyresult application(environ, start_response)# Server builds an HTTP response and transmits it to the client…def app(environ, start_response):A barebones WSGI app.start_response(200 OK, [(Content-Type, text/plain)])return [Hello world!]run_application(app) 工作流程 Framework 提供一个 可调用对象 application callable  服务器每次接收到HTTP Client request后服务器把一个包含了WSGI/CGI变量的字典  和 一个 start_response’ callable 做为参数 传递给 ’application’ callable Framework/Application 生成HTTP状态 和 HTTP响应头然后把它们传给 start_response’ callable让服务器保存它们。最后 Framework/Application 返回一个 response body 服务器把状态响应头响应体合并到HTTP响应里然后传给 HTTP客户端这步不是WSGI规格里的一部分 自定义Application   此时我们不使用Framework自己编写一个简单的app def app(environ, start_response):A barebones WSGI application.This is a starting point for your own Web framework :)status 200 OKresponse_headers [(Content-Type, text/plain)]start_response(status, response_headers)return [Hello world from a simple WSGI application!\n]   保存以上代码到wsgiapp.py文件 (lsbaws) $ python webserver2.py wsgiapp:app WSGIServer: Serving HTTP on port 8899 ...   使用HTTP客户端调用Pyramid应用时生成的HTTP响应    Content-Type, Content-Length, Date, 和Servedr。这些headers是Web服务器组合而成的。虽然他们并不是必须的。headers目的是传输HTTP请求/响应的额外信息。   ’environ’字典必须包含WSGI规范规定的必要的WSGI和CGI变量。   服务器在解析请求后从HTTP请求拿到了字典的值字典的内容看起来像下面这样       Web框架使用字典里的信息来决定使用哪个视图基于指定的路由请求方法等从哪里读请求体错误写到哪里去如果有的话。 总结 简要重述下WSGI Web服务器必须做哪些工作才能处理发给WSGI应用的请求吧 首先服务器启动并加载一个由Web框架/应用提供的可调用的’application’ 然后服务器读取请求 然后服务器解析它 然后服务器使用请求的数据创建了一个’environ’字典 然后服务器使用’environ’字典和’start_response’做为参数调用’application’并拿到返回的响应体。 然后服务器使用调用’application’返回的数据由’start_response’设置的状态和响应头来构造HTTP响应。 最终服务器把HTTP响应传回给户端。       现在你有了一个可工作的WSGI服务器它可以处理兼容WSGI的Web框架如DjangoFlaskPyramid或者你自己的WSGI框架。   最优秀的地方是服务器可以在不修改代码的情况下使用不同的Web框架。 Question   How do you make your server handle more than one request at a time?   该怎么做才能让服务器同一时间处理多个请求呢    Part3:   服务器同一时间只处理一个客户端请求在每次发送给客户端响应后添加一个60秒的延迟进行测试    #!/usr/bin/env python # -*-coding:utf-8 -*-import socket import timeSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 5def handle_request(client_connection):request client_connection.recv(1024)print(request.decode())http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)time.sleep(60) # sleep and block the process for 60 secondsdef serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT))while True:client_connection, client_address listen_socket.accept()handle_request(client_connection)client_connection.close()if __name__ __main__: 使用curl 命令来进行测试屏幕上输出 hello World $ curl http://localhost:8888/hello Hello, World! 再打开另外一个terminal输入同样的内容发现不会立刻产生任何输出而是挂起。而且服务器也不会打印出新请求。 当你等待足够长时间大于60秒后你会看到第一个curl终止了第二个curl在屏幕上打印出“Hello, World!”然后挂起60秒然后再终止 服务器完成处理第一个curl客户端请求然后睡眠60秒后开始处理第二个请求。   两个程序间的网络通信通常是使用 Socket(插座) 来完成的它允许你的程序使用 file descriptor(文件描述符) 和别的程序通信。    本文将详细谈谈在Linux上的TCP/IP socket。理解socket的一个重要的概念是TCP socket pairs  socket pairs 是由 4-tuple (4元组) 构成分别是本地ip本地端口目标ip目标端口。 一个socket pairs 唯一标识着网络上的TCP连接 标识着每个 endpoint 终端的两个值IP地址和端口号通常被称为socket。 tuple{10.10.10.2:49152, 12.12.12.3:8888}是客户端TCP连接的唯一标识着两个终端的socket pairs tuple{12.12.12.3:8888, 10.10.10.2:49152}是服务器TCP连接的唯一标识着两个终端的socket pairs 服务器创建一个socket并开始接受客户端连接的标准流程经历通常如下 1、服务器创建一个TCP/IP Socket listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) 2、设置Socket options listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 3、服务器绑定地址 listen_socket.bind(SERVER_ADDRESS) 4、监听Socket listen_socket.listen(REQUEST_QUEUE_SIZE) listen方法只会被服务器调用。它告诉Kernel内核它要接收这个socket上到来的连接请求 服务器开始循环地接收客户端连接。 当有连接到达时accept call 返回Client Socket服务器从Client Socket 读取request data在 standard output标准输出中打印内容发送信息给Client然后服务器关闭客户端连接准备好再次接受新的客户端连接。 下面是客户端使用TCP/IP和服务器通信要做的    客户端代码 import socket# create a socket and connect to a serversock socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((localhost, 8888))# send and receive some datasock.sendall(btest)data sock.recv(1024)print(data.decode()) 客户端仅需提供一个远程ip地址或者host name 和远程端口 客户端没必要调用bind是因为客户端不关心本地IP地址和本地端口号。 当客户端调用connect时kernel 的TCP/IP栈自动分配一个本地IP址地和本地端口。 本地端口被称为暂时端口 ephemeral port也就是short-lived 端口。  服务器上标识着一个客户端连接的众所周知的服务的端口被称为well-known端口举例来说80就是HTTP22就是SSH import socketsock socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((localhost, 8888))host, port sock.getsockname()host, port (127.0.0.1, 60589) 上面这个例子中内核分配了60589这个暂时端口。   What is a process?   进程就是一个正在运行的程序的实例。   当服务器代码执行时它被加载进内存运行起来的程序实例被称为进程。   内核Kernel记录了进程的一堆信息用于跟踪进程ID就是一个例子。 在控制台窗口运行webserver3b.py $ python webserver3b.py 在别的控制台窗口使用ps命令获取这个进程的信息 $ ps | grep webserver3b | grep -v grep 7182 ttys003 0:00.04 python webserver3b.py ps命令表示你确实运行了一个Python进程webserver3b。进程创建时内核分配给它一个进程ID也就是 PID。 在UNIX里每个用户进程都有个父进程父进程也有它自己的进程ID叫做父进程ID或者简称PPID。 假设你是在BASH shell里运行的服务器那新进程的父进程ID就是BASH shell的进程ID。  子Python shell进程和父BASH shell进程的关系   what is a file descriptor?   fire descriptor文件描述符是当你打开文件、创建文件、创建Socket时内核返回的一个非负整数   你可能已经听过啦在UNIX里一切皆文件。   内核使用文件描述符来追踪进程打开的文件当需要读或写文件时可以用文件描述符标识它   Python给你包装成更高级别的对象来处理文件和socket你不必直接使用文件描述符来标识一个文件   但是在底层UNIX中是这样标识文件和socket的通过它们的整数文件描述符。      默认情况下UNIX shell分配文件描述符0给进程的标准输入文件描述符1给进程的标准输出文件描述符2给标准错误。      可以使用对象的 fileno() 方法来获取对应的文件描述符。 import syssys.stdin open file stdin, mode r at 0x102beb0c0sys.stdin.fileno() 0sys.stdout.fileno() 1sys.stderr.fileno() 2   使用write system call 去输出一个字符串使用文件描述符作为参数。 import sysimport osres os.write(sys.stdout.fileno(), hello\n) hello   Socket使用文件描述符 import socketsock socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.fileno() 3   当服务器进程在60秒的睡眠时你仍然可以用curl命令来连接但是curl没有立刻输出内容它只是在那挂起。   因为设置了 socket对象的listen方法和它的BACKLOG参数 REQUEST_QUEUE_SIZE(请求队列长度)。   BACKLOG参数决定了内核为进入的连接请求准备的队列长度。   当服务器睡眠时第二个curl命令可以连接到服务器因为内核在服务器socket的进入连接请求队列上有足够的可用空间。   然而增加BACKLOG参数不会让服务器同时处理多个客户端请求需要设置一个合理的backlog参数这样accept调用就不用再等新连接到来立刻就能从队列里获取新的连接然后开始处理客户端请求。    How do you write a concurrent server?      在Unix上写一个并发服务器最简单的方法是使用fork()系统调用   它能同时处理多个客户端请求    import os import socket import timeSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 5def handle_request(client_connection):request client_connection.recv(1024)print(Child PID: {pid}. Parent PID {ppid}.format(pidos.getpid(),ppidos.getppid(),))print(request.decode())http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)time.sleep(60)def serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT))print(Parent PID (PPID): {pid}\n.format(pidos.getpid()))while True:client_connection, client_address listen_socket.accept()pid os.fork()if pid 0: # childlisten_socket.close() # close child copyhandle_request(client_connection)client_connection.close()os._exit(0) # child exits hereelse: # parentclient_connection.close() # close parent copy and loop overif __name__ __main__:serve_forever()   虽然服务器子进程在处理客户端请求时睡眠60秒但不影响别的客户端因为它们是被不同的完全独立的进程处理的。   你可以看到curl命令立刻就输出了“Hello, World!”然后挂起60秒。   理解 fork() 最重要的一点是你 fork 了一次但它返回了两次一个是在父进程里一个是在子进程里。   当你 fork 了一个新进程子进程返回的进程ID是0。父进程里fork返回的是子进程的PID      当父进程fork了一个新的子进程子进程就获取了父进程文件描述符的拷贝      你可能已经注意到啦上面代码里的父进程关闭了客户端连接 else: # parentclient_connection.close() # close parent copy and loop over   如果它的父进程关闭了同一个socket子进程为什么还能从客户端socket读取数据呢   因为内核使用描述符引用计数来决定是否关闭socket只有当描述符引用计数为0时才关闭socket。   当服务器创建一个子进程时子进程获取了父进程的文件描述符拷贝内核增加了这些描述符的引用计数。   在一个父进程和一个子进程的场景中客户端socket的描述符引用计数就成了2   当父进程关闭了客户端连接socket它仅仅把引用计数减为1不会引发内核关闭这个socket。   子进程也把父进程的 listen_socket 拷贝给关闭了因为子进程不用接受新连接它只关心处理已经连接的客户端的请求 listen_socket.close() # close child copy    what happens if you do not close duplicate descriptors   现在服务器父进程唯一的角色就是接受一个新的客户端连接fork一个新的子进程来处理客户端请求然后重复接受另一个客户端连接   What does it mean when we say that two events are concurrent?       当我们说两个事件并发时我们通常表达的是它们同时发生。   定义为如果你不能通过观察程序来知道哪个先发生的那么这两个事件就是并发的。       Two events are concurrent if you cannot tell by looking at the program which will happen first.   服务器不关闭复制的描述符例子 import os import socketSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 5def handle_request(client_connection):request client_connection.recv(1024)http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)def serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT))clients []while True:client_connection, client_address listen_socket.accept()# store the reference otherwise its garbage collected# on the next loop runclients.append(client_connection)pid os.fork()if pid 0: # childlisten_socket.close() # close child copyhandle_request(client_connection)client_connection.close()os._exit(0) # child exits hereelse: # parent# client_connection.close()print(len(clients))if __name__ __main__:serve_forever()   curl 打印出来内容后它并不终止而是一直挂起。   它的子进程处理了客户端请求关闭了客户端连接然后退出但是客户端curl仍然不终止。   当子进程关闭了客户端连接内核减少引用计数值变成了1。   服务器子进程退出但是客户端socket没有被内核关闭掉因为引用计数不是0   所以结果就是终止数据包在TCP/IP说法中叫做FIN没有发送给客户端所以客户端就保持在线啦。   这里还有个问题如果服务器不关闭复制的文件描述符然后长时间运行最终会耗尽可用文件描述符。      使用shell内建的命令ulimit检查一下shell默认设置的进程可用资源 $ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 3842 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 3842 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited   Ubuntu上进程的最大可打开文件描述符是1024   在已存在或新的控制台窗口限制最大可以使用256个文件描述符 $ ulimit -n 256   在同一个控制台上启动webserver3d.py   使用下面的Client代码进行测试 import argparse import errno import os import socketSERVER_ADDRESS localhost, 8888 REQUEST b\ GET /hello HTTP/1.1 Host: localhost:8888def main(max_clients, max_conns):socks []for client_num in range(max_clients):pid os.fork()if pid 0:for connection_num in range(max_conns):sock socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect(SERVER_ADDRESS)sock.sendall(REQUEST)socks.append(sock)print(connection_num)os._exit(0)if __name__ __main__:parser argparse.ArgumentParser(descriptionTest client for LSBAWS.,formatter_classargparse.ArgumentDefaultsHelpFormatter,)parser.add_argument(--max-conns,typeint,default1024,helpMaximum number of connections per client.)parser.add_argument(--max-clients,typeint,default1,helpMaximum number of clients.)args parser.parse_args()main(args.max_clients, args.max_conns)   在新的控制台窗口里启动client.py让它创建300个连接同时连接服务器。 $ python client3.py --max-clients300   很快服务器就崩了。      服务器应该关闭复制的描述符。但即使关闭了复制的描述符你还没有接触到底层因为你的服务器还有个问题zombies僵尸      再次运行服务器在另一个控制台窗口运行curl命令   运行ps命令显示运行着的Python进程。 $ ps auxw | grep -i python | grep -v grep vagrant 9099 0.0 1.2 31804 6256 pts/0 S 16:33 0:00 python webserver3d.py vagrant 9102 0.0 0.0 0 0 pts/0 Z 16:33 0:00 [python] defunct   PId为9102的进程的状态是Z进程的名称是 defunct这个就是僵尸进程。它的问题在于你杀死不了他们。   使用 kill -9 也杀死不了他们      Zombies是它的父进程没有等它还没有接收到它的终止状态。   当一个子进程比父进程先终止内核把子进程转成僵尸存储进程的一些信息等着它的父进程以后获取。   存储的信息通常是进程ID进程终止状态进程使用的资源。   如果服务器不好好处理这些僵尸系统就会越来越堵塞。   首先停止服务器然后新开一个控制台窗口使用ulimit命令设置最大用户进程为400确保设置打开文件更高如500 $ ulimit -u 400 $ ulimit -n 500   启动Server $ python webserver3d.py   新开一个控制台启动Client python client3.py --max-clients500   服务器又一次崩了是OSError的错误抛出资源临时不可用的异常当试图创建新的子进程时但创建不了时因为达到了最大子进程数限制。      如果不处理好僵尸服务器长时间运行就会出问题。    what do you need to do to take care of zombies ?   需要获取它们的终止状态。可以通过调用 wait 来解决。   不幸的是如果调用wait就会阻塞服务器实际上就是阻止了服务器处理新的客户端连接请求。   我们可以使用signal handler 和 wait system call 相组合的方法    当子进程结束时内核发送一个SIGCHLD 信号父进程可以设置一个Signal handler 来异步的被通知然后就能wait子进程获取它的终止状态因此阻止了僵尸进程出现。   asynchronous event 异步事件意味着父进程不会提前知道事件发生的时间。   SIGCHLD 信号   子进程结束时, 父进程会收到这个信号。    signal(参数一,参数二) 参数一我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号时系统定义的宏。 参数二我们处理的方式是系统默认还是忽略还是捕获。可以写一个handdle函数来处理我们捕获的信号。   那么 SIGCHILD 和 wait 到底是一个什么关系呢?   其实这两者之间没有必然的关系。   主进程可以直接调用waitpid or wait来回收子进程的结束状态不一定非得通过SIGCHILD信号处理函数也就是说waitpid or wait不是依靠SIGCHLD信号是否到达来判断子进程是否结束。但是如果主进程除了回收子进程状态以外还有其他的业务需要处理那么最好是通过SIGCHILD信号处理函数来调用waitpid or wait,因为这是异步的操作。   服务器端修改后代码为 import os import signal import socket import timeSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 5def grim_reaper(signum, frame):pid, status os.wait()print(Child {pid} terminated with status {status}\n.format(pidpid, statusstatus))def handle_request(client_connection):request client_connection.recv(1024)print(request.decode())http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)# sleep to allow the parent to loop over to accept and block theretime.sleep(3)def serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT)) # 绑定信号处理函数将SIGCHLD绑定在函数grim_reaper上面signal.signal(signal.SIGCHLD, grim_reaper)while True:client_connection, client_address listen_socket.accept()pid os.fork()if pid 0: # childlisten_socket.close() # close child copyhandle_request(client_connection)client_connection.close()os._exit(0)else: # parentclient_connection.close()if __name__ __main__:serve_forever()   观察服务器      The call to accept failed with the error EINTR.   当子进程退出引发SIGCHLD事件时激活了事件处理器此时父进程阻塞在accept调用然后当事件处理器完成时accept系统调用就中断了      我们需要重新调用accept() import errno import os import signal import socketSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 1024def grim_reaper(signum, frame):pid, status os.wait()def handle_request(client_connection):request client_connection.recv(1024)print(request.decode())http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)def serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT))signal.signal(signal.SIGCHLD, grim_reaper)while True:try:client_connection, client_address listen_socket.accept()except IOError as e:code, msg e.args# restart accept if it was interruptedif code errno.EINTR:continueelse:raisepid os.fork()if pid 0: # childlisten_socket.close() # close child copyhandle_request(client_connection)client_connection.close()os._exit(0)else: # parentclient_connection.close() # close parent copy and loop overif __name__ __main__:serve_forever()   现在我们使用Client直接创建128个并发的连接进行测试 python client.py --max-clients 128   看到了吧少年僵尸又回来了    当你运行128个并发客户端时建立了128个连接子进程处理了请求然后几乎同时终止了这就引发了SIGCHLD信号洪水般的发给父进程。问题在于UNIX信号往往是不会排队的父进程错过了一些信号导致了一些僵尸到处跑没人管       解决方案就是设置一个SIGCHLD事件处理器但不用wait了改用waitpid system call带上WNOHANG参数循环处理确保所有的终止的子进程都被处理掉。   pid_t waitpid(pid_t pid,int *status,int options)   从本质上讲系统调用waitpid和wait的作用是完全相同的但waitpid多出了两个可由用户控制的参数pid和options从而为我们编程提供了另一种更灵活的方式。   参数status:用来保存被收集进程退出时的一些状态它是一个指向int类型的指针。   参数pid需要的是一个进程ID。但当pid取不同的值时在这里有不同的意义。          pid0时只等待进程ID等于pid的子进程不管其它已经有多少子进程运行结束退出了只要指定的子进程还没有结束,waitpid就会一直等下去。     pid-1时等待任何一个子进程退出没有任何限制此时waitpid和wait的作用一模一样。        pid0时等待同一个进程组中的任何子进程如果子进程已经加入了别的进程组waitpid不会对它做任何理睬。     pid-1时等待一个指定进程组中的任何子进程这个进程组的ID等于pid的绝对值。      参数option提供了一些额外的选项来控制waitpid目前在Linux中只支持 WNOHANG 和 WUNTRACED 两个选项这是两个常数可以用|运算符把它们连接起来使用   如果使用了WNOHANG参数调用waitpid即使没有子进程退出它也会立即返回不会像wait那样永远等下去。   返回值     当正常返回的时候waitpid返回收集到的子进程的进程ID     如果设置了选项WNOHANG而调用中waitpid发现没有已退出的子进程可收集则返回0     如果调用中出错则返回-1这时errno会被设置成相应的值以指示错误所在   以下是修改后的webserver3g.py import errno import os import signal import socketSERVER_ADDRESS (HOST, PORT) , 8888 REQUEST_QUEUE_SIZE 1024def grim_reaper(signum, frame):while True:try:pid, status os.waitpid(-1, # Wait for any child processos.WNOHANG # Do not block and return EWOULDBLOCK error)except OSError:returnif pid 0: # no more zombiesreturndef handle_request(client_connection):request client_connection.recv(1024)print(request.decode())http_response b\ HTTP/1.1 200 OKHello, World! client_connection.sendall(http_response)def serve_forever():listen_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)listen_socket.bind(SERVER_ADDRESS)listen_socket.listen(REQUEST_QUEUE_SIZE)print(Serving HTTP on port {port} ....format(portPORT))signal.signal(signal.SIGCHLD, grim_reaper)while True:try:client_connection, client_address listen_socket.accept()except IOError as e:code, msg e.args# restart accept if it was interruptedif code errno.EINTR:continueelse:raisepid os.fork()if pid 0: # childlisten_socket.close() # close child copyhandle_request(client_connection)client_connection.close()os._exit(0)else: # parentclient_connection.close() # close parent copy and loop overif __name__ __main__:serve_forever()   there are no more zombies. Yay! Life is good without zombies :)   现在你已经拥有了自己的简单并发服务器而且这个代码有助于你在将来的工作中开发一个产品级的Web服务器。   修改第二部分的代码达到并发的效果?详情代码   What’s next? As Josh Billings said, “Be like a postage stamp — stick to one thing until you get there.” Start mastering the basics. Question what you already know. And always dig deeper. “If you learn only methods, you’ll be tied to your methods. But if you learn principles, you can devise your own methods.” —Ralph Waldo Emerson”   https://ruslanspivak.com 转载于:https://www.cnblogs.com/5poi/p/7531970.html
http://www.sadfv.cn/news/278426/

相关文章:

  • 汕头建设网站关于做ppt的网站有哪些
  • 两学一做知识问答网站用户图片上传wordpress
  • 兰州新区小程序建站长沙发布致全体
  • 怎样给公司做一个网站电脑网页制作模板
  • 做装修公司网站视频制作公司排行
  • 返利网站怎么做上海设计公司 快消品
  • 网站建设对标行业分析开山云匠网
  • 住房和城乡建设部办公厅网站做外贸必看的网站和论坛有哪些
  • 做网站怎么跑业务温州本地网站
  • 温州制作网站wordpress淘宝客pid
  • 事业单位网站开发工作规程昆明网站seo
  • 网页设计与网站开发教程全网营销平台
  • 西安优秀的定制网站建设公司哪家好外网设计灵感网站
  • 南阳网站建设seo广州骏域网站
  • 为什么做网站要用谷歌浏览器购物分享网站怎么做盈利
  • 杭州网站建设网络公司网站设计培训学校有哪家
  • 网站建设 就业方向步骤怎么读
  • 微信清粉网站开发wordpress不能上传主题
  • 广州市城乡和建设局网站首页网站建设及宣传管理规定
  • 网站软件有哪些深圳外贸网站制作公司
  • 做网站和做公众号北京云邦网站建设
  • 如何做网站地图做网站常用图标
  • 网页给别人做的 网站后续收费网站建设的ppt
  • python做网站内容爬虫江宁网站建设要多少钱
  • 英文都不懂 学网站建设维护难吗山西响应式网站建设哪家有
  • 国家建设部查询网站微信怎么制作网页
  • 响应式网站报价无法打开网页如何解决
  • 广州专业的网站制作audio player wordpress
  • 阿芹网站建设安庆市建设局网站
  • php网站后台开发wordpress网易云插件怎么用