2012-04-11 39 views
5

我有一個用Python編寫的非常簡單的網絡服務器。它監聽端口13000,如果在瀏覽器中打開http://localhost:13000,如何才能使其傳遞一個簡單的「Hello World」網頁?如何正確使用套接字庫發送HTTP響應與Python?

就在那裏是我的代碼:

# set up socket and connection 
while True: 
    sock, addr = servSock.accept() 
    # WHAT GOES HERE? 
    sock.close() 

正如你所看到的,我不完全知道如何真正發回的網頁?

我只需要使用socket庫。

編輯:問題不是我不知道如何制定HTTP響應,我不知道如何實際使它顯示在我的瀏覽器!它只是繼續旋轉/加載。

回答

9

根據問題的變化更新

可能的話,它不斷旋轉,因爲由於缺少的的Content-LengthConnection頭組合,瀏覽器可能會以爲這是Connection: keep-alive,所以它繼續接受永遠從你的服務器數據。嘗試發送Connection: close,並通過實際Content-Length看看是否有幫助。


這不會做你期望的嗎? :)

#!/usr/bin/env python 
# coding: utf8 

import socket 

MAX_PACKET = 32768 

def recv_all(sock): 
    r'''Receive everything from `sock`, until timeout occurs, meaning sender 
    is exhausted, return result as string.''' 

    # dirty hack to simplify this stuff - you should really use zero timeout, 
    # deal with async socket and implement finite automata to handle incoming data 

    prev_timeout = sock.gettimeout() 
    try: 
     sock.settimeout(0.01) 

     rdata = [] 
     while True: 
      try: 
       rdata.append(sock.recv(MAX_PACKET)) 
      except socket.timeout: 
       return ''.join(rdata) 

     # unreachable 
    finally: 
     sock.settimeout(prev_timeout) 

def normalize_line_endings(s): 
    r'''Convert string containing various line endings like \n, \r or \r\n, 
    to uniform \n.''' 

    return ''.join((line + '\n') for line in s.splitlines()) 

def run(): 
    r'''Main loop''' 

    # Create TCP socket listening on 10000 port for all connections, 
    # with connection queue of length 1 
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, \ 
           socket.IPPROTO_TCP) 
    server_sock.bind(('0.0.0.0', 13000)) 
    server_sock.listen(1) 

    while True: 
     # accept connection 
     client_sock, client_addr = server_sock.accept() 

     # headers and body are divided with \n\n (or \r\n\r\n - that's why we 
     # normalize endings). In real application usage, you should handle 
     # all variations of line endings not to screw request body 
     request = normalize_line_endings(recv_all(client_sock)) # hack again 
     request_head, request_body = request.split('\n\n', 1) 

     # first line is request headline, and others are headers 
     request_head = request_head.splitlines() 
     request_headline = request_head[0] 
     # headers have their name up to first ': '. In real world uses, they 
     # could duplicate, and dict drops duplicates by default, so 
     # be aware of this. 
     request_headers = dict(x.split(': ', 1) for x in request_head[1:]) 

     # headline has form of "POST /can/i/haz/requests HTTP/1.0" 
     request_method, request_uri, request_proto = request_headline.split(' ', 3) 

     response_body = [ 
      '<html><body><h1>Hello, world!</h1>', 
      '<p>This page is in location %(request_uri)r, was requested ' % locals(), 
      'using %(request_method)r, and with %(request_proto)r.</p>' % locals(), 
      '<p>Request body is %(request_body)r</p>' % locals(), 
      '<p>Actual set of headers received:</p>', 
      '<ul>', 
     ] 

     for request_header_name, request_header_value in request_headers.iteritems(): 
      response_body.append('<li><b>%r</b> == %r</li>' % (request_header_name, \ 
                request_header_value)) 

     response_body.append('</ul></body></html>') 

     response_body_raw = ''.join(response_body) 

     # Clearly state that connection will be closed after this response, 
     # and specify length of response body 
     response_headers = { 
      'Content-Type': 'text/html; encoding=utf8', 
      'Content-Length': len(response_body_raw), 
      'Connection': 'close', 
     } 

     response_headers_raw = ''.join('%s: %s\n' % (k, v) for k, v in \ 
               response_headers.iteritems()) 

     # Reply as HTTP/1.1 server, saying "HTTP OK" (code 200). 
     response_proto = 'HTTP/1.1' 
     response_status = '200' 
     response_status_text = 'OK' # this can be random 

     # sending all this stuff 
     client_sock.send('%s %s %s' % (response_proto, response_status, \ 
                 response_status_text)) 
     client_sock.send(response_headers_raw) 
     client_sock.send('\n') # to separate headers from body 
     client_sock.send(response_body_raw) 

     # and closing connection, as we stated before 
     client_sock.close() 

run() 

有關更詳細的說明,請參閱description of HTTP protocol

+1

是的,但...瀏覽器只是「旋轉」,什麼也沒有顯示? – antonpug 2012-04-11 21:29:06

+0

我已經更新了代碼示例以保證工作100%:)我希望您能找到有用的標頭處理的基本原則,但我建議您不要依賴這種代碼並實現全功能的HTTP請求解析器。 – toriningen 2012-04-11 21:55:24

+1

好吧,你已經做出了相當完整的回答......雖然我認爲它仍在旋轉的原因(直到超時)是因爲它正在等待雙倍「\ n」。但至少,你的代碼示例是一個很好的代碼片段,以防萬一;) – zmo 2012-04-11 22:25:25

4

發送回是這樣的:

HTTP/1.1 200 OK 
Date: Wed, 11 Apr 2012 21:29:04 GMT 
Server: Python/6.6.6 (custom) 
Content-Type: text/html 

然後實際的HTML代碼。確保在Content-Type行之後和html之前有一個換行符。

1

或者,如果你只是不想記得完整協議,你可以找到它再次使用:

% nc stackoverflow.com 80 
GET/HTTP/1.1 
Host: stackoverflow.com 

HTTP/1.1 200 OK 
Cache-Control: public, max-age=60 
Content-Type: text/html; charset=utf-8 
Expires: Wed, 11 Apr 2012 21:33:49 GMT 
Last-Modified: Wed, 11 Apr 2012 21:32:49 GMT 
Vary: * 
Date: Wed, 11 Apr 2012 21:32:49 GMT 
Content-Length: 206008 

[...] 
% 

好,你應該通常喜歡一個網站,看起來更簡潔(通常只有一個服務靜態文件)比計算器;)

的最低要求(你會發現答案)是:

sock.send(r'''HTTP/1.0 200 OK 
Content-Type: text/plain 

Hello, world! 

''') 

2回報是強制性的服務器得到的答案,OT herwise瀏覽器無限期地等待頭

但要模仿一個Web服務器的行爲,不要忘記把你的答案瀏覽器發送你的一些數據,然後兩個回車後,通常你能得到什麼利用發送:

% nc -kl localhost 13000 
GET/HTTP/1.1 
Host: localhost:13000 
User-Agent: Mozilla/5.0... 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip, deflate 
DNT: 1 
Connection: keep-alive 

% 

這樣你就可以提高你的測試例程

1

您可能要結帳Web對象http://www.webob.org/

這是一個SIM卡用於創建與HTTP兼容的請求和響應的輕量級項目。你可以做的只是與你的請求/響應對象的東西......或者只是委託繁重到的WebObjects

樣品

>>> from webob import Response 
>>> res = Response() 
>>> res.status 
'200 OK' 
>>> res.headerlist 
[('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', '0')] 
>>> res.body 
'' 
2
# set up socket and connection 
while True: 
sock, addr = servSock.accept() 
sock.send("HTTP/1.1 200 OK\n" 
     +"Content-Type: text/html\n" 
     +"\n" # Important! 
     +"<html><body>Hello World</body></html>\n"); 
sock.close() 
0

我把前面的回答和編輯的Python3 UTF編碼-8和字節編碼。感謝您的原始答案,它幫助了很多。

import socket 

MAX_PACKET = 32768 

def recv_all(sock): 
    r'''Receive everything from `sock`, until timeout occurs, meaning sender 
    is exhausted, return result as string.''' 

    # dirty hack to simplify this stuff - you should really use zero timeout, 
    # deal with async socket and implement finite automata to handle incoming data 

    prev_timeout = sock.gettimeout() 
    try: 
     sock.settimeout(0.1) 

     rdata = [] 
     while True: 
      try: 
       # Gotta watch for the bytes and utf-8 encoding in Py3 
       rdata.append(sock.recv(MAX_PACKET).decode('utf-8')) 
      except socket.timeout: 
       return ''.join(rdata) 

     # unreachable 
    finally: 
     sock.settimeout(prev_timeout) 

def normalize_line_endings(s): 
    r'''Convert string containing various line endings like \n, \r or \r\n, 
    to uniform \n.''' 
    test = s.splitlines() 
    return ''.join((line + '\n') for line in s.splitlines()) 

def run(): 
    r'''Main loop''' 

    # Create TCP socket listening on 10000 port for all connections, 
    # with connection queue of length 1 
    server_sock = socket.socket(socket.AF_INET, 
           socket.SOCK_STREAM, 
           socket.IPPROTO_TCP) 
    #Added the port 13001 for debuging purposes 

    try: 
     server_sock.bind(('0.0.0.0', 13000)) 
     print('PORT 13000') 
    except: 
     server_sock.bind(('0.0.0.0', 13001)) 
     print('PORT 13001') 
    # except: 
    #  server_sock.bind(('0.0.0.0', 13002)) 
    #  print('PORT 13002') 

    server_sock.listen(1) 

    while True: 
     # accept connection 
     try: 
      client_sock, client_addr = server_sock.accept() 

      # headers and body are divided with \n\n (or \r\n\r\n - that's why we 
      # normalize endings). In real application usage, you should handle 
      # all variations of line endings not to screw request body 
      request = normalize_line_endings(recv_all(client_sock)) # hack again 

      request_head, request_body = request.split('\n\n', 1) 

      # first line is request headline, and others are headers 
      request_head = request_head.splitlines() 
      request_headline = request_head[0] 
      # headers have their name up to first ': '. In real world uses, they 
      # could duplicate, and dict drops duplicates by default, so 
      # be aware of this. 
      request_headers = dict(x.split(': ', 1) for x in request_head[1:]) 

      # headline has form of "POST /can/i/haz/requests HTTP/1.0" 
      request_method, request_uri, request_proto = request_headline.split(' ', 3) 

      response_body = [ 
       '<html><body><h1 style="color:red">Hello, world!</h1>', 
       '<p>This page is in location %(request_uri)r, was requested ' % locals(), 
       'using %(request_method)r, and with %(request_proto)r.</p>' % locals(), 
       '<p>Request body is %(request_body)r</p>' % locals(), 
       '<p>Actual set of headers received:</p>', 
       '<ul>', 
      ] 

      for request_header_name, request_header_value in request_headers.items(): 
       response_body.append('<li><b>%r</b> == %r</li>' % (request_header_name, 
                    request_header_value)) 

      response_body.append('</ul></body></html>') 

      response_body_raw = ''.join(response_body) 

      # Clearly state that connection will be closed after this response, 
      # and specify length of response body 
      response_headers = { 
       'Content-Type': 'text/html; encoding=utf8', 
       'Content-Length': len(response_body_raw), 
       'Connection': 'close', 
      } 

      response_headers_raw = ''.join('%s: %s\n' % (k, v) for k, v in \ 
                response_headers.items()) 

      # Reply as HTTP/1.1 server, saying "HTTP OK" (code 200). 
      response_proto = 'HTTP/1.1'.encode() 
      response_status = '200'.encode() 
      response_status_text = 'OK'.encode() # this can be random 

      # sending all this stuff 
      client_sock.send(b'%s %s %s' % (response_proto, response_status, 
                  response_status_text)) 
      client_sock.send(response_headers_raw.encode()) 
      client_sock.send(b'\n') # to separate headers from body 
      client_sock.send(response_body_raw.encode()) 

      # and closing connection, as we stated before 

     finally: 
      client_sock.close() 

run() 
相關問題