python學習(九) 網絡編程學習--簡易網站服務器

python `網絡編程`和其他語言都是一樣的,服務器這塊步驟為:
`1. 創建套接字`
`2. 綁定地址`
`3. 監聽該描述符的所有請求`
`4. 有新的請求到了調用accept處理請求`

Python Web服務器網關接口(Python Web Server Gateway Interface,簡稱`“WSGI”`),可以保證同一個服務器響應不同應用框架的請求,WSGI的出現,讓開發者可以將網絡框架與網絡服務器的選擇分隔開來,例如,你可以使用Gunicorn或Nginx/uWSGI或Waitress服務器來運行Django、Flask或Pyramid應用。下面簡單實現一個機遇WSGI協議的服務器。

import socket
from io import StringIO
import sysclass WSGIServer(object):address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMrequest_queue_size = 1def __init__(self, server_address):# Create a listening socketself.listen_socket = listen_socket = socket.socket(self.address_family,self.socket_type)# Allow to reuse the same addresslisten_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# Bindlisten_socket.bind(server_address)# Activatelisten_socket.listen(self.request_queue_size)# Get server host name and porthost, port = self.listen_socket.getsockname()[:2]self.server_name = socket.getfqdn(host)self.server_port = port# Return headers set by Web framework/Web applicationself.headers_set = []

  


定義了一個WSGIServer類,并且在類的init函數完成了套接字的創建、綁定、監聽等。
下面實現WSGIServer的輪詢檢測新的連接并處理連接:

def set_app(self, application):self.application = applicationdef serve_forever(self):listen_socket = self.listen_socketwhile True:#   New client connectionself.client_connection, client_address = listen_socket.accept()# Handle one request and close the client connection. Then# loop over to wait for another client connectionself.handle_one_request()

?

實現處理請求的函數

def handle_one_request(self):self.request_data = request_data = self.client_connection.recv(1024)# Print formatted request data a la 'curl -v'print(''.join('< {line}\n'.format(line=line)for line in request_data.splitlines()))self.parse_request(request_data)# Construct environment dictionary using request dataenv = self.get_environ()# It's time to call our application callable and get# back a result that will become HTTP response bodyresult = self.application(env, self.start_response)# Construct a response and send it back to the clientself.finish_response(result)

?

解析請求

def parse_request(self, text):request_line = text.splitlines()[0]request_line = request_line.rstrip('\r\n')# Break down the request line into components(self.request_method, # GETself.path, # /helloself.request_version # HTTP/1.1) = request_line.split()

?


返回當前服務器wsgi版本等信息

def get_environ(self):env = {}
env['wsgi.version'] = (1, 0)env['wsgi.url_scheme'] = 'http'env['wsgi.input'] = StringIO.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 env

?


填寫app所需的回調函數

def start_response(self, status, response_headers, exc_info=None):# Add necessary server headersserver_headers = [('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),('Server', 'WSGIServer 0.2'),]self.headers_set = [status, response_headers + server_headers]# To adhere to WSGI specification the start_response must return# a 'write' callable. We simplicity's sake we'll ignore that detail# for now.# return self.finish_response

?

發送數據并且關閉連接

def finish_response(self, result):try:status, response_headers = self.headers_setresponse = 'HTTP/1.1 {status}\r\n'.format(status=status)for header in response_headers:response += '{0}: {1}\r\n'.format(*header)response += '\r\n'for data in result:response += data# Print formatted response data a la 'curl -v'print(''.join('> {line}\n'.format(line=line)for line in response.splitlines()))self.client_connection.sendall(response)finally:self.client_connection.close()

?

主函數和參數解析,創建服務器

SERVER_ADDRESS = (HOST, PORT) = '', 8888def 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(port=PORT))httpd.serve_forever()

?

將上面的文件保存為webserver.py
下面搭建虛擬環境,并且安裝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

?


編寫pyramidapp.py,主要是調用pyramidapp接口生成app

from pyramid.config import Configurator
from pyramid.response import Responsedef hello_world(request):return Response('Hello world from Pyramid!\n',content_type='text/plain',)config = Configurator()
config.add_route('hello', '/hello')
config.add_view(hello_world, route_name='hello')
app = config.make_wsgi_app()

?

可以通過自己開發的網絡服務器來啟動上面的Pyramid應用。
`python webserver.py pyramidapp:app`


同樣可以創建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',mimetype='text/plain')app = flask_app.wsgi_app

  

上述代碼的工作原理:

`1 網絡框架提供一個命名為application的可調用對象`。
`2 服務器每次從HTTP客戶端接收請求之后,調用application。它會向可調用對象傳遞一個名叫environ的字典作為參數,其中包含了WSGI/CGI的諸多變量,以及一個名為start_response的可調用對象`。
`3 框架/應用生成HTTP狀態碼以及HTTP響應報頭(HTTP response headers),然后將二者傳遞至start_response,等待服務器保存。此外,框架/應用還將返回響應的正文。
服務器將狀態碼、響應報頭和響應正文組合成HTTP響應,并返回給客戶端`。

可以采用多進程的方式處理多個客戶端請求,將上述代碼稍作修改

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(port=PORT))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()

  

grim_reaper 函數為捕捉子進程退出的回調函數,父進程等待所有子進程退出后再退出,避免僵尸進程。由于子進程退出父進程捕獲到消息,調用grim_reaper處理,由于父進程之前阻塞在accept上,捕獲子進程銷毀消息后,父進程accept失敗,所以增加了errno.EINTR錯誤判斷,如果是由于信號中斷導致accept失敗,就讓父進程繼續調用accept即可。


謝謝關注我的微信公眾平臺:

轉載于:https://www.cnblogs.com/secondtonone1/p/7481432.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371304.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371304.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371304.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

concurrency 方面的books

http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/轉載于:https://www.cnblogs.com/WCFGROUP/p/6566150.html

Spring 3.1緩存和配置

我最近在博客中談論有關Spring 3.1及其新的緩存注釋Cacheable和CacheEvict 。 與所有Spring功能一樣&#xff0c;您需要進行一定數量的設置&#xff0c;并且通常使用Spring的XML配置文件來完成。 在緩存的情況下&#xff0c;打開Cacheable和CacheEvict并不容易&#xff0c;因為…

按條件分類_保稅倉儲企業能否同時存儲非保貨物?“倉儲貨物安裝臺分類監管”如何申請?...

保稅倉儲企業能否同時存儲非保貨物呢&#xff1f;保稅和非保貨物是不是真的不能同在一個“屋檐下”呢&#xff1f;哪些企業可以開展“倉儲貨物按狀態分類監管”業務&#xff1f;企業又該如何申請該項業務&#xff1f;本文就對這些問題進行一下梳理。什么是“倉儲貨物按狀態分類…

ZooKeeper的原理(轉)

一、ZooKeeper的角色 領導者&#xff08;Leader&#xff09;&#xff0c;負責進行投票的發起和決議&#xff0c;更新系統狀態。 學習者&#xff08;Learner&#xff09;&#xff0c;包括跟隨者&#xff08;Follower&#xff09;和觀察者&#xff08;Observer&#xff09;&#…

java課堂筆記

轉載于:https://www.cnblogs.com/16-C-kai/p/6567042.html

Spring– DAO和服務層

歡迎來到Spring教程的第三部分。 在這一部分中&#xff0c;我們將繼續編寫Timesheet應用程序&#xff0c;這次我們將實現DAO層&#xff0c;業務服務并編寫一些測試。 在上一部分中&#xff0c;我們定義了GenericDao接口&#xff0c;該接口告訴我們需要對實體執行哪些操作。 現在…

51nod 1907(多項式乘法啟發式合并)

題目&#xff1a; 分析&#xff1a; 對于一個確定的生成子圖&#xff0c;很明顯是在一個連通塊上走&#xff0c;走完了再跳到另一個連通塊上&#xff0c;假設連通塊個數為cnt&#xff0c;那么答案一定是$min(a_{cnt-1},a_cnt,..,a_{n-1})$ 那現在的問題就是如何求出對于原圖而言…

煮飯的機器人作文_公示|“筆隨我心、心由筆動”作文大賽獲獎名單

卡士大昌杯“筆隨我心、心由筆動”獲獎作品開平的咸湯圓滑輪記&#xff0f;我的宅家成長記折疊式小屋&#xff0f;夕陽&#xff0f;包粽子在過去的卡士大昌杯“筆隨我心、心由筆動”作文活動中我們收到了許多優秀投稿經過專業團隊評選得出獲獎選手作品如下主辦方協辦方一等獎《…

BZOJ 4491: 我也不知道題目名字是什么

4491: 我也不知道題目名字是什么 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 278 Solved: 154[Submit][Status][Discuss]Description 給定一個序列A[i]&#xff0c;每次詢問l,r&#xff0c;求[l,r]內最長子串&#xff0c;使得該子串為不上升子串或不下降子串 Input 第一…

Spring-boot中讀取config配置文件的兩種方式

了解過spring-Boot這個技術的&#xff0c;應該知道Spring-Boot的核心配置文件application.properties&#xff0c;當然也可以通過注解自定義配置文件的信息。 Spring-Boot讀取配置文件的方式&#xff1a; 一.讀取核心配置文件信息application.properties的內容 核心配置文件是指…

JavaFX 2 GameTutorial第5部分

介紹 這是與JavaFX 2 Game Tutorial相關的六部分系列的第五部分。 我知道自從我寫關于游戲的博客以來已經很長時間了&#xff0c;但希望您仍然與我在一起。 如果您想回顧一下&#xff0c;請閱讀第1部分 &#xff0c; 第2 部分 &#xff0c; 第3 部分和第4 部分 &#xff0c;以了…

h5是可以一鍵打包小程序的_H5手機網站封裝打包微信小程序并實現分享及微信支付...

手機網站打包小程序教程&#xff0c;生成小程序&#xff0c;網頁版小程序 打包微信小程序&#xff0c;H5封裝成微信小程序。微信小程序開發一般分為2種方式&#xff0c;一種就是原生開發小程序&#xff0c;一種是將手機網站打包成小程序。原生開發小程序成本較高&#xff0c;技…

Hive中的數據庫、表、數據與HDFS的對應關系

1、hive數據庫 我們在hive終端&#xff0c;查看數據庫信息&#xff0c;可以看出hive有一個默認的數據庫default&#xff0c;而且我們還知道hive數據庫對應的是hdfs上面的一個目錄&#xff0c;那么默認的數據庫default到底對應哪一個目錄呢&#xff1f;我們可以通過hive配置文件…

軟件工程概論作業3

轉載于:https://www.cnblogs.com/clueless/p/6568351.html

使用JSF的面向服務的UI

在大型軟件開發項目中&#xff0c;面向服務的體系結構非常常見&#xff0c;因為它提供了可供不同團隊或部門使用的功能接口。 創建用戶界面時&#xff0c;應應用相同的原理。 對于具有開票部門和客戶管理部門等的大型公司&#xff0c;組織結構圖可能如下所示&#xff1a; 如果計…

pocib模板流程圖_各單據流程POCIB

POCIB各階段流程報關流程從廣義上講&#xff0c;報關是指進出境運輸工具負責人、進出境口貨物收發貨人、進出境物品的所有人或者他們的代理人向海關辦理運輸工具、貨物、物品進出境手續及相關手續的全過程。其中&#xff0c;進出境運輸工具負責人、進出口貨物收發貨人、進出境物…

WinDbg 查看靜態變量

有如下Class。若想查看靜態變量內容。因為靜態變量和類綁定&#xff0c;僅需要查看類即可。 namespace ConsoleApplication13 {class Program{public static string public_string "pubstr_static";public static string private_string "pristr_static"…

vue 固定div 滾動_vue.js-div滾動條隱藏但有滾動效果的實現方法

組件被包在一個高度固定的divmounted () {var boDiv document.getElementById(this.id);if(boDiv undefined){return;}var isFirefoxnavigator.userAgent.indexOf("Firefox")if(isFirefox>0){boDiv.addEventListener(DOMMouseScroll, function(event) { //火狐v…

JBoss核心Java Web服務

這篇博客文章涉及Web服務。 好吧&#xff0c;更確切地說&#xff0c;它處理JBoss上的“普通” java Web服務。 這意味著我們將創建一個沒有任何其他框架&#xff08;如CXF&#xff0c;Axis等&#xff09;的Web服務。 JBoss它自己提供對Web服務的支持。 因此&#xff0c;如果您真…

JavaSE--for each

參考&#xff1a;http://blog.csdn.net/yasi_xi/article/details/25482173 學習多線程的時候實例化線程數組而挖掘出來的一直以來的理解誤區 之前一直以為for each 本質上和for循環以及迭代器沒什么區別 1 package foreach;2 3 public class ForeachDemo1 {4 5 public …