今天翻看自己以前的博客时,发现了这则博客,距今大约也有一年多的时间了,觉得还是蛮有趣的一个作业,于是跟着博客又做了一遍,觉得之前的排版有点不大好,所以此番用markdown 稍微重做些修改更新一下博客。
顾名思义,本博客是介绍如何用python编写一个多线程web服务器,于是我利用socket模块写了个TCP服务器,程序很简单,运行程序后,通过浏览器端输入:
localhost:port/xxxxxx
可以访问到本地文件目录中的文件,我在本地编写了两个html文件用于访问测试:
- Success.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
</br>
<center><h2>Connect to local web server!</h2></center>
<center><img src="http://i2.sinaimg.cn/ent/m/c/2008-03-16/U2507P28T3D1949676F326DT20080316182933.jpg" width="300px"/></center>
</br>
<center><h4><a href="">MyCSDN</a></h4></center>
</body>
</html>
- Fail.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
<center><h2>所访问的路径不存在!</h2></center>
</br>
<!--打开网页图片-->
<center><img src="https://image.freepik.com/free-vector/404-error-web-template-with-cow-flying-flat-style_23-2147777436.jpg" width="400px"/></center>
</br></br>
<center><h4><a href="">LQ的博客</a></h4><center>
</body>
</html>
- 附上Python源代码:
#*************************************#
#*** python 多线程web服务器的设计 ****#
#*** Author LQ ****#
#*** Python 版本:3.6.2 ****#
#*************************************#
#-*- coding: utf-8 -*-
from socket import *
import threading # 引入线程模块
import os
# 针对线程使用编写的函数
def Server(tcpClisock, addr):
BUFSIZE = 1024 # 将缓冲区大小设置为 1KB。listen()方法的参数是在连接被转接或拒绝之前,传入连接请求的最大数
print('connected from:', addr)
rec = tcpClisock.recv(BUFSIZE)
data = rec.decode()
# 当服务器因为网络问题未收到返回信息时,直接退出
if len(data) == 0:
tcpClisock.close()
return
HOME_DIR = os.getcwd() # 服务器端可访问的文件目录
print("***************************************************\n")
index = 4 # 检索文件的搜索路径
# 找到路径
while data[index] != ' ':
index += 1
# 如果检索文件为空,则默认导向访问成功的页面
if index == 5 : direction = os.path.join(HOME_DIR, "Success.html")
else: direction = os.path.join(HOME_DIR, data[5 : index]) # 拼接出完整的路径
# 如果路径存在,本服务器默认只支持访问html文件
if os.path.exists(direction) and direction.endswith(".html"):
file=open(direction) # 打开路径中的文件
SUCCESS_PAGE = "HTTP/1.1 200 OK\r\n\r\n" + file.read() # 构造成功报文反馈给服务器
print(SUCCESS_PAGE)
tcpClisock.sendall(SUCCESS_PAGE.encode()) # 返回给客户端浏览器成功的页面
# sendall 函数只可发送字节类型,对字符串数据进行转换
tcpClisock.close() # 关闭专门针对一个客户机程序创建的新套接字
# 如果路径不存在,返回失败页面
else:
FAIL_PAGE = "HTTP/1.1 404 NotFound\r\n\r\n" + open(os.path.join(HOME_DIR, "Fail.html")).read()
print(FAIL_PAGE)
tcpClisock.sendall(FAIL_PAGE.encode()) # 返回给客户端浏览器失败的页面
# sendall 函数只可发送字节类型,对字符串数据进行转换
tcpClisock.close() # 关闭专门针对一个客户机程序创建的新套接字
if __name__ =='__main__':
HOST = "" # HOST 变量是空白的,这是对 bind()方法的标识,表示它可以使用任何可用的地址 # 此程序在什么主机上运行,就会为该主机的IP地址
PORT = 4004 # 随机的端口号,并且该端口号似乎没有被使用或被系统保留
ADDR = (HOST, PORT) # 地址 = 主机名 + 端口号
tcpSersock = socket(AF_INET, SOCK_STREAM) # 创建一个套接字对象
tcpSersock.bind(ADDR) # 将套接字绑定到服务器地址 相当于欢迎套接字,且绑定的是源ip地址以及源端口号。始终欢迎别的套接字的申请接入
tcpSersock.listen(5) # 启用服务器的监听,调用参数是在连接被转接或拒绝之前,传入连接请求的最大数,此处说明允许传入链接请求数为5
print("waiting for connection......\n")
while True: #进入无限循环
tcpClisock, addr = tcpSersock.accept()
# 调用 accept()函数之后,就开启了一个简单的(单线程)服务器,它会等待客户端的连接。
# 默认情况下,accept()是阻塞的,这意味着执行将被暂停,直到一个连接到达。
# 当一个传入的请求到达时,服务器会创建一个新的通信端口来直接与客户端进行通信,再次空出主要的端口
# 使用新的客户端套接字能够空出主线(原始服务器套接字)以使其能够接受新的客户端连接。
thread = threading.Thread(target=Server, args=(tcpClisock, addr))
thread.start() # 开始执行该线程
tcpSersock.close() # 这种情况永远也不会碰到,因为在此程序中,服务器始终在一个无限循环中运行
# 但如果写了一个处理程序,当一个处理程序检测到一些外部条件时,服务器就应该关闭。应该调用一个 close()方法关闭服务器的欢迎套接字
具体实现的功能有:
向服务器请求特定文件,从服务器文件系统读取请求的文件,并创建一个由请求的文件的html内容组成的HTTP响应报文反馈给浏览器并显示出来
HTTP/1.1 200 OK
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
</br>
<center><h2>Connect to local web server!</h2></center>
<center><img src="http://i2.sinaimg.cn/ent/m/c/2008-03-16/U2507P28T3D1949676F326DT20080316182933.jpg" width="300px"/></center>
</br>
<center><h4><a href="">MyCSDN</a></h4></center>
</body>
</html>
- 从后台可以看到请求的文件的内容
从浏览器端显示出的访问服务器文件成功的页面
多线程
connected from: ('127.0.0.1', 59634)
***************************************************
HTTP/1.1 200 OK
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
</br>
<center><h2>Connect to local web server!</h2></center>
<center><img src="http://i2.sinaimg.cn/ent/m/c/2008-03-16/U2507P28T3D1949676F326DT20080316182933.jpg" width="300px"/></center>
</br>
<center><h4><a href="">MyCSDN</a></h4></center>
</body>
</html>
connected from: ('127.0.0.1', 59670)
***************************************************
HTTP/1.1 200 OK
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
</br>
<center><h2>Connect to local web server!</h2></center>
<center><img src="http://i2.sinaimg.cn/ent/m/c/2008-03-16/U2507P28T3D1949676F326DT20080316182933.jpg" width="300px"/></center>
</br>
<center><h4><a href="">MyCSDN</a></h4></center>
</body>
</html>
多个客户端访问服务器时,都可以并发地访问,通过后台观察打印出的地址可以发现服务器已经接收到了多个客户端的访问,并且服务器为每个客户端联系时均建立了连接套接字
从浏览器访问可以看出,可以同时打开两个页面访问本地服务器,其中服务器的端口号为4004
异常检测
访问服务器中不存在的文件路径时,后台会返回访问失败的页面:
connected from: ('127.0.0.1', 35590)
***************************************************
HTTP/1.1 404 NotFound
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html"; charset="utf-8"/>
</head>
<body>
<center><h2>所访问的路径不存在!</h2></center>
</br>
<!--打开网页图片-->
<center><img src="https://image.freepik.com/free-vector/404-error-web-template-with-cow-flying-flat-style_23-2147777436.jpg" width="400px"/></center>
</br></br>
<center><h4><a href="">LQ的博客</a></h4><center>
</body>
</html>
此时浏览器端会返回访问失败的页面: