概述:web服务器其实就是运行在物理机上的web服务器。它等待客户端向他发送请求,接收成功后,用客户端请求的资源响应。客户端与服务器端的通信是通过http协议实现的。客户端可以是浏览器或可以发送请求的程序。1、一个简单的web服务器我们可以使用socket()函数来创建一个网络连接,或者打开一个网络文件,socket的返回值是一个文件描述符。有了文件描述符,我们就可以使用普通的文件操作函数来传输数据,比如read用来读数据,write用来写数据。只要创建连接,剩下的就是文件操作了。importsockethost,port='',8080#SOCK_STREAM流格式socket,也叫面向连接的socket(使用TCP协议)#AF_INET使用ipv4地址#一般有af和type两个参数,就可以创建一个socket。操作系统会自动推导出协议类型listen_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM,proto=0)listen_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)listen_socket.bind((host,port))listen_socket.listen(5)print(f'ServiceHTTPonPort{port}')whileTrue:client_connection,client_address=listen_socket.accept()request=client_connection.recv(1024)print(request)http_response="""\HTTP/1.1200OKHelloworld!"""client_connection.sendall(http_response)client_connection.close()接下来我们考虑如何在我们编写的web服务器上运行我们的web应用程序,那么我们如何做不同的应用程序才能运行通常在Web服务器上,无需修改体系结构和代码。答案是pythonwebservergatewayinterface(简称WSGI,读作wizgy)。WSGI允许开发人员选择将Web应用程序框架与Web服务器分开。通过这种方式,您可以混合搭配Web框架和Web服务器,选择适合您需要的一对。例如,您可以在Gunicorn或Nginx/uWSGI或Waitress上运行Django、Flask或Pyramid。真正的混搭,这要归功于WSGI对服务器和模式的支持。下面我们去实现一个简单的WSGI服务:importsocketimportsysimportioclassWSGIServer(object):address_family=socket.AF_INETsocket_type=socket.SOCK_STREAMrequest_queue_size=1def__init__(self,server_address):self.listen_socket=socket.socket(self.address_family,self.socket_type)self.listen_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)self.listen_socket.listen(self.request_queue_size)host,port=self.listen_socket.getsockname()[:2]self.server_name=socket.getfqdn(host)self.server_port=portself.headers_set=[]defset_app(self,application):self.application=applicationdefserver_forever(self):listen_socket=self.listen_socketwhileTrue:self.client_connection,self.client_address=listen_socket.accept()self.handle_one_request()defhandle_one_request(self):self.request_data=self.client_connection.recv(1024).decode('utf-8')self.parse_request(self.request_data)env=self.get_environ()结果=self.application(env,self.start_response)self.finish_response(result)defparse_reqeust(self,文本):request_line=text.splitlines()request_line=request_line.rstrip('\r\n')(self.request_method,self.path,self.request_version)=request_line.split()defget_environ(self):env=dict()env['wsgi.version']=(1,0)env['wsgi.url_scheme']='http'env['wsgi.input']=io.StringIO(self.request_data)env['wsgi.errors']=sys.stderrenv['wsgi.multithread']=Falseenv['wsgi.multiprocess']=Falseenv['wsgi.run_once']=False#需要的CGI变量env['REQUEST_METHOD']=self.request_method#GETenv['PATH_INFO']=self.path#/helloenv['SERVER_NAME']=self.server_name#localhostenv['SERVER_PORT']=str(self.server_port)#8888returnenvdefstart_response(self,status,response_headers,exc_info=None):server_headers=[('Date','Tue,26Mar201900:22:48GMT'),('Server','WSGIServer0.2'),]self.headers_set=[status,response_headers+server_headers]deffinish_response(self,result):尝试:status,response_headers=self.headers_setresponse=f'HTTP/1.1{status}\r\n'forheaderinresponse_headers:response+="{}:{}\r\n".format(*header)response+='\r\n'fordatainresult:response+=dataprint('====')self.client_connection.sendall(response)除了Exceptionase:print(e)finally:self.client_connection.close()SERVER_ADDRESS=(host,port)='',5000defmake_server(服务器地址,应用程序):服务r=WSGIServer(server_address)server.set_app(application)returnserverif__name__=='__main__':iflen(sys.argv)<2:sys.exit('ProvideaWSGIapplicationobjectasmodule:callable')app_path=sys.argv[1]模块,application=app_path.split(":")module=__import__(module)application=getattr(module,application)httpd=make_server(SERVER_ADDRESS,application)print('----')httpd。server_forever()接下来,让我们实现一个简单的并发服务器。我们前面写的是迭代服务器,一次只能处理一个请求。导入osimporttimeimportsocketSERVER_ADDRESS=(HOST,PORT)='',5000REQUEST_QUEUE_SIZE=5defhandle_request(client_conn):request=client_conn.revc(1024)print(f"Childpid:{os.getpid()},Parentpid:{os.getppid()}")print(request.decode())http_response=b"""HTTP/1.1200OKHelloworld!"""client_conn.sendall(http_response)time.sleep(60)defserver_forever():#创建套接字listen_sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#绑定地址listen_sock.bind(SERVER_ADDRESS)#设置监听数listen_sock.listen(REQUEST_QUEUE_SIZE)print(f"Parentpid:{os.getpid()}")#AlwaysreceivewhileTrue:client_conn,client_addr=listen_sock.accept()#创建进程pid=os.fork()ifpid==0:#子进程#关闭子进程复制父进程的socketlisten_sock.close()#处理请求handle_request(client_conn)#之后processing,connectCloseclient_conn.close()#子进程退出os._exit(0)else:#Parentprocess#关闭连接client_conn.close()以上是并发服务器的源码,现在唯一作用server父进程就是接受一个新的clientConnection,fork一个新的子进程来处理client的请求,然后再反复accept另一个clientconnection,就没有别的事可做了。服务器父进程不处理客户端请求-它的弟弟(子进程)执行此操作。上面的服务器可能会产生僵尸进程,我们改一下importosimporttimeimporterrnoimportsignalimportsocketSERVER_ADDRESS=(HOST,PORT)='',5000REQUEST_QUEUE_SIZE=5defwait_child(signum,frame):whileTrue:try:#Adoptos.waitpidsystemCallpid,status=os.waitpid(-1,#等待所有子进程被处理os.WNOHANG#不要阻塞并返回EWOULDBLOCK错误)exceptOSErrorase:returnifpid==0:returndefhandle_request(client_conn):request=client_conn.revc(1024)print(f"Childpid:{os.getpid()},Parentpid:{os.getppid()}")print(request.decode())http_response=b"""HTTP/1.1200OKHelloworld!"""client_conn.sendall(http_response)time.sleep(60)defserver_forever():#创建套接字listen_sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#绑定地址listen_sock.bind(SERVER_ADDRESS)#设置监听数listen_sock.listen(REQUEST_QUEUE_SIZE)print(f"Parentpid:{os.getpid()}")#注册等待函数,当父进程退出时,会触发wait_child函数#等待子进程执行完signal.signal(signal.SIGCHLD,wait_child)#保持接收whileTrue:try:client_conn,client_addr=listen_sock.accept()exceptIOErrorase:code,msg=e.argsifcode==errno.EINTR:continueelse:raise#创建进程pid=os.fork()ifpid==0:#子进程#关闭子进程复制父进程的socketlisten_sock.close()#处理请求handle_request(client_conn)#处理后关闭连接client_conn.close()#子进程退出os._exit(0)else:#父进程#关闭连接client_conn.close()参考:https://ruslanspivak.com/lsbaws-part1/https://ruslanspivak.com/lsba...https://ruslanspivak。com/lsba...
