为什么服务器和客户端不同步?(python套接字)

2024-04-20 02:21:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正在编写一个小型的客户机-服务器应用程序,用于通过套接字将任意文件从服务器传输到客户机。在

服务器一次只处理一个客户端,但是当一个客户端被服务时,它应该准备好处理一个新的客户端连接。在

客户端将请求一个文件,如果该文件存在,客户端将接收该文件,将其写入磁盘并关闭连接。在

服务器代码:

PORT = 9000
BUFSIZE = 1000

def main(argv):
    print('The server is ready to receive')
    server_socket = socket(AF_INET, SOCK_STREAM)
    server_socket.bind(('', PORT))
    server_socket.listen(1)
    while True:
        connection_socket, addr = server_socket.accept()

        try:
            requested_filepath = connection_socket.recv(BUFSIZE).decode()
            print("Client requested the file: " + requested_filepath)
            capital_sentence = requested_filepath.upper()
            if(os.path.isfile(requested_filepath)):
                filesize = str(os.path.getsize(requested_filepath))
                connection_socket.send(filesize.encode())
                with open(requested_filepath, 'rb') as f:
                    while(True):
                        content = f.read(BUFSIZE)
                        if not content:
                            break
                        connection_socket.send(content)
                print('File has been send')
            else:
                error = "error"
                connection_socket.send(error.encode())
        finally: 
            connection_socket.close()

客户代码:

^{pr2}$

我可以运行服务器并让客户机请求并获取一个文件,但是当我第二次或第三次运行客户端时,服务器和客户端似乎不同步。两个程序都会中断并返回以下错误消息:

客户端错误:

Traceback (most recent call last):
  File "client.py", line 37, in <module>
    main(sys.argv[1:])
  File "client.py", line 16, in main
    response = client_socket.recv(BUFSIZE).decode()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 6: invalid start byte

服务器错误:

The server is ready to receive
Client requested the file: /pepe.jpeg
File has been send
Client requested the file: /pepe.jpeg
File has been send
Client requested the file: /pepe.jpeg
Traceback (most recent call last):
  File "server.py", line 44, in <module>
    main(sys.argv[1:])
  File "server.py", line 30, in main
    connection_socket.send(content)
ConnectionResetError: [Errno 104] Connection reset by peer

我没有以正确的方式关闭插座连接吗?在


Tags: 文件thein服务器clientsend客户端server
1条回答
网友
1楼 · 发布于 2024-04-20 02:21:37

您陷入了一个最常见的TCP套接字编程陷阱。您假设您的套接字将发送消息,而它只发送和接收数据,并且对消息结构完全不可知。即使您使用多个send调用发送数据,recv调用也不会接收到这个确切的结构,但不管发生什么,都会在缓冲区中。如果你发送一千次一个字节,你的recv(1000)将收到一千个字节,这就是这里的情况。在

您的问题是由于您的服务器比您的客户端快一点。我不得不调整您的代码,以便能够可靠地再现代码,但这确实做到了:

client_socket.send(filepath.encode())
sleep(1)
response = client_socket.recv(BUFSIZE).decode()

这就模拟了服务器比客户机快,最终还是会发生这种情况。通过添加sleep,我们可以使它每次都发生。在

在TCP套接字上调用recv时,可能会发生以下五种情况之一:

  1. 没有数据和呼叫阻塞
  2. 你收到了数据,你收到的数据就是一条“信息”,不管你的上下文是什么
  3. 在您从套接字读取之前,服务器已发送了多条消息,而您一次就收到了所有消息
  4. 你的客户太急于阅读,当你的第一条信息只有一部分可用时,它决定阅读
  5. 3和4的组合:您将收到多条完整消息外加一条部分消息

您的代码所发生的情况是,您的服务器已成功地发送了编码的文件大小以及您的一些数据。在您的客户机上,现在假设您的第一个recv只接收文件大小,但这无法保证。可能已经有一些文件数据(正如您将要阅读的BUFSIZE-那里几乎有一个完整的数据缓冲区),当您试图将其解码为一个整数时,会发生一些奇怪的事情,因为数据不是您期望的那样。在

处理TCP套接字的唯一可靠方法是从套接字读取数据,附加到临时处理缓冲区,然后解析该缓冲区并查看其中的内容。如果有“消息”,处理它并从缓冲区中删除它。任何保留在缓冲区中的内容都必须保留在那里,并且您的下一个recv结果将附加到该缓冲区中。在

快速修复此问题的最简单方法是如果您的服务器发出固定长度的初始消息。然后,您可以安全地从套接字中准确地读取这个数量的字符,并将其作为大小/错误消息进行处理,剩下的将是数据。在很多方面,这是一个可怕的解决办法,你应该瞄准更好的东西。“正确”的方法是设计一个协议,服务器在协议中放置分隔符,这样客户机就可以检测到哪些消息意味着什么。你的协议可以是

^{pr2}$

甚至简单到假设换行符前面的所有内容都是文件大小,后面的所有内容都是数据。在

但是,即使添加了sleep(1),它也能更好地工作,因为它现在可以将初始消息填充到100个字节。由于(4),这仍然可能出错,所以实际上你需要检查你最初是否收到了100个字符,并且一直读到现在为止,但是我将把这个留给你去实现。在

        if(os.path.isfile(requested_filepath)):
            filesize = str(os.path.getsize(requested_filepath))
            connection_socket.send(("%s" % filesize).encode().ljust(100))
            with open(requested_filepath, 'rb') as f:
                while(True):
                    content = f.read(BUFSIZE)
                    if not content:
                        break
                    connection_socket.send(content)
            print('File has been send')
        else:
            error = "error"
            connection_socket.send(error.encode().ljust(100))

客户:

^{4}$

PS您的服务器应该捕捉到“连接重置由对等”错误。如果出现网络问题或客户端应用程序崩溃,就会发生这种情况。服务器可以安全地忽略此错误并停止发送到特定的客户端套接字。在

相关问题 更多 >