1. 写在前面

这里总结的并不够详细,有时间了再进行补充。

2. 设计思路

HTTP协议是建立在TCP上的
1. 建立服务器端TCP套接字(绑定ip,port),等待监听连接:listen
(2. 打开浏览器(client)访问这个(ip,port),服务器端接收连接:accept)
3. 获取浏览器的请求内容:data = recv(1024)
# 由于浏览器发送的request是HTTP格式的,需要解码
4. 将接收的报文节解码:decode
# 解析解码后的数据
5. 根据行分切数据
6. 解析首部行(header)为:方法,请求路径+文件名
7. 根据解析首部行获取的数据来查找并获取文件内容
8. 构建响应报文(也要是HTTP报文格式的),包括首部行响应信息(200 OK或是file cannot found)
9. 编码响应报文:encode
10. 关闭socket连接

3. 两个版本

3.1 多线程版本

这里采用多线程的方法对每一个请求连接本机的请求建立连接,缺点在于除非关闭服务器程序,否则已建立连接的套接字不会被释放,耗费资源

import socket
import threading def handleReq(clientSocket):
requestData = clientSocket.recv(1024)
requestList = requestData.decode().split("\r\n")
reqHeaderLine = requestList[0]
print("request line: " + reqHeaderLine)
fileName = reqHeaderLine.split(" ")[1].replace("/", "")
try:
file = open("./" + fileName, 'rb') # read the corresponding file from disk
print("fileName: " + fileName) # 查看文件名
except FileNotFoundError:
responseHeader = "HTTP/1.1 404 Not Found\r\n" + \
"Server: 127.0.0.1\r\n" + "\r\n" responseData = responseHeader + "No such file\nCheck your input\n" content = (responseHeader + responseData).encode(encoding="UTF-8") # send the correct HTTP response error
else:
content = file.read() # store in temporary buffer
file.close()
resHeader = "HTTP/1.1 200 OK\r\n"
fileContent01 = "Server: 127.0.0.1\r\n"
fileContent02 = content.decode()
response = resHeader + fileContent01 + "\r\n" + fileContent02 # send the correct HTTP response
clientSocket.sendall(response.encode(encoding="UTF-8")) def startServer(serverAddr, serverPort):
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind((serverAddr, serverPort))
serverSocket.listen(0)
while True:
try:
print("wait for connecting...")
print("while true")
clientSocket, clientAddr = serverSocket.accept()
print("one connection is established, ", end="")
print("address is: %s" % str(clientAddr))
handleThread = threading.Thread(target=handleReq, args=(clientSocket,))
handleThread.start()
print("client close")
except Exception as err:
print(err)
break
serverSocket.close() # while出错了就关掉 if __name__ == '__main__':
ipAddr = "127.0.0.1"
port = 8000
startServer(ipAddr, port)

3.2 多进程版本

改进了多线程版本的“缺点”

import multiprocessing
import socket def handleReq(clientSocket):
requestData = clientSocket.recv(1024)
requestList = requestData.decode().split("\r\n")
reqHeaderLine = requestList[0]
print("request line: " + reqHeaderLine)
fileName = reqHeaderLine.split(" ")[1].replace("/", "")
try:
file = open("./" + fileName, 'rb') # read the corresponding file from disk
print("fileName: " + fileName) # 查看文件名
except FileNotFoundError:
responseHeader = "HTTP/1.1 404 Not Found\r\n" + \
"Server: 127.0.0.1\r\n" + "\r\n" responseData = responseHeader + "No such file\nCheck your input\n" content = (responseHeader + responseData).encode(encoding="UTF-8") # send the correct HTTP response error
else:
content = file.read() # store in temporary buffer
file.close()
resHeader = "HTTP/1.1 200 OK\r\n"
fileContent01 = "Server: 127.0.0.1\r\n"
fileContent02 = content.decode()
response = resHeader + fileContent01 + "\r\n" + fileContent02 # send the correct HTTP response
clientSocket.sendall(response.encode(encoding="UTF-8")) def startServer(serverAddr, serverPort):
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serverSocket.bind((serverAddr, serverPort))
serverSocket.listen(0)
while True:
try:
print("wait for connecting...")
print("while true")
clientSocket, clientAddr = serverSocket.accept()
print("one connection is established, ", end="")
print("address is: %s" % str(clientAddr))
handleProcess = multiprocessing.Process(target=handleReq, args=(clientSocket,))
handleProcess.start() # handle request
clientSocket.close()
print("client close")
except Exception as err:
print(err)
break
serverSocket.close() # while出错了就关掉 if __name__ == '__main__':
ipAddr = "127.0.0.1"
port = 8000
startServer(ipAddr, port)

这个版本与多线程版本的区别:

1. 建立套接字时对套接字进行了相关设置【稍后解释】

2. 在开启新进程之后调用“clientSocket.close()”释放资源

对第一点不同的解释

下面解释的来源:https://www.jb51.net/article/50858.htm

python定义了setsockopt()和getsockopt(),一个是设置选项,一个是得到设置。这里主要使用setsockopt(),具体结构如下:

setsockopt(level,optname,value)

level定义了哪个选项将被使用。通常情况下是SOL_SOCKET,意思是正在使用的socket选项。它还可以通过设置一个特殊协议号码来设置协议选项,然而对于一个给定的操作系统,大多数协议选项都是明确的,所以为了简便,它们很少用于为移动设备设计的应用程序。

optname参数提供使用的特殊选项。关于可用选项的设置,会因为操作系统的不同而有少许不同。如果level选定了SOL_SOCKET,那么一些常用的选项见下表:

选项

意义

期望值

SO_BINDTODEVICE

可以使socket只在某个特殊的网络接口(网卡)有效。也许不能是移动便携设备

一个字符串给出设备的名称或者一个空字符串返回默认值

SO_BROADCAST

允许广播地址发送和接收信息包。只对UDP有效。如何发送和接收广播信息包

布尔型整数

SO_DONTROUTE

禁止通过路由器和网关往外发送信息包。这主要是为了安全而用在以太网上UDP通信的一种方法。不管目的地址使用什么IP地址,都可以防止数据离开本地网络

布尔型整数

SO_KEEPALIVE

可以使TCP通信的信息包保持连续性。这些信息包可以在没有信息传输的时候,使通信的双方确定连接是保持的

布尔型整数

SO_OOBINLINE

可以把收到的不正常数据看成是正常的数据,也就是说会通过一个标准的对recv()的调用来接收这些数据

布尔型整数

SO_REUSEADDR

当socket关闭后,本地端用于该socket的端口号立刻就可以被重用。通常来说,只有经过系统定义一段时间后,才能被重用。

布尔型整数

本节在学习时,用到了SO_REUSEADDR选项,具体写法是:

S.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 这里value设置为1,表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。

Python3网络学习案例三:编写web server的更多相关文章

  1. Python3网络学习案例四:编写Web Proxy

    代理服务器的定义和作用请走百度百科~ 1. Web Proxy的实现思路 这是基于上一篇"编写Web Server"写的,主要逻辑见下图: 我们要写的就是中间的Web Proxy部 ...

  2. Python3网络学习案例一:Ping详解

    1. 使用Ping做什么 ping用于确定本地主机是否能与另一台主机成功交换(发送与接收)数据包,再根据返回的信息,就可以推断TCP/IP参数是否设置正确,以及运行是否正常.网络是否通畅等. 2. 效 ...

  3. Python3网络学习案例二:traceroute详解

    1. 写在前面 本文是基于上一篇"ping详解"写的: 不同操作系统下的命令也不同,本文仅针对windows系统,命令为"tracert xxx",效果如下 2 ...

  4. python3.4学习笔记(三) idle 清屏扩展插件

    python3.4学习笔记(三) idle 清屏扩展插件python idle 清屏问题的解决,使用python idle都会遇到一个常见而又懊恼的问题——要怎么清屏?在stackoverflow看到 ...

  5. Java嵌入式数据库H2学习总结(三)——在Web应用中嵌入H2数据库

    H2作为一个嵌入型的数据库,它最大的好处就是可以嵌入到我们的Web应用中,和我们的Web应用绑定在一起,成为我们Web应用的一部分.下面来演示一下如何将H2数据库嵌入到我们的Web应用中. 一.搭建测 ...

  6. ios开发网络学习AFN三:AFN的序列化

    #import "ViewController.h" #import "AFNetworking.h" @interface ViewController () ...

  7. 简易web server之python实现

    网络编程一项基本功是socket编程,包括TCP socket,UDP socket的客户端.服务器端编程. 应用层的各路协议如http,smtp,telnet,ftp等都依赖于传输层的TCP或者UD ...

  8. 自己动手实现网络服务器(Web Server)——基于C#

    前言 最近在学习网络原理,突然萌发出自己实现一个网络服务器的想法,并且由于第三代小白机器人的开发需要,我把之前使用python.PHP写的那部分代码都迁移到了C#(别问我为什么这么喜欢C#),之前使用 ...

  9. python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容

    python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...

随机推荐

  1. Dynamically allocated memory 动态分配内存【malloc】Memory leaks 内存泄漏

    内存泄露Memory leaks :没有指针指向原来a分配出来的那段空间了

  2. Java date format 时间格式化

      import java.util.Date; import java.text.DateFormat; /** * 格式化时间类 * DateFormat.FULL = 0 * DateForma ...

  3. c语言 static的用法

    static在c里面可以用来修饰变量,也可以用来修饰函数.先看用来修饰变量的时候.变量在c里面可分为存在全局数据区.栈和堆里.其实我们平时所说的堆栈是栈而不是堆,不要弄混.int a ;int mai ...

  4. K-DTree入门

    \(K-D Tree\),一种用来维护\(K\)维数据的数据结构.常用于维护各种高维的数据,或者是邻近搜索等.从另一种意义上说,实际上就是高维的二叉搜索树.对于一些常见的问题,如\(k\)远点对.三位 ...

  5. vue3.0版本安装

    如果安装过其他版本的vue的话先卸载 npm uninstall -g vue-cli //卸载指令 卸载不会影响以前项目的启动 然后安装 NPM安装: npm install -g @vue/cli ...

  6. 全方位剖析 Linux 操作系统,太全了!!!

    Linux 简介 UNIX 是一个交互式系统,用于同时处理多进程和多用户同时在线.为什么要说 UNIX,那是因为 Linux 是由 UNIX 发展而来的,UNIX 是由程序员设计,它的主要服务对象也是 ...

  7. AMD Ryzen 5000系列桌面处理器 2020年10月8日发布

    AMD Ryzen 5 5600X 6核心12线程,基础频率3.7GHz,最大频率4.6GHz,二级缓存3MB,三级缓存32MB,不锁频,支持DDR4 3200MHz内存,台积电7纳米工艺,PCIe ...

  8. GUI版本的emacs

    概要 emacs 配置 X11 配置 输入法配置 spacemacs 中的配置 fcitx 汉字显示方块的问题 总结 优势 劣势 概要 之前一直使用 terminal 版本的 emacs, 性能和显示 ...

  9. Rust之路(2)——数据类型 上篇

    [未经书面同意,严禁转载] -- 2020-10-13 -- Rust是系统编程语言.什么意思呢?其主要领域是编写贴近操作系统的软件,文件操作.办公工具.网络系统,日常用的各种客户端.浏览器.记事本. ...

  10. openresty使用redis作本地缓存

    一,为什么要使用redis作本地缓存? 1,使用缓存通常会有三层 当使用openresty作为web服务器时,我们更看重是的它可以通过lua编程的扩展能力,就openresty而言,它可以实现的功能非 ...