python 3 网络构建_Python3网络学习案例三:编写web server

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 多线程版本

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

#!/usr/bin/env python3#-*- coding: UTF-8 -*-

importsocketimportthreadingdefhandleRequest(tcpSocket):#1. Receive request message from the client on connection socket

requestData = tcpSocket.recv(1024)#2. Extract the path of the requested object from the message (second part of the HTTP header)

requestList = requestData.decode().split("\r\n")

reqHeaderLine=requestList[0]print("request line:" +reqHeaderLine)

fileName= reqHeaderLine.split(" ")[1].replace("/", "")#3. Read the corresponding file from disk

try:

file= open("./" + fileName, 'rb') #read the corresponding file from disk

print("fileName:" +fileName)#4. Store in temporary buffer

content = file.read().decode() #store in temporary buffer

file.close()

resHeader= "HTTP/1.1 200 OK\r\n" +\"Server: 127.0.0.1\r\n" + "\r\n"response= (resHeader + content).encode(encoding="UTF-8") #send the correct HTTP response

exceptFileNotFoundError:

content= "404 NOT FOUND\n"resHeader= "HTTP/1.1 404 Not Found\r\n" +\"Server: 127.0.0.1\r\n" + "\r\n"response= (resHeader + content).encode(encoding="UTF-8") #send the correct HTTP response error

#5. Send the correct HTTP response error

tcpSocket.sendall(response)#6. Send the content of the file to the socket

else:

tcpSocket.sendall(response)#7. Close the connection socket

tcpSocket.close()defstartServer(serverAddress, serverPort):#1. Create server socket

serverSocket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)

serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)#2. Bind the server socket to server address and server port

serverSocket.bind((serverAddress, serverPort))#3. Continuously listen for connections to server socket

serverSocket.listen(0)#4. When a connection is accepted, call handleRequest function, passing new connection socket (see

#https://docs.python.org/3/library/socket.html#socket.socket.accept)

whileTrue:try:print("wait for connecting...")print("while true")

tcpSocket, clientAddr=serverSocket.accept()print("one connection is established,", end="")print("address is: %s" %str(clientAddr))

handleThread= threading.Thread(target=handleRequest, args=(tcpSocket,))

handleThread.start()exceptException as err:print(err)break

#5. Close server socket

serverSocket.close()if __name__ == '__main__':whileTrue:try:

hostPort= int(input("Input the port you want:"))

startServer("", hostPort)break

exceptException as e:print(e)continue

3.2 多进程版本

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

importmultiprocessingimportsocket

defhandleReq(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) #查看文件名

exceptFileNotFoundError:

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"))defstartServer(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)whileTrue: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")exceptException as err:print(err)breakserverSocket.close()#while出错了就关掉

if __name__ == '__main__':ipAddr= "127.0.0.1"port= 8000startServer(ipAddr, port)

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

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

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

对第一点不同的解释

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被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值