当我使用 http1.1 协议时,为啥 style.css 文件不在同一个 TCP 连接中?

Posted

技术标签:

【中文标题】当我使用 http1.1 协议时,为啥 style.css 文件不在同一个 TCP 连接中?【英文标题】:Why the style.css file doesn't come in the same TCP connect when I use http1.1 protocol?当我使用 http1.1 协议时,为什么 style.css 文件不在同一个 TCP 连接中? 【发布时间】:2022-01-12 07:08:49 【问题描述】:

我正在使用 python 套接字来编写一个持久的 http 服务器。我认为 style.css 文件应该通过相同的端口号传输,但似乎我没有得到结果。包 48 和 49 表明 style.css 与包 37 相比通过不同的端口传输。

我认为 res_for_good 的标头可能有问题。

import socket
from datetime import datetime
import threading

res_for_good = '''HTTP/1.1 200 OK\r
Date: Sun, 18 Oct 2012 10:36:20 GMT\r
Accept-Ranges: bytes\r
Content-Type: text/html; charset=iso-8859-1\r
Connection: keep-alive\r
Keep-Alive: timeout=3 max=5\r
Content-Length: 112\r
\r
<head>
<link rel="stylesheet" href="./style.css" type="text/css">
</head>
<html>
<body>good HaHa</body>
</html>
'''

res_for_notfound='''HTTP/1.1 404 Not Found\r
Date: Sun, 18 Oct 2012 10:36:20 GMT\r
Accept-Ranges: bytes\r
Content-Type: text/html; charset=iso-8859-1\r
Connection: keep-alive\r
Keep-Alive: timeout=3 max=5\r
Content-Length: 116\r
\r
<head>
<link rel="stylesheet" href="./style.css" type="text/css">
</head>
<html>
<body>404 Not Found</body>
</html>
'''

res_for_style='''HTTP/1.1 200 OK\r
Date: Sun, 18 Oct 2012 10:36:20 GMT\r
Accept-Ranges: bytes\r
Content-Type: text/css; charset=iso-8859-1\r
Keep-Alive: timeout=3 max=5\r
Connection: keep-alive\r
Content-Length: 46\r
\r
body
    color: red;
    font-size: 100px;

'''

def serveClient(clientsocket, address):
    start = datetime.now()
    objcount=0
    # we need a loop to continuously receive messages from the client
    while True:
        objcount+=1
        
        # then receive at most 1024 bytes message and store these bytes in a variable named 'data'
        # you can set the buffer size to any value you like
        data = clientsocket.recv(1024)
        data_utf8=data.decode('utf-8').split('\r\n')
        #data_json = json.loads(data_utf8)
        print(address)
        print(data)
        
        
        
        # if the received data is not empty, then we send something back by using send() function
        if '/good.html' in data_utf8[0]:
            clientsocket.sendall(res_for_good.encode())
        

        if '/style.css' in data_utf8[0]:
            print("transfer css")
            #res="Content-Type: text/css\n\n"+css_file.read()
            res=res_for_style
            clientsocket.sendall(res_for_style.encode())
        
        if '/redirect.html' in data_utf8[0]:
            clientsocket.sendall(res_for_redirect.encode())
        elif data:
            clientsocket.sendall(res_for_notfound.encode())    
        
        if data == b'':
            objcount-=1
        
        print("object count: "+str(objcount))
        now = datetime.now()
        # we need some condition to terminate the socket
        # lets see if the client sends some termination message to the server
        # if so, then the server close the socket 
        if objcount == max_rec_Object or (now-start).total_seconds()>waiting_time:
            print(start)
            print(now)
            print('close socket')
            clientsocket.close()
            break

while True:
    # accept a new client and get it's informati        
    # print(socket.gethostbyaddr(s.getpeername))

    (clientsocket, address) = s.accept()
    # create a new thread to serve this new client
    # after the thread is created, it will start to execute 'target' function with arguments 'args' 
    threading.Thread(target = serveClient, args = (clientsocket, address)).start()

【问题讨论】:

【参考方案1】:

我认为style.css文件应该通过同一个端口号传输

首先,HTTP/1.1 中没有这样的要求。然后,您的代码中存在错误。

首先是错误:从数据包捕获中可以看出,您的服务器使用 3 个 HTTP 响应来响应对 /good.html 的单个请求:一个是预期的 200,另外两个是意外的 404。第一个错误的 404 是由于这个原因代码:

    if '/good.html' in data_utf8[0]:
        clientsocket.sendall(res_for_good.encode())
    ...
    if '/redirect.html' in data_utf8[0]:
        clientsocket.sendall(res_for_redirect.encode())
    elif data:
        clientsocket.sendall(res_for_notfound.encode())   

由于它在处理/good.html 后并没有停止,它最终会在最后显示的行中结束并发送res_for_notfound

第二个错误的404是由于这段代码:

当真时: ... 数据 = clientsocket.recv(1024) ... elif 数据: clientsocket.sendall(res_for_notfound.encode())

这里盲目假设请求不会超过 1024 并且会在单个 recv 中读取。两种假设都是错误的。从抓包可以看出,请求其实是1098字节(看ACK=1099)。即使它小于 1024,也不能保证它会在单个 recv 中被读取,这不是 TCP 的工作原理。

由于两个无关的 404 响应,客户端正确地假定服务器无法发送正确的响应,因此关闭连接以恢复正常状态。


也就是说,即使响应正确,也不能保证第二个请求是通过同一个 TCP 连接进入的。 显式或隐式宣布支持 HTTP keep alive 只是意味着客户端和服务器 支持 将 TCP 连接重用于另一个 HTTP 请求。这并不意味着特定的现有 TCP 连接必须用于下一个请求,也不意味着任何现有的 TCP 连接必须完全使用而不是创建一个新连接。

从抓包可以看出,浏览器最初打开了两个到服务器的TCP连接。这并不少见,因为通常一个站点包含许多应尽快检索的资源。只有 HTTP/1.1 只能通过单个 TCP 连接顺序而不是并行检索资源。因此,准备好另一个备用 TCP 连接是个好主意。然后,在您的情况下,这个其他已经存在的连接将用于新资源。

【讨论】:

但是为什么tcp连接在43号就关闭了。 @Ying:查看更新的回复。您的代码中不仅有错误的期望,还有实际的错误。

以上是关于当我使用 http1.1 协议时,为啥 style.css 文件不在同一个 TCP 连接中?的主要内容,如果未能解决你的问题,请参考以下文章

HTTP 02 HTTP1.1 协议

为啥 QLineEdit Style 在聚焦时不改变?

面试必备-HTTP协议-3

HTTP1.1协议详解

http协议以及http1.0和http1.1的区别

http协议