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. Paxos 协议

    可用性与一致性 为了向用户提供更好的服务体验,现代软件架构越来越注重系统的可用性availability. 正是在这种趋势的驱动下,微服务与容器化技术才能在今天大行其道. 而高可用架构的前提是冗余: ...

  2. 020 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 14 变量与常量 知识总结

    020 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 14 变量与常量 知识总结 本文知识点:变量与常量 知识总结 Java中的标识符 Java中的关键字 目前常 ...

  3. VSCode搭建golang环境

    安装对应版本的Golang 略 VSCode安装对应 Go 插件 在应用商店安装即可:go VSCode安装 Go 工具: 在VSCode输入:Crtl + Shift + P 在弹出框输入:inst ...

  4. Ubuntu常用工具安装

    安装 aptitude 管理软件 $ sudo apt-get install aptitude 安装gdebi(安装deb包) # 安装: $ sudo apt install gdebi-core ...

  5. Appium自动化测试之环境安装

    安装前准备: Python 安装包下载       选择想要想在的python包Node-v6.11.2下载安卓SDK下载appium_forwindows下载 以上四个文件下载下来后,分别解压安装, ...

  6. 对do{ }while();一直以来的误解 -----如何理解do{ }while( );语句

    在do{ }while( ); 语句中,我之前的理解是:先执行一次do{ },然后判断while( )中的内容,一般里面都是字符串或者数值作比较嘛,所以理解是:如果判断的这个东西,在这个范围中(等于这 ...

  7. ubuntu19.10 系统需要安装的软件

    将ubuntu18 升级到ubuntu19 期间好几次卡在启动界面,比较担心要不要重装系统,有幸后来正常了.明显感觉操作快了不少.下半年稳定版就出来,到时候免不了再折腾一番,提前把安全记录做好. 下面 ...

  8. 8.Android-简单的登录案例编写

    本章来学习登录案例,由于还未学习自定义控件外观,所以ui界面先用最简单的,并保存登录账号密码到data/data/包名/files下 1.学习之前需要掌握的Context类(通过Context来往AP ...

  9. day44 Pyhton 数据库Mysql

    内容回顾 什么是进程? 就是为了形容执行中的程序的一种称呼 它是操作系统中资源分配的最小单位 进程之间是数据隔离的,占用操作系统资源相对多 独立存在的 谈谈你对并发的理解 同时有多个任务需要执行,但是 ...

  10. linux(centos8):kubernetes安装的准备工作

    一,安装docker-ce19.03.11 1,卸载podman [root@kubemaster ~]# dnf remove podman podman是红帽系os自带的容器,卸载是为了避免冲突 ...