第一章 网络编程协议
1.1 IP地址介绍
**IP地址的概念:**ip地址就是标识网络中设备的一个地址,好比现实生活中的家庭地址
**IP地址的表现形式:**IP地址分为两类:IPv4和IPv6
**IP地址的作用:**标识网络中唯一的一台设备
**查看IP地址:**Linux和Mac使用ifconfig查看,Window使用ipconfig查看
域名就是IP地址的别名本机的IP地址是localhost
1.2 端口和端口号介绍
每运行一个网络程序都会有一个端口,想要给对应的程序发送数据,找到对应端口即可
**什么是端口:**端口是传输数据的通道,好比教室的门,是数据传输的必经之路,每一个端口都会有一个对应的端口号,好比每个教师都有一个门牌号,想要找到端口,找到端口号即可
应用程序之间进行数据通信流程是这样的:通过IP地址找到对应的设备,通过端口号找到对应的端口,然后通过端口把数据传输给应用程序
**端口号分类:**知名端口号和动态端口号
- 知名端口号:知名端口号是指众所周知的端口号,范围从0到1023。这些端口号一般固定分配给一些服务,比如21端口分配给FTP(文件传输协议)服务,25端口分配给SMTP (简单邮件传输协议)服务,80端口分配给HTTP服务。
- 动态端口号:一般程序员开发应用程序使用端口号称为动态端口号,范围是从1024到65535。如果程序员开发的程序没有设置端口号,操作系统会在动态端口号这个范围内随机生成一个给开发的应用程序使用。当运行一个程序默认会有一个端口号,当这个程序退出时,所占用的这个端口号就会被释放
1.3 TCP介绍
**网络应用程序之间的通信流程:**之前我们学习了IP地址和端口号,通过IP地址能够找到对应的设备,然后再通过端口号找到对应的端
口,再通过端口把数据传输给应用程序,这里要注意,数据不能随便发送,在发送之前还需要选择一个对应的传输协议,保证程序之间按照指定的传输规则进行数据的通信,而这个传输协议就是我们今天学习的TCP。
**TCP的概念:**TCP的英文全拼(Transmission Control Protocol)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。面向连接的效果图:
**TCP通信步骤:**1.创建连接 2.传输数据 3.关闭连接
**说明:**TCP通信模型相当于生活中的’打电话‘,在通信开始之前,一定要先建立好连接,才能发送数据,通信结束要关闭连接。
TCP的特点:
1.面向连接通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接,以释放系统资源。
2.可靠传输:TCP采用发送应答机制、超时重传、错误校验、流量控制和阻塞管理
1.4 socket的介绍
**socket的概念:**socket (简称套接字)是进程之间通信-一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要基于这个socket。
**socket的作用:**负责进程之间的网络数据传输,好比数据的搬运工
1.5 TCP网络应用程序开发流程
**TCP网络应用程序开发流程的介绍:**TCP网络应用程序开发分为:TCP客户端程序开发、TCP服务端程序开发
*说明:*客户端程序是指运行在用户设备上的程序服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务
TCP客户端程序开发流程步骤说明:
1.创建客户端套接字对象socket()
2.和服务端套接字建立连接connect()
3.发送数据send()
4.接收数据recv()
5.关闭客户端套接字close()
TCP服务端程序开发流程步骤说明:
1.创建服务端端套接字对象socket()
2.绑定端口号bind()
3.设置监听listen()
4.等待接受客户端的连接请求accept()
5.接收数据recv()
6.发送数据send()
7.关闭套接字close()
1.6 TCP 客户端程序开发
socket类的介绍:
1.导入socket模块import socket
2.创建客户端socket对象socket.socket(AddressFamily, Type)
参数说明:
-
AddressFamily 表示IP地址类型,分为IPv4和IPv6
-
Type 表示传输协议类型
方法说明:
-
connect(host, port)) 表示和服务端套接字建立连接, host是服务器ip地址,port是应用程序的端口号
-
send(data) 表示发送数据,data是二进制数据
-
recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度
import socket
if __name__ == '__main__':
# 1.创建客户端套接字对象
#AF_INET:ipv4地址类型;SOCK_STREAM:tcp传输协议类型
tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.和服务端套接字建立连接
tcp_client_socket.connect(('192.168.43.142',9090))
# 3.发送数据
sent_content='你好,我是客户端小白'
#对字符串进程编码成为二进制数据
send_data=sent_content.encode('utf-8')
tcp_client_socket.send(send_data)
# 4.接收数据
#1024表示每次接受的最大字节数
recv_data=tcp_client_socket.recv(1024)
#受到的是二进制文件,对二进制文件解码
recv_content=recv_data.decode('utf-8')
print('接受服务端的数据为:',recv_content)
# 5.关闭客户端套接字
tcp_client_socket.close()
1.7 TCP服务端程序开发
socket类的介绍:
1.导入socket模块import socket
2.创建客户端socket对象socket.socket(AddressFamily, Type)
参数说明:
-
AddressFamily 表示IP地址类型,分为IPv4和IPv6
-
Type 表示传输协议类型
方法说明:
- bind(host, port)表示绑定端口号, host是ip地址,port是端口号,ip地址一般不指定,表示本机的任何一个ip地址都可以
- listen (backlog)表示设置监听,backlog参数表示最大等待建立连接的个数
- accept()表示等待接受客户端的连接请求
- send(data) 表示发送数据,data是二进制数据
- recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度
import socket
if __name__ == '__main__':
# 1.创建服务端端套接字对象
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 参数1:表示当前套接字
# 参数2:设置端口号复用选项
# 参数3:设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 2.绑定端口号
#第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
tcp_server_socket.bind(("",9090))
# 3.设置监听
#128表示最大等待连接的个数
tcp_server_socket.listen(128)
# 4.等待接受客户端的连接请求
#当客户端和服务端建立连接成功都会返回一个新的套接字
#tcp_ server socket只负责等待接收客户端的连接请求,收发消息不使用该套接字
new_client,ip_port=tcp_server_socket.accept()
print("客户端的ip和端口为:",ip_port)
# 5.接收数据
recv_data=new_client.recv(1024)
recv_content=recv_data.decode('utf-8')
print("接收客户端的数据为:",recv_content)
# 6.发送数据
send_content="收到,over!".encode('utf-8')
new_client.send(send_content)
# 7.关闭套接字
#关闭服务端套接字,表示和客户端终止通信
new_client.close()
#关闭服务端套接字,表示服务端以后不会再等待接收客户端连接请求
tcp_server_socket.close()
**说明:**当客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1-2分钟
解决办法有两种:
1.更换服务端端口号
2.设置端口号复用(推荐大家使用),也就是说让服务端程序退出后端口号立即释放
1.8 TCP网络应用程序的注意点
1.当TCP客户端程序想要和TCP服务端程序进行通信的时候必须要先建立连接
2.TCP客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。
3.TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序。
4.listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。
5.当TCP客户端程序和TCP服务端程序连接成功后,TCP服务器端程序会产生-个新的套接字,收发客户端消息使用该套接字。
6.关闭accept返回的套接字意味着和这个客户端已经通信完毕。
7.关闭listen后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信
8.当客户端的套接字调用close后,服务器端的recv会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的recv也会解阻塞,返回的数据长度也为0。
1.9 案例-多任务版TCP服务端程序
服务端服务多个客户端具体实现步骤
1.编写一个TCP服务端程序,循环等待接受客户端的连接请求
2.当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
3.把创建的子线程设置成为守护主线程, 防止主线程无法退出。
import socket
import threading
#处理客户端请求的任务
def handle_client_request(ip_port,new_client):
# 5.接收数据
recv_data = new_client.recv(1024)
recv_content = recv_data.decode('utf-8')
print("接收客户端的数据为:", recv_content)
while True:
if recv_data:
# 6.发送数据
send_content = "收到,over!".encode('utf-8')
new_client.send(send_content)
else:
print("客户端下线")
break
# 7.关闭套接字
# 关闭服务端套接字,表示和客户端终止通信
new_client.close()
if __name__ == '__main__':
# 1.创建服务端端套接字对象
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 参数1:表示当前套接字
# 参数2:设置端口号复用选项
# 参数3:设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 2.绑定端口号
#第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
tcp_server_socket.bind(("",9090))
# 3.设置监听
#128表示最大等待连接的个数
tcp_server_socket.listen(128)
while True:
# 4.等待接受客户端的连接请求
#当客户端和服务端建立连接成功都会返回一个新的套接字
#tcp_ server socket只负责等待接收客户端的连接请求,收发消息不使用该套接字
new_client,ip_port=tcp_server_socket.accept()
print("客户端的ip和端口为:",ip_port)
# 当客户端和服务端建立连接成功,创建子线程,让子线程专门负责接收客户端的消息
sub_thread=threading.Thread(target=handle_client_request,args=(ip_port,new_client))
sub_thread.setDaemon(True)
sub_thread.start()
#关闭服务端套接字,表示服务端以后不会再等待接收客户端连接请求
#因为服务端要一直运行所以关闭服务端套接字的操作可以省略
#tcp_server_socket.close()
1.10 socket之send和recv原理剖析
认识TCP socket的发送和接收缓冲区
当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间.
send原理剖析
send是不是直接把数据发给服务端?
不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的, 它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡
recv原理剖析
recv是不是直接从客户端接收数据?
不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间), 应用程序再从接收缓存区获取客户端发送的数据。
**小结:**不管是recv还是send都不是直接接收到对方的数据和发送数据到对方, 发送数据会写入到发送缓冲区,接收数据是从接收缓冲区来读取,发送数据和接收数据最终是由操作系统控制网卡来完成
第二章 HTTP协议和静态Web服务器
2.1 HTTP协议
HTTP协议的介绍
HTTP协议的全称是(HyperText Transfer Protocol),翻译过来就是超文本传输协议。超文本是超级文本的缩写,是指超越文本限制或者超链接,比如:图片、音乐、视频、超链接等等都属于超文本。
HTTP协议的制作者是蒂姆.伯纳斯-李,1991年设计出来的,HTTP协议设计之前目的是传输网页数据的,现在允许传输任意类型的数据。传输HTTP协议格式的数据是基于TCP传输协议的,发送数据之前需要先建立连接。
HTTP协议的作用
它规定了浏览器和Web服务器通信数据的格式,也就是说浏览器和web服务器通信需要使用http协议。
小结
HTTP协议是一个超文本传输协议
HTTP协议是一个基于TCP传输协议传输数据的
HTTP协议规定了浏览器和Web服务器通信数据的格式
2.2 URL
URL的概念
URL的英文全拼是(Uniform Resoure Locator),表达的意思是统一资源定位符, 通俗理解就是网络资源地址,也就是我们常说的网址。
URL的组成
URL的样子:
https://news.163.com/18/1122/10/E178J204000189FH.html
URL的组成部分
1.协议部分: https://、http://、ftp://
2.域名部分: news.163.com
3.资源路径部分: /18/1122/10/E178J204000189FH.html
域名
域名就是IP地址的别名,它是用点进行分割使用英文字母和数字组成的名字,使用域名目的就是方便的记住某台主机IP地址。
URL的扩展
htts://news.1 63.com/hello.html?page=1&count=10
查询参数部分: ?page=1&count=10
参数说明
?后面的page表示第一个参数,后面的参数都使用&进行连接
2.3 查看HTTP协议的通信过程
谷歌浏览器开发者工具的使用
首先需要安装Google Chrome浏览器,然后Windows和Linux平台按F12调出开发者工具, mac OS 选择视图->开发者->开发者工具或者直接使用alt+command+i这个快捷键,还有一个多平台通用的操作就是在网页右击选择检查。
开发者工具的标签选项说明
●元素(Elements) :用于查看或修改HTML标签
●控制台(Console) :执行js代码
●源代码(Sources) : 查看静态资源文件,断点调试js代码
●网络(Network) :查看http协议的通信过程
小结
谷歌浏览器的开发者工具是查看http协议的通信过程利器,通过Network标签选项可以查看每一次的请求和响应的通信过程, 调出开发者工具的通用方法是在网页右击选择检查。
开发者工具的Headers选项总共有三部分组成
- General: 主要信息
- Response Headers:响应头
- Request Headers:请求头
Response选项是查看响应体信息的
2.4 HTTP请求报文
HTTP请求报文介绍
HTTP最常见的请求报文有两种:
- GET方式的请求报文
- POST方式的请求报文
说明
●GET: 获取web服务器数据(比如获取新闻列表数据)
●POST: 向web服务器提交数据(比如登陆的时候把用户名和密码发送给服务器会使用)
GET请求报文如下
GET / HTTP/1.1\r\n =>请求方法(方式)请求的资源路径http协议的版本
Host: www.itcast.com\r\n =>服务器的主机ip地址和端口号,提示如果看不到端口号默认是80
Connection: keep-alive\r\n =>和服务端程序保存长连接,当客户端和服务端有一段时间没有进行通信,那么服务器程序会主动向客户端断开连接
Upgrade-Insecure-Requests: 1\r\n =>让客户端请求不安全请求,以后要使用https
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36\r\n =>用户代理,客户端程序名称
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n =>告诉服务端程序可以接受的数据类型
Accept-Encoding: gzip, deflate\r\n => 告诉服务端程序支持的压缩算法
Accept-Language: zh-CN,zh;q=0.9\r\n =>| 告诉服务端程序支持的语言
Cookie:...\r\n =>客户端用户身份的标识
-------空行-----
\r\n
------http get请求报文的格式-----
请求行\r\n
请求头\r\n
空行(\r\n)
提示:每项信息之间都需要一个\r\n, 是要http协议规定
------http post请求报文的格式-----
请求行\r\n
请求头\r\n
空行(\r\n)
请求体
提示:请求体就是浏览器发送给服务器的数据
2.5 HTTP响应报文
web服务器程序发送给浏览器的http协议的数据
HTTP/1.1 200 OK\r\n =>http协议版本状态码状态描述
Server: Tengine\r\n =>服务器名称
Content-Type: text/html\r\n =>服务器发送给浏览器的内容类型及编码格式
Transfer-Encoding: chunked\r\n =>服务器发送给客户端程序(浏览器)的数据不确定数据长度,数据发送结束的接收标识: 0\r\n,Content-Length: 200 (字节),服务器发送给客户端程序的数据确定长度。内容长度这两个选项只能二选一
Connection: keep-alive\r\n =>和客户端保持长连接
Date: Tue, 11 Aug 2020 12:49:06 GMT\r\n =>服务器的时间
----以下都是自定义响应头信息,字节定义响应头的名字和响应头的值,比如: is_login: True
Vary: Accept-Encoding\r\n
Content-Encoding: gzip\r\n
Ali-Swift-Global-Savetime: 1597150146\r\n
Via: cache21.l2cn1812[51,200-0,M], cache33.l2cn1812[53,0], kunlun1.cn1361[177,200-0,M], kunlun4.cn1361[178,0]\r\n
X-Cache: MISS TCP_MISS dirn:-2:-2\r\n
X-Swift-SaveTime: Tue, 11 Aug 2020 12:49:06 GMT\r\n
X-Swift-CacheTime: 0\r\n
Timing-Allow-Origin: *\r\n
EagleId: b706e79815971501463756558e\r\n
-------空行-----
\r\n
------响应体 就是真正意义上给浏览器解析使用的数据-----
提示:对于请求头和响应头信 息程序员都可以进行自定义,按照客户端和服务器约定好的方式来制定即可
-----http响应报文的格式--------
响应行\r\n
响应头\r\n
空行\r\n
响应体\r\n
HTTP状态码介绍
HTTP状态码是用于表示web服务器响应状态的3位数字代码。
状态码 | 说明 |
---|---|
200 | 请求成功 |
307 | 重定向 |
401 | 错误的请求,请求地址或者参数有误 |
404 | 请求资源在服务器不存在 |
500 | 服务器内部源代码出现错误 |
2.6 搭建Python自带静态web服务器
静态Web服务器是什么?
可以为发出请求的浏览器提供静态文档的程序。平时我们浏览百度新闻数据的时候,每天的新闻数据都会发生变化,那访问的这个页面就是动态的,而我们开发的是静态的,页面的数据不会发生变化。
如何搭建Python自带的静态Web服务器
搭建Python自带的静态Web服务器使用python3 -m http.server端口号
-m选项说明:
-m表示运行包里面的模块,执行这个命令的时候,需要进入你自己指定静态文件的目录,然后通过浏览器
就能访问对应的html文件了,这样一个静态的web服务器就搭建好了。
2.7 静态Web服务器-返回固定页面
开发自己的静态Web服务器
实现步骤:
1.编写一个TCP服务端程序
2.获取浏览器发送的http请求报文数据
3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。
4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字
import socket
if __name__ == '__main__':
#创建tcp服务端套接字
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#设置端口号复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
#绑定端口号
tcp_server_socket.bind(("",8000))
#设置监听
tcp_server_socket.listen(125)
#等待接受客户端的连接请求
while True:
#等待接受客户端的连接请求
#第一个参数是返回的套接字,第二个参数是客户端的ip地址和端口号
new_socker,ip_port=tcp_server_socket.accept()
#代码执行到此,说明连接建立成功
recv_data=new_socker.recv(4096)
print(recv_data)
#打开文件读取文件中的数据
with open('静态网页\index2.html','r',encoding='utf-8') as file:
file_data=file.read()
#把数据封装成http响应报文格式的数据
# 响应行
response_line='HTTP/1.1 200 OK\r\n'
# 响应头
response_header="Server:PWS/1.0\r\n"
# 响应体
response_body=file_data
response=response_line+response_header+'\r\n'+response_body
#把字符串编码成二进制
response_data=response.encode('utf-8')
#发送给浏览器的报文数据
new_socker.send(response_data)
#关闭服务于客户端的套接字
new_socker.close()
2.8 静态Wen服务器-返回指定页面
返回指定路径的页面,若未找到返回404页面
import socket
def main():
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", 8000))
# 设置监听
tcp_server_socket.listen(128)
# 等待接受客户端的连接请求
while True:
# 等待接受客户端的连接请求
# 第一个参数是返回的套接字,第二个参数是客户端的ip地址和端口号
new_socker, ip_port = tcp_server_socket.accept()
# 代码执行到此,说明连接建立成功
recv_data = new_socker.recv(4096)
print(recv_data)
if len(recv_data) == 0:
new_socker.close()
return
# 对二进制数据进行解码
recv_content = recv_data.decode('utf-8')
# 对数据进行分割
request_list = recv_content.split(" ", maxsplit=2)
request_path = request_list[1]
print(request_path,'-------------')
if request_path == '/':
request_path = '/index2.html'
try:
# 打开文件读取文件中的数据
# 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件
with open('静态网页' + request_path, 'rb') as file:
file_data = file.read()
except:
with open('静态网页/error.html' , 'rb') as file:
file_data = file.read()
# 响应行
response_line = 'HTTP/1.1 400 Not Found\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
else:
# 把数据封装成http响应报文格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
finally:
# 响应体
response_body = file_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 把字符串编码成二进制
response_data = response
# 发送给浏览器的报文数据
new_socker.send(response_data)
# 关闭服务于客户端的套接字
new_socker.close()
if __name__ == '__main__':
main()
2.9 静态Wen服务器-多任务版
import socket
import threading
def handle_client_request(new_socker):
recv_data = new_socker.recv(4096)
print(recv_data)
if len(recv_data) == 0:
new_socker.close()
return
# 对二进制数据进行解码
recv_content = recv_data.decode('utf-8')
# 对数据进行分割
request_list = recv_content.split(" ", maxsplit=2)
request_path = request_list[1]
print(request_path, '-------------')
if request_path == '/':
request_path = '/index2.html'
try:
# 打开文件读取文件中的数据
# 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件
with open('静态网页' + request_path, 'rb') as file:
file_data = file.read()
except:
with open('静态网页/error.html', 'rb') as file:
file_data = file.read()
# 响应行
response_line = 'HTTP/1.1 400 Not Found\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
else:
# 把数据封装成http响应报文格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
finally:
# 响应体
response_body = file_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 把字符串编码成二进制
response_data = response
# 发送给浏览器的报文数据
new_socker.send(response_data)
# 关闭服务于客户端的套接字
new_socker.close()
def main():
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", 8000))
# 设置监听
tcp_server_socket.listen(128)
# 等待接受客户端的连接请求
while True:
# 等待接受客户端的连接请求
# 第一个参数是返回的套接字,第二个参数是客户端的ip地址和端口号
new_socker, ip_port = tcp_server_socket.accept()
# 代码执行到此,说明连接建立成功
sub_thread=threading.Thread(target=handle_client_request,args=(new_socker,))
#设置成为守护主线程
sub_thread.setDaemon(True)
sub_thread.start()
if __name__ == '__main__':
main()
2.10静态Wen服务器-面向对象开发版
以面向对象的方式开发静态Web服务器
实现步骤:
1.把提供服务的Web服务器抽象成一个类(HTTPWebServer)
2.提供Web服务器的初始化方法,在初始化方法里面创建socket对象
3.提供一个开启Web服务器的方法,让Web服务器处理客户端请求操作。
import socket
import threading
class HttpWebServer(object):
def __init__(self):
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", 8000))
# 设置监听
tcp_server_socket.listen(128)
self.tcp_server_socket=tcp_server_socket
#处理客户端请求
@staticmethod
def handle_client_request(new_socker):
recv_data = new_socker.recv(4096)
print(recv_data)
if len(recv_data) == 0:
new_socker.close()
return
# 对二进制数据进行解码
recv_content = recv_data.decode('utf-8')
# 对数据进行分割
request_list = recv_content.split(" ", maxsplit=2)
request_path = request_list[1]
print(request_path, '-------------')
if request_path == '/':
request_path = '/index2.html'
try:
# 打开文件读取文件中的数据
# 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件
with open('静态网页' + request_path, 'rb') as file:
file_data = file.read()
except:
with open('静态网页/error.html', 'rb') as file:
file_data = file.read()
# 响应行
response_line = 'HTTP/1.1 400 Not Found\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
else:
# 把数据封装成http响应报文格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
finally:
# 响应体
response_body = file_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 把字符串编码成二进制
response_data = response
# 发送给浏览器的报文数据
new_socker.send(response_data)
# 关闭服务于客户端的套接字
new_socker.close()
#启动服务器的方法
def start(self):
# 等待接受客户端的连接请求
while True:
# 等待接受客户端的连接请求
# 第一个参数是返回的套接字,第二个参数是客户端的ip地址和端口号
new_socker, ip_port = self.tcp_server_socket.accept()
# 代码执行到此,说明连接建立成功
sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socker,))
# 设置成为守护主线程
sub_thread.setDaemon(True)
sub_thread.start()
def main():
#创建web服务器
web_server=HttpWebServer()
#启动服务器
web_server.start()
if __name__ == '__main__':
main()
2.11 静态Wen服务器-命令行启动动态绑定端口号
获取终端参数
import sys
#获取终端命令行
params=sys.argv
# 列表里面的每项数据都是字符串类型
print(params,type(params))
动态绑定端口号
import socket
import threading
import sys
class HttpWebServer(object):
def __init__(self,port):
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", port))
# 设置监听
tcp_server_socket.listen(128)
self.tcp_server_socket=tcp_server_socket
#处理客户端请求
@staticmethod
def handle_client_request(new_socker):
recv_data = new_socker.recv(4096)
print(recv_data)
if len(recv_data) == 0:
new_socker.close()
return
# 对二进制数据进行解码
recv_content = recv_data.decode('utf-8')
# 对数据进行分割
request_list = recv_content.split(" ", maxsplit=2)
request_path = request_list[1]
print(request_path, '-------------')
if request_path == '/':
request_path = '/index2.html'
try:
# 打开文件读取文件中的数据
# 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件
with open('静态网页' + request_path, 'rb') as file:
file_data = file.read()
except:
with open('静态网页/error.html', 'rb') as file:
file_data = file.read()
# 响应行
response_line = 'HTTP/1.1 400 Not Found\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
else:
# 把数据封装成http响应报文格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = "Server:PWS/1.0\r\n"
finally:
# 响应体
response_body = file_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 把字符串编码成二进制
response_data = response
# 发送给浏览器的报文数据
new_socker.send(response_data)
# 关闭服务于客户端的套接字
new_socker.close()
#启动服务器的方法
def start(self):
# 等待接受客户端的连接请求
while True:
# 等待接受客户端的连接请求
# 第一个参数是返回的套接字,第二个参数是客户端的ip地址和端口号
new_socker, ip_port = self.tcp_server_socket.accept()
# 代码执行到此,说明连接建立成功
sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socker,))
# 设置成为守护主线程
sub_thread.setDaemon(True)
sub_thread.start()
def main():
params=sys.argv
# print(params)
if len(params)!=2:
print("执行格式如下:python xxx.py 9000")
return
#判断第二个参数是否都是有数字组成的字符串
if not params[1].isdigit():
print("执行格式如下:python xxx.py 9000")
return
port=int(params[1])
#创建web服务器
web_server=HttpWebServer(port)
#启动服务器
web_server.start()
if __name__ == '__main__':
main()