1.http协议的简介
HTTP,HyperText Transfer Protocol。超文本传输协议,是互联网上应用最为广泛的一种网络协议。基于TCP的协议,HTTP是一个客户端和服务器端请求和应答的标准
2.TCP三次握手过程
SYN是请求同步的意思,synchronize(同步)的缩写
ACK是确认同步的意思,acknowledgement(确认)的缩写
TCP是主机对主机层的传输控制协议,提供可靠的连接服务
TCP的三次握手
第一次握手:*(客户端:服务器在吗?)
建立连接时,客户端A发生SYN包(SYN=j)到服务器B
并进入SYN_SEND状态,等待服务器B确认
第二次握手:*(服务器:收到了,在的,)
服务器B收到SYN包,必须确认客户A的SYN,ACK=j+1
同时自己也发送一个SYN包,SYN=k
即,SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:*(客户端:我要发数据了)
客户端A收到服务器B的SYN+ACK包
向服务器B发送确认包ACK(ACK=k+1)
此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手
3.TCP四次挥手过程
客户端A发送一个FIN.用来关闭客户A到服务器B的数据传送(报文段4)(客户端:我要关了)
服务器B收到这个FIN. 它发回一个ACK,确认序号为收到的序号+1(报文段5)。和SYN一样,一个FIN将占用一个序号(服务端:好的,收到)
服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)(服务端:我也要关了)
客户端A发回ACK报文确认,并将确认序号设置为序号加1(报文段7)(客户端:好的,收到)
4.服务器如何复用同一个端口而不会提示端口被占用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
允许地址重用
5.str.splitlines()按照行(’\r’, ‘\n’, ‘\r\n’)分隔,返回一个包含各行作为元素的列表
补充:
在response_header中加入
Content-Type: text/html; charset=utf-8 # 让浏览器按照utf-8格式解码
Content-Length:len(response_body) # 在单进程.单线程非堵塞长链接中使用,表示发送 body的长度,告诉浏览器数据已发送完毕,此时不用再关闭套接字
套接字变为非堵塞:
tcp_service_socket.setblocking(False)
单线程-单线程-非堵塞长链接代码:
import socket, re
def service_client(new_cs, request):
"""为浏览器发送数据"""
request_lists = request.splitlines()
if request_lists:
ret = re.match(r"[^/]+(/[^ ]*)", request_lists[0])
file_name = ""
if ret:
file_name = ret.group(1)
if file_name == "/":
file_name = "/index.html"
try:
f = open("./html" + file_name, "rb")
print("正在准备为浏览器发送请求数据。。。")
except:
response_body = "-------请求没有被找到------"
response_heard = "HTTP/1.1 404 NOT FOUND\r\n"
response_heard += "Content-Type: text/html; charset=utf-8\r\n"
response_heard += "Content-length:%d\r\n" % len(response_body)
response_heard += "\r\n"
response = response_body + response_heard
new_cs.send(response.encode("utf-8"))
else:
response_body = f.read()
f.close()
response_heard = "HTTP/1.1 200 OK\r\n"
response_heard += "Content-Type: text/html; charset=utf-8\r\n"
response_heard += "Content-Length:%d\r\n" % len(response_body)
response_heard += "\r\n"
response = response_heard.encode("utf-8")+ response_body
new_cs.send(response)
print("浏览器的请求数据已发送完毕!")
def main():
# 1.创建套接字
tcp_ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2.绑定
tcp_ss.bind(("", 9900))
# 3.监听
tcp_ss.listen(128)
tcp_ss.setblocking(False) # # 将监听套接字变为非堵塞
client_socket_list = list() # 创建一个存放浏览器服务的套接字列表
while True:
# 4.等待浏览器链接
try:
new_cs, c_addr = tcp_ss.accept() # 等待浏览器的链接
except Exception as ret: # 因监听套接字为非堵塞套接字,当没有新浏览器连接时就会报错,处理报错信息
pass
else:
new_cs.setblocking(False) # 将为浏览器服务的套接字设置为非堵塞
client_socket_list.append(new_cs) # 将为浏览器服务的套接字加入到列表
for client_socket in client_socket_list: # 遍历为浏览器服务的套接字列表
try:
recv_data = client_socket.recv(1024).decode("utf-8") # 接收浏览器发送的请求信息
except:
pass
else:
if recv_data: # 接收到浏览器请求,则调用为浏览器发送请求的数据的函数
service_client(client_socket, recv_data)
else: # 表示浏览器已关闭
print("浏览器已退出")
client_socket.close() # 关闭为浏览器服务的套接字
client_socket_list.remove(client_socket) # 从列表中删除该套接字
tcp_ss.close()
if __name__ == '__main__':
main()