当我使用 http1.1 协议时,为什么 style.css 文件没有出现在同一个 TCP 连接中?

Why the style.css file doesn't come in the same TCP connect when I use http1.1 protocol?

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

我认为 res_for_good 的 header 可能有问题。

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()

I think that the style.css file should be transfer through the same port number

首先,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 只是意味着客户端和服务器 支持 对另一个 HTTP 请求重用一个 TCP 连接。这并不意味着特定的现有 TCP 连接 必须 用于下一个请求,也不意味着任何现有的 TCP 连接 必须 被使用而不是创建一个新的。

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