Python实现简易HTTP服务器:从原理到代码详解

Chloe
Chloe
发布时间:2026-10-13 | 4人浏览过
原创

本文详解如何使用Python构建简易HTTP服务器。从客户端与服务端角色、浏览器引擎、Socket通信机制到HTTP协议规范及URL结构进行系统梳理,结合Python代码示例演示如何监听端口、解析请求并返回符合HTTP标准的响应内容,帮助开发者理解Web通信底层原理。

若要编写一个类似Tomcat的简易服务器,首先需要明确以下核心概念:

1. 客户端(Client)与服务端(Server)的角色及功能

在角色A向角色B请求数据的场景中,A被视为客户端,B被视为服务端。客户端的主要职责是发送请求,并接收服务端根据该请求返回的数据;服务端的主要职责则是接收客户端的请求,并返回相应的数据。

2. 浏览器的定义及其工作原理

我们常提到的B/S(Browser/Server)架构和C/S(Client/Server)架构中,B/S架构特指基于浏览器的应用程序,即最终界面在浏览器中展示的应用属于B/S架构,否则为C/S架构(例如《英雄联盟》游戏)。但从本质上看,所有应用都属于C/S架构,因为浏览器本身就是一种特殊的客户端。

浏览器的特殊性在于其内部包含三大核心引擎:

  • DOM解析引擎:负责解析HTML文档结构。
  • 样式解析引擎:负责解析CSS样式表。
  • 脚本解析引擎:负责解析JavaScript代码。

3. Socket(套接字)

上述客户端与服务端如何建立连接并传递数据?这依赖于Socket。Socket编程提供了网络通信的能力,且几乎所有编程语言都支持Socket编程。

4. HTTP协议及其与TCP/IP的区别

客户端和服务端通过Socket实现了网络通信能力,从而能够传递数据。而协议则是规范数据传输的标准,意味着客户端和服务端之间的数据交互必须遵循特定的规范和标准,不能随意传输。

TCP/IP(Transmission Control Protocol/Internet Protocol):传输控制协议/网间协议。

HTTP(HyperText Transfer Protocol):超文本传输协议。

TCP/IP与HTTP的区别:

可以做一个形象的比喻:TCP/IP好比是马路,而HTTP则是马路上行驶的汽车。因此,HTTP协议是建立在TCP/IP协议基础之上的。

HTTP主要应用于Web程序,其设计初衷是为了提供一种发布和接收HTML页面的方法。这听起来可能较为抽象,具体而言,当我们访问一个网站时,需要获取基于该网站的内容(如HTML、CSS、JavaScript)。通过抓取浏览器接收到的资源包(可使用Fiddler等工具),我们会发现除了网页所需的实体内容外,还包含以下头部信息:

HTTP/1.1 200 OK Cache-Control: private Content-Type: text/plain; charset=utf-8 Content-Encoding: gzip Vary: Accept-Encoding Server: Microsoft-IIS/7.5 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Tue, 24 Jan 2017 03:25:23 GMT Connection: close Content-Length: 661

这就是HTTP协议的规范示例。例如,Content-Type指定了传输文件的格式,Content-Encoding规定了编码格式。此类参数众多,此处不再逐一介绍其具体含义。

5. URL(统一资源定位符)的含义

URL即我们常说的网址,以下面这个URL为例进行解析:http://198.2.17.25:8080/webapp/index.html

其含义是:定位到IP地址为198.2.17.25的服务器,访问其下目录为webapp中的index.html文件。

然而,我们常见的URL形式如下:http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx

这与上述示例本质相同,区别在于这里涉及域名解析,即将域名goodcandle.cnblogs.com解析为对应的IP地址。

在弄清楚以上五个核心概念后,我们开始编写代码。

webServer.py

import socket
import sys
import getFileContent
#声明一个将要绑定的IP和端口,这里是用本地地址
server_address = ('localhost', 8080)
class WebServer():
  def run(self):
    print >>sys.stderr, 'starting up on %s port %s' % server_address
    #实例化一个Socket
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP和端口
    sock.bind(server_address)
    #设置监听
    sock.listen(1)
    #这里首先给个死循环,其实这里是需要多线程的,再后续版本将会实现
    while True:
      #接受客户端的请求并得到请求信息和请求的端口信息
      connection, client_address = sock.accept()
      print >>sys.stderr, 'waiting for a connection'
      try:
        #获取请求信息
        data = connection.recv(1024)
        if data:
          #发送请求信息
          connection.sendall(getFileContent.getHtmlFile(data))
      finally:
        connection.close()

if __name__ == '__main__':
  server=WebServer()
  server.run()

webServer.py代码结构清晰简洁,connection.sendall()用于服务端向浏览器返回信息,但发送的数据必须严格遵循HTTP协议规范。

import sys
import os

#得到要发送的数据信息
def getHtmlFile(data):
  msgSendtoClient=""
  requestType=data[0:data.find("/")].rstrip()
  #判断是GET请求还是POST请求
  if requestType=="GET":
    msgSendtoClient=responseGetRequest(data,msgSendtoClient)
  if requestType=="POST":
    msgSendtoClient=responsePostRequest(data,msgSendtoClient)
  return msgSendtoClient

#打开文件,这里不直接写,二是去取要发送的文件再写
def getFile(msgSendtoClient,file):
    for line in file:
     msgSendtoClient+=line
    return msgSendtoClient

#筛选出请求的一个方法
def getMidStr(data,startStr,endStr):
  startIndex = data.index(startStr)
  if startIndex>=0:
    startIndex += len(startStr)
    endIndex = data.index(endStr)
    return data[startIndex:endIndex]

#获取要发送数据的大小,根据HTTP协议规范,要提前指定发送的实体内容的大小
def getFileSize(fileobject):
  fileobject.seek(0,2)
  size = fileobject.tell()
  return size

#设置编码格式和文件类型
def setParaAndContext(msgSendtoClient,type,file,openFileType):
  msgSendtoClient+="Content-Type: "+type+";charset=utf-8"
  msgSendtoClient+="Content-Length: "+str(getFileSize(open(file,"r")))+"\n"+"\n"
  htmlFile=open(file,openFileType)
  msgSendtoClient=getFile(msgSendtoClient,htmlFile)
  return msgSendtoClient

#GET请求的返回数据
def responseGetRequest(data,msgSendtoClient):
  return responseRequest(getMidStr(data,'GET /','HTTP/1.1'),msgSendtoClient)

#POST请求的返回数据
def responsePostRequest(data,msgSendtoClient):
  return responseRequest(getMidStr(data,'POST /','HTTP/1.1'),msgSendtoClient)

#请求返回数据
def responseRequest(getRequestPath,msgSendtoClient):
  headFile=open("head.txt","r")
  msgSendtoClient=getFile(msgSendtoClient,headFile)
  if getRequestPath==" ":
    msgSendtoClient=setParaAndContext(msgSendtoClient,"text/html","index.html","r")
  else:
    rootPath=getRequestPath
    if os.path.exists(rootPath) and os.path.isfile(rootPath):
      if ".html" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"text/html",rootPath,"r")
      if ".css" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"text/css",rootPath,"r")
      if ".js" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"application/x-javascript",rootPath,"r")
      if ".gif" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"image/gif",rootPath,"rb")
      if ".doc" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"application/msword",rootPath,"rb")
      if ".mp4" in rootPath:
        msgSendtoClient=setParaAndContext(msgSendtoClient,"video/mpeg4",rootPath,"rb")
    else:
      msgSendtoClient=setParaAndContext(msgSendtoClient,"application/x-javascript","file.js","r")
  return msgSendtoClient

Github源码下载:https://github.com/Jiashengp/Python_httpServer

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表