python-----Web-------持续更新

URL

网址简称URL(统一资源定位符)
格式:协议://主机地址:端口号/路径?参数
主机地址:1.IP 地址 2.域名
路径:后台解析请求设置的路由关键字(哪个页面哪个文件夹)
参数:请求时附加的参数(要搜索的值、问题)

IP地址(互联网协议地址)

⽣活中我们说的IP地址,通常指的是 IPv4(IP协议的第4个版本),采⽤“点分⼗进制”表示(如:192.168.1.100),⼀共有2^32-1个
IPv6 采⽤“点分⼗六进制”表示,⽽IPv6中IP地址的⻓度为128,即有2^128-1个。
每⼀个IP地址包括两部分:⽹络号 和 主机号
在这里插入图片描述
IP地址127.0.0.1 ~ 127.255.255.255⽤于回路测试
特殊的IP地址:127.0.0.1 可以代表本机IP地址,⽤ http://127.0.0.1 就可以测试本机中配置的Web服务器
特殊的域名localhost 是本机域名,⽤来解析到 本机127.0.0.1 ip地址上

域名

域名,简称DN(全称:Domain Name),域名可以理解为是⼀个⽹址,就是⼀个特殊的名字。
在这里插入图片描述

Ports 端口

区分一个电脑中不同应用程序
分为以下两类:
知名端口(Well Known Ports),范围是0~1023,固定不变,用户不能使用
在这里插入图片描述
第二个就是动态端口(Dynamic Ports),不固定分配某种服务,而是动态分配
查看端口:
netstat -an
netstat -an | grep 21 #查询所有含有21的端⼝使⽤情况
lsof -i :21 #查看端⼝号被哪个程序占⽤,需要sudo权限
netstat -ntl (可以查看服务器socket)
-a (all)显示所有选项,netstat默认不显示LISTEN相关
-t (tcp) 仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化为数字
-l 仅列出在Listen(监听)的服务状态
-p 显示建立相关链接的程序名

网络传输方式

TCP (传输控制协议) UDP(用户数据报协议)
在这里插入图片描述

UDP

使用网络调试助手,mNetAssist,利用socket 进行发送和接收数据
socket(简称 套接字 ) ,是⽀持TCP/IP的⽹络通信的基本操作单元,可以看做是不同主机之间的 进程进⾏双向通信的端点,简单的说就是通信的两⽅的⼀种约定,⽤套接字中的相关函数来完成通信 过程。

# 导入模块
import socket

# 创建套接字 两个参数(协议类型,传输方式)
# 参数一:socket.AF_INET 使用IPv4  /socket.AF_INET6 使用IPv6
# 参数二:socket.SOCK_DGRAM  UDP     socket.SOCK_STREAM TCP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 数据传输 sendto 两个参数(要发送数据的二进制格式,对方IP和端口号)
# 参数一:字符串.encode() ,括号里面可以写字符集参数,默认为UTF-8(我写UTF-8, 中文就是乱码) 参数二:元组
udp_socket.sendto("How are you?".encode("GBK"), ("xxx.xxx.xxx.xxx", 8080))

# 接收数据(二进制) recvfrom(1024)从套接字中接收1024个字节的数据
# 此方法会造成程序的阻塞,等待另外一台计算机发来数据;
# 如果对方发数据了,recvfrom会自动解除阻塞
# 如果对方没有发送数据,会一直等待
recv_data = udp_socket.recvfrom(1024)
# 接收到的数据是一个元组,1.数据的二进制格式 2.发送方的ip和端口号
# 解码数据,得到字符串
recv_text = recv_data[0].decode("GBK")
# 输出显示接收到的内容
print("来自:", recv_data[1], "的消息:", recv_text)

# 关闭套接字
udp_socket.close()

上面代码每重新运行一次,在网络调试助手中,可以看到每次的端口号都在变化,⼀般情况下,在⼀台电脑上运⾏的⽹络程序有很多,为了不与其他的⽹络程序占⽤同⼀个端⼝号,往 往在编程中,udp的端⼝号⼀般不绑定 但是如果需要做成⼀个服务器端的程序的话,是需要绑定的

# 导入模块
import socket
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送端绑定端口 .bind(address),address是一个元组
# 元组的第一个元素是字符串类型的本机IP地址(也可省略),第二个是端口号
udp_socket.bind(("", 8888))
# 接收端绑定端口类似
# 发送数据
udp_socket.sendto("你是谁?  \n".encode('gbk'), ("xxx.xxx.xxx.xxx",8080))
# 关闭套接字
udp_socket.close()

在这里插入图片描述
广播地址(Broadcast Address)是专⻔⽤于同时向⽹络中所有⼯作站进⾏发送的⼀个地址。在使⽤ TCP/IP 协议的⽹络中,主机标识段host ID 为全1 的IP 地址为⼴播地址。
IP地址的⽹络字段和主机字段全为1就是地址:255.255.255.255。 所以,向 255.255.255.255 发送消息,就是发送⼴播消息。与上面不同的除了地址不一样,就是需要设置广播权限

# 导入模块
import socket
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 设置广播权限(套接字默认不允许发送广播,需要开启相关的权限)===========》
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
# 发送数据
udp_socket.sendto("hello".encode(), ("xxx.xxx.xxx.255",8080))
# 关闭套接字
udp_socket.close()

TCP(全双工模式)

面向连接、可靠的,基于字节流的传输层通信协议,与UDP相比 多了 创建连接connect,连接是一对一的,不适合广播的应用程序。每个TCP socket在内核中都有⼀个发送缓冲区和⼀个接收缓冲区,每个UDP socket都有⼀个接收缓冲区,没有发送缓冲区,从概念上来说就是只要有数据就发,不 管对⽅是否可以正确接收。因此TCP 有流量控制。
可靠传输:
1.应答机制
2.超时重传
发送端发出一股报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段
TCP 为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包按序接收。然后接收端实体对已成功收到的包发回一个相应的确认( ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传
3.错误校验
4.流量控制和阻塞管理
TCP和UDP的不同点:(是网络通信的两种传输方式)
TCP 面向连接,可靠,有序数据传输、重发丢失的数据包、舍弃重发的数据包、无差错的数据传输、阻塞和流量控制
严格区分客户端和服务端
在这里插入图片描述
还是使用网络调试助手,mNetAssist,作为TCP服务器调试,编写客户端程序

# 导入模块
import socket
# 创建套接字
tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 建立连接
tcp_client_socket.connect(("xxx.xxx.xxx.xxx", 8080))
# 发送数据
tcp_client_socket.send("hi ,what is your name".encode())
# 接收数据
recv_data = tcp_client_socket.recv(1024)
print("收到的数据", recv_data.decode("gbk"))
# 关闭套接字
tcp_client_socket.close()

使用网络调试助手,mNetAssist,作为TCP客户端调试,编写服务端程序,可以接收多条信息,连接多个客户端

# 导入模块
import socket
# 创建套接字
tcp_sever_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定ip和端口号
tcp_sever_socket.bind(("", 8081))
# 开启监听(设置套接字为被动模式)128是允许接收的最大的连接数
tcp_sever_socket.listen(128)
# 收发数据,收到的数据包括两部分,1.返回一个新的套接字对象2.客户端ip地址和端口号 元组
while True:
    new_client_socket, client_ip_port = tcp_sever_socket.accept()
    print("客户端 %s 来了"% str(client_ip_port))
    while True:
        recv_data = new_client_socket.recv(1024)
        if recv_data:
            recv_text = recv_data.decode("GBK")
            print("接收到[%s]的信息:%s"%(client_ip_port, recv_text))
            new_client_socket.send("好的,已经收到!~".encode('gbk'))
        else:
            print("客户端已经断开连接!")
            break
    new_client_socket.close()# 表示不能再和当前的客户端通信了
# 关闭连接
tcp_sever_socket.close() # 表示程序不再接受新的客户端连接,已经连接的可以继续服务

多客户端连接,必须等待上一个客户端断开连接,才可以连接并发送数据。

TCP三次握手

由客户端执行connect来触发,建立TCP连接
在这里插入图片描述
(1)第⼀次握⼿:Client将标志位SYN置为1,随机产⽣⼀个值seq=J,并将该数据包发送给Server, Client进⼊SYN_SENT状态,等待Server确认。
(2)第⼆次握⼿:Server收到数据包后由标志位SYN=1知道Client请求建⽴连接,Server将标志位 SYN和ACK都置为1,ack=J+1,随机产⽣⼀个值seq=K,并将该数据包发送给Client以确认连接请 求,Server进⼊SYN_RCVD状态。
(3)第三次握⼿:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK 置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确 则连接建⽴成功,Client和Server进⼊ESTABLISHED状态,完成三次握⼿,随后Client与Server之间 可以开始传输数据了

为什么需要三次握⼿,两次不可以吗?或者四次、五次可以吗
我们来分析⼀种特殊情况,假设客户端请求建⽴连接,发给服务器SYN包等待服务器确认, 服务器收到确认后,如果是两次握⼿,假设服务器给客户端在第⼆次握⼿时发送数据,数据从服 务器发出,服务器认为连接已经建⽴,但在发送数据的过程中数据丢失,客户端认为连接没有建 ⽴,会进⾏重传。假设每次发送的数据⼀直在丢失,客户端⼀直SYN,服务器就会产⽣多个⽆效 连接,占⽤资源,这个时候服务器可能会挂掉。这个现象就是我们听过的“SYN的洪⽔攻击”。 总结:第三次握⼿是为了防⽌:如果客户端迟迟没有收到服务器返回确认报⽂,这时会放弃 连接,重新启动⼀条连接请求,但问题是:服务器不知道客户端没有收到,所以他会收到两个连 接,浪费连接开销。如果每次都是这样,就会浪费多个连接开销。

TCP 四次挥手

用于断开连接
在这里插入图片描述
1.为什么TIME_WAIT状态需要经过2MSL(最⼤报⽂段⽣存时间)才能返回到CLOSE状态?

TCP的TIME_WAIT需要等待2MSL,当TCP的⼀端发起主动关闭,三次挥⼿完成后发送第四 次挥⼿的ACK包后就进⼊这个状态,等待2MSL时间主要⽬的是:防⽌最后⼀个ACK包对⽅没有 收到,那么对⽅在超时后将重发第三次握⼿的FIN包,主动关闭端接到重发的FIN包后可以再发⼀ 个ACK应答包。在TIME_WAIT状态时两端的端⼝不能使⽤,要等到2MSL时间结束才可以继续使 ⽤。当连接处于2MSL等待阶段时任何迟到的报⽂段都将被丢弃。

2. 为什么连接的时候是三次握⼿,关闭的时候却是四次握⼿?
因为当Server端收到Client端的SYN连接请求报⽂后,可以直接发送SYN+ACK报⽂。其中 ACK报⽂是⽤来应答的,SYN报⽂是⽤来同步的。但是关闭连接时,当Server端收到FIN报⽂ 时,很可能并不会⽴即关闭SOCKET,所以只能先回复⼀个ACK报⽂,告诉Client端,“你发的 FIN报⽂我收到了”。只有等到我Server端所有的报⽂都发送完了,我才能发送FIN报⽂,因此不 能⼀起发送。故需要四步握⼿。

文件下载器

在这里插入图片描述

=================================服务端代码===================================
# 导入模块
import socket
# 创建套接字
ftp_sever_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定连接
ftp_sever_socket.bind(("", 8080))
# 设置监听,设置套接字由主动为被动
ftp_sever_socket.listen(128)
# 接受客户端连接
while True:
    new_client_socket,ip_port = ftp_sever_socket.accept()
    print("欢迎新客户端:",ip_port)
    # 接收客户端发送的文件名
    recv_data = new_client_socket.recv(1024)
    file_name = recv_data.decode()
    print(file_name)
    # 根据文件名读取文件名
    try:
        with open(file_name, "rb") as file:
            while True:
                file_data = file.read(1024)
                # 把读取的内容发送给客户端(循环)
                if file_data:
                    new_client_socket.send(file_data)
                else:
                    break
    except Exception as e:
        print("文件 %s 下载失败!" % file_name)
    else:
        print("文件 %s 下载成功" % file_name)
    # 关闭和当前客户端的连接
    new_client_socket.close()
# 关闭服务器
ftp_sever_socket.close()
=================================客户端代码===================================
# 导入模块
import socket
# 创建套接字
ftp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 建立连接
ftp_client_socket.connect(("xxx.xxx.xxx.xxx",8080))
# 接受用户输入的文件名
file_name = input("请输入下载的文件名:")
# 发送文件名到服务器
ftp_client_socket.send(file_name.encode())
# 创建文件,并且准备保存
with open("D:/workspace/"+file_name, "wb") as file:
    while True:
        file_data = ftp_client_socket.recv(1024)
        if file_data:
            file.write(file_data)
        else:
            break
# 接收服务器端发送的数据,保存到本地
# 关闭套接字
ftp_client_socket.close()

浏览器访问服务器的过程

在这里插入图片描述

DNS服务器
域名解析系统,用来将域名转换成对应的IP地址
本地DNS
hosts是本地的DNS,DNS中就是IP地址和域名的对应关系表
hosts⽂件是隐藏⽂件、系统⽂件、没有扩展名的⽂件。
本地hosts文件优先级高于DNS服务器

HTTP协议(超文本传输协议)

默认端口是80,https是超文本传输协议含安全证书(端口号是443)
是⼀种应⽤层协议,主要用来发布和传输html页面(网页)
http协议分成两部分:Request 请求协议、Response 响应协议

HTTP协议报⽂格式查看 :windows和Linux平台按F12调出开发者⼯具,Network,刷新一下
元素(Elements):⽤于查看或修改HTML元素的属性、CSS属性、监听事件、断点等.
控制台(Console):控制台⼀般⽤于执⾏⼀次性代码, 查看JavaScript对象, 查看调试⽇志信息或异常 信息.
源代码(Sources):该⻚⾯⽤于查看⻚⾯的HTML⽂件源代码、JavaScript源代码、CSS源代码, 此外 最重要的是可以调试JavaScript源代码, 可以给JS代码添加断点等. ⽹
络(Network):⽹络⻚⾯主要⽤于查看 header 等与⽹络连接相关的信息.
注意: Network 中的每⼀项就是⼀次请求/响应过程, 点击每⼀项, 可查看本次请求响应的报⽂信息.

http请求协议包含四部分:

  1. 请求⾏(request-line) :请求方式 请求路径 协议版本
  2. 请求头(request-header):就是所有当前需要⽤到的协议项的集合,协议:协议名、协议值(内容)(会传Cookie)
  3. 空⾏ (分割请求头和请求的主体)
  4. 请求内容(request-content)也叫请求主体,只有post⽅式提交的时候,才有请求数据

http响应协议包含四部分:

  1. 响应行(状态行):协议版本号 状态码 状态描述
  2. 响应头
  3. 空⾏ (分割请求头和请求的主体)
  4. 响应主体(响应数据)

get和post方法区别
⼀对⼀、⼀对多、多对多

  • 明文显示参数(url中可以看到参数)
  • 速度快
  • 参数长度有限制(根据浏览器来定)
    场景:查询
    post:
  • 非明文显示
  • 速度慢
  • 参数没有限制
    场景:登录、注册

状态码:(状态码和状态描述是⼀⼀对应的!)
在这里插入图片描述
常见的状态码:
在这里插入图片描述
server、content-type、Set-Cookie 是要知道的
在这里插入图片描述
可以使用网络助手模拟TCP服务器,响应请求并且发送数据,地址可以改一下的:
在这里插入图片描述
长连接和短连接:http1.0 http1.1 http2.0
⻓连接可以省去较多的TCP建⽴和关闭的操作,节约时间。但是如果⽤户量太⼤容易造成服务器 负载过⾼最终导致服务不可⽤
短连接对于服务器来说实现起来较为简单,存在的连接都是有⽤的连接,不需要额外的控制⼿段。但是如果⽤户访问量很⼤, 往往可能在很短时间内需要创建⼤量的连接,造成服务器响应速度 过慢

HTTP 是一种无状态的连接,客户端每次读取 web页面时,服务器都会认为这是一次新的会话。但有时候我们又需要持久保持某些信息,比如登录时的用户名、密码,用户上一次连接时的信息等。这些信息就由 Cookie 和 Session 保存。

Cookie :
在这里插入图片描述
cookie是由服务器生成,存储在浏览器端的一小段文本信息
cookie特点:
1.以键值对方式进行存储
2.通过浏览器访问一个网站时,会将浏览器存储的跟网站相关的cookie信息发送给改网站的服务器,request.COOKIES
3.cookie 是基于域名安全的
4.cookie是有过期时间的,如果不指定,默认关闭浏览器之后cookie就会过期

Seesion:
session存储在服务器端
特点:1.session是以键值对进行存储的 2.session依赖于cookie,唯一的标识码保存在session cookie中 3.session也是由过期时间的,如果不指定,默认两周就会过期
应用场景:
cookie:记住用户名。安全性要求不高
session:涉及到安全性要求比较高的数据,银行卡账户和密码

Wireshark抓包工具使用

软件功能:分析网络底层协议;解决网络故障问题;找寻网络安全问题
在这里插入图片描述

模拟客户端浏览器 请求web服务器的网页过程

# 导入模块
import socket
# 创建套接字
tcp_socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 建立连接,http端口默认80(DNS解析)
tcp_socket_client.connect(("www.icoderi.com", 80))
# 拼接请求协议 get 没有请求体
# 请求行
request_line = "GET / HTTP/1.1\r\n"
# 请求头
request_header = "Host:www.icoderi.com\r\n"
# 请求空行
request_blank = "\r\n"
# 整体拼接
request_data = request_line + request_header + request_blank
# 发送请求协议
tcp_socket_client.send(request_data.encode())
# 接收服务器响应内容并解码
recv_data = tcp_socket_client.recv(4096)
recv_text = recv_data.decode()
# 查找\r\n\r\n的位置
loc = recv_text.find("\r\n\r\n")
# 截取字符串
html_data = recv_text[loc+4:]
print(html_data)
# 保存内容
with open("index.html", "w") as file:
    file.write(html_data.encode())
# 关闭连接
tcp_socket_client.close()

实现简单的web服务器并返回 固定数据 (页面)给浏览器

'''
实现简单的web服务器并返回固定数据(页面)给浏览器
'''
# 导入模块
import socket

def request_handler(new_client_socket, ip_port):
    '''接收信息,并且做出响应'''
    # 接收客户端浏览器发送的协议
    request_data = new_client_socket.recv(1024)
    # 判断协议是否为空
    if not request_data:
        print("%s 客户端已经下线!" % str(ip_port))
        new_client_socket.close()
        return
    # 拼接响应报文
    response_line = "HTTP/1.1 200 OK\r\n"
    response_header = "Server:Python20WS/2.1\r\n"
    response_blank = "\r\n"
    ==============返回固定数据==============
    response_body = "HelloWorld"
    response_data = response_line + response_header + response_blank + response_body
    # 发送响应报文
    new_client_socket.send(response_data.encode())
    =======================================
     ==============返回固定页面(with open 读取文件内容)==============
     with open("index.html","rb") as file:
     	response_body = file.read()
    response_data = (response_line + response_header + response_blank).encode() + response_body
    # 发送响应报文
    new_client_socket.send(response_data)
    ===================================================================
    # 关闭套接字
    new_client_socket.close()

def main():
    # 创建套接字
    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(("", 8080))
    # 设置监听,让套接字由主动变为被动
    tcp_server_socket.listen(128)
    while True:
        # 接收客户端连接
        new_client_socket, ip_port = tcp_server_socket.accept()
        request_handler(new_client_socket, ip_port)
    # 关闭套接字
    tcp_server_socket.close()


if __name__ == '__main__':
    main()

直接在浏览器输入:127.0.0.1:8080 测试结果

实现简单的web服务器并返回 指定页面 给浏览器

'''
实现简单的web服务器并返回 指定页面  给浏览器
'''
# 导入模块
import socket

def request_handler(new_client_socket, ip_port):
    '''接收信息,并且做出响应'''
    # 接收客户端浏览器发送的协议
    request_data = new_client_socket.recv(1024)
    # 判断协议是否为空
    if not request_data:
        print("%s 客户端已经下线!" % str(ip_port))
        new_client_socket.close()
        return

    # 把请求协议解码,得到请求报文的字符串
    request_text = request_data.decode()
    # 得到请求行 1.查找第一个\r\n出现的位置,得到 Get 路径 http/1.1
    loc = request_text.find("\r\n")
    request_line = request_text[:loc] # GET /index.html HTTP/1.1

    # 2.把请求行按照空格拆分
    request_line_list = request_line.split(" ") # ['GET', '/index.html', 'HTTP/1.1']

    # 得到资源路径
    file_path = request_line_list[1]
    print("[%s]正在请求:%s" % (str(ip_port), file_path))# 不仅会请求html 还有相应的图片,js

    if file_path == "/":
        file_path = "/index.html"

    # 拼接响应报文
    response_line = "HTTP/1.1 200 OK\r\n"
    response_header = "Server:Python20WS/2.1\r\n"
    response_blank = "\r\n"
    # 当服务器没有指定请求的资源,使用try except 返回404
    try:
        # 返回固定页面 通过with open 读取文件
        with open("static" + file_path, "rb") as file:
            # 把读取的文件内容返回给客户端(二进制)
            response_body = file.read()
    except Exception as e:
        # 重新修改响应行 为404
        response_line = "HTTP/1.1 404 Not found\r\n"
        # 响应内容为错误,并转化为字节码
        response_body = "Error! (%s)" % str(e)
        response_body = response_body.encode()

    response_data = (response_line + response_header + response_blank).encode() + response_body
    # 发送响应报文
    new_client_socket.send(response_data)
    # 关闭套接字
    new_client_socket.close()

def main():
    # 创建套接字
    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(("", 8080))
    # 设置监听,让套接字由主动变为被动
    tcp_server_socket.listen(128)
    while True:
        # 接收客户端连接
        new_client_socket, ip_port = tcp_server_socket.accept()
        request_handler(new_client_socket, ip_port)
    # 关闭套接字
    tcp_server_socket.close()


if __name__ == '__main__':
    main()

直接在浏览器输入:127.0.0.1:8080/index.html 或 127.0.0.1:8080/index2.html 或 127.0.0.1:8080/ 测试结果
上面的代码用 面向对象封装如下:

'''使用面向对象封装 实现简单的web服务器返回指定页面'''
# 导入模块
import socket
class WebServer(object):
    def __init__(self):
        # 创建套接字
        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(("", 8080))
        # 设置监听,让套接字由主动变为被动
        tcp_server_socket.listen(128)
        # 定义实例属性,保存套接字对象
        self.tcp_server_socket = tcp_server_socket

    def start(self):
        # 接收客户端请求
        while True:
            new_client_socket, ip_port = self.tcp_server_socket.accept()
            self.request_handler(new_client_socket, ip_port)
        # 关闭套接字
        self.tcp_server_socket.close()

    def request_handler(self, new_client_socket, ip_port):
        # 接收客户端的信息,并作出响应
        request_data = new_client_socket.recv(1024)
        # 判断协议是否为空
        if not request_data:
            print("%s 客户端已经下线!" % str(ip_port))
            new_client_socket.close()
            return
        # 解析请求协议,得到请求报文字符串
        request_text = request_data.decode()
        # 得到请求行
        loc = request_text.find("\r\n")
        request_line = request_text[:loc]
        # 得到请求的资源路径
        request_line_list = request_line.split(" ")
        file_path = request_line_list[1]
        print("[%s]正在请求:%s" % (str(ip_port),file_path))
        if file_path == "/":
            file_path = "/index.html"

        # 服务器响应(拼接响应报文)
        response_line = "HTTP/1.1 200 OK\r\n "
        response_header = "Server:Python20WS/2.1\r\n"
        response_header = "Content-Type: text/html;charset=utf-8\r\n"# 让网页使用html方式渲染
        response_blank = "\r\n"
        try:
            with open("static"+ file_path, "rb") as file:
                response_body = file.read()
        except Exception as e:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_body = "Error! (%s)" % str(e)
            response_body = response_body.encode()
        response_data = (response_line + response_header + response_blank).encode() + response_body
        # 发送响应报文
        new_client_socket.send(response_data)
        # 关闭套接字
        new_client_socket.close()
def main():
    #主函数
    #  创建WebServer类的对象
    ws = WebServer()
    ws.start()

if __name__ == '__main__':
    main()

接下来可以 分模块 不断优化以上代码
1.新建一个新的文件夹applicaiton,并新建一个app.py文件,在里面建立函数def application(),函数的内容就是将def request_handler()中的接收 到request_data 返回响应data 移过去
2.也可以在app.py 中将解析请求报文返回资源路径的那一部分 单独建立一个函数方法
3. 在文件夹appliction中新建一个utils.py文件,功能为 拼接响应报文

使用 sys.argv 获取系统传递到程序的参数
使用 isdigit() 方法检测字符串是否只由数字组成。

dict = {"name": "wenmei", "age": 18, "sex":"女"}
print(dict.keys())
# 输出的结果是:dict_keys(['name', 'age', 'sex']),不是列表
# 使用list转为列表
stu_list = list(dict.keys())
# 也可以用tuple转为元组,方便后续遍历
stu_tuple = tuple(dict.keys())
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值