【问题标题】:Traceback with Sockets & Multithreading使用套接字和多线程进行回溯
【发布时间】:2020-01-25 02:39:05
【问题描述】:

我正在创建两个简单的服务器-客户端 python 脚本来回显命令。也就是说,服务器打开一个套接字,然后客户端连接,发送一个简单的消息,然后服务器将消息回显给客户端,然后打印它。我正在尝试使这个多线程,这就是问题出现的地方。

我正在关注this Real Python 教程,并且能够成功运行echo-client.pyecho-server.py。我了解基本概念并且可以很好地遵循代码。所以我向服务器程序添加了代码,使其成为多线程:

服务器多线程.py

#!/usr/bin/env python3

import socket
from threading import Thread

HOST = "127.0.0.1"
PORT = 65432
BUFFER_SIZE = 16


def handler(conn, addr):
    while True:
        data = conn.recv(BUFFER_SIZE)
        print(f"Server received {len(data)} bytes from {addr[0]}")
        if not data:
            break
        conn.sendall(data)


with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print(f"Connection from {addr[0]}")
        Thread(target=handler, args=(conn, addr)).start()

这是客户端程序。

client.py

#!/usr/bin/env python3

import socket

HOST = "127.0.0.1"
PORT = 65432
BUFFER_SIZE = 16

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b"Hello, world!")
    data = s.recv(BUFFER_SIZE)

print(f"Received {data}")

当我运行多线程服务器时,我收到以下回溯:

Connection from 127.0.0.1
Server received 13 bytes from 127.0.0.1
Exception in thread Thread-1:
Traceback (most recent call last):
  File "D:\Program Files\Python\Python37\lib\threading.py", line 917, in _bootstrap_inner
    self.run()
  File "D:\Program Files\Python\Python37\lib\threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "server-multithreaded-new.py", line 17, in handler
    conn.sendall(data)
OSError: [WinError 10038] An operation was attempted on something that is not a socket

此外,客户端没有得到完整的“Hello, World!”消息返回(如在非多线程版本中)。相反,它只是得到一个空字节字符串:

Received b''

我的猜测是,在数据仍在传输时,套接字似乎正在关闭,但我不知道为什么。毕竟,我的理解是with 上下文管理器应该智能地自动处理关闭。

有谁知道为什么会这样,我该如何解决?

【问题讨论】:

    标签: python-3.x multithreading sockets networking


    【解决方案1】:

    毕竟,我的理解是 with 上下文管理器应该 智能且自动地处理关闭。

    嗯,是的,也不是。当执行离开with 前缀的范围时,上下文管理器将关闭套接字,也就是说,几乎在你的线程启动后立即关闭。同时,您的线程仍在运行(与主线程并行),并且在它启动后几毫秒(或者甚至可能在它启动之前,取决于操作系统如何安排新线程),它发现主线程已从其下方关闭其套接字。

    特别是,主线程中的上下文管理器不够聪明,无法意识到您已将套接字的引用传递给忙于使用它的单独线程;它只是在退出范围时总是关闭套接字,句号。

    解决这个问题的简单方法是让您的网络线程负责关闭套接字而不是主线程;例如,您可以将上下文管理器代码/with-block 放置在网络线程中而不是主线程中,如下所示:

    # network I/O thread
    def handler(conn, addr):
        with conn:
            while True:
                data = conn.recv(BUFFER_SIZE)
                print(f"Server received {len(data)} bytes from {addr[0]}")
                if not data:
                   break
                conn.sendall(data)
    
    # main thread
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen()
        conn, addr = s.accept()
        print(f"Connection from {addr[0]}")
        Thread(target=handler, args=(conn, addr)).start()
    

    【讨论】:

      猜你喜欢
      • 2016-09-26
      • 2011-02-11
      • 2016-03-16
      • 2013-01-19
      • 2012-09-07
      • 1970-01-01
      • 2017-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多