【问题标题】:An operation was attempted on something that is not a socket (tried fixing a lot)尝试对不是套接字的东西进行操作(尝试修复很多)
【发布时间】:2016-06-23 16:34:30
【问题描述】:

在您说这是重复之前,我已经查看了很多关于此的文章,但仍然无法修复它。 我正在制作一个非常基本的聊天客户端和服务器 python 程序。

但是,通过我的客户端连接后,它在服务器控制台上显示“已连接”,但在聊天中立即断开连接,并出现错误“OSError: [WinError 10038] An operation was tried on something that is not a 插座'

聊天

def chat_client():
    if(len(sys.argv) not in (3, 4)):
        print("Usage: python chat_client.py <hostname> <port> <optional-username>\n")
        sys.exit()

    host = sys.argv[1]
    port = int(sys.argv[2])
    username = ""
    if len(sys.argv) == 4: 
        username = sys.argv[3]
    else:
        username = "Guest"

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(2)

    # Connect to remote host
    try:
        s.connect((host, port))
    except:
        print("Unable to connect")
        sys.exit()

    print("Connected to remote host. You can start sending messages")
    print("***   Press Control-C to log off   ***\n")
    sys.stdout.write("[" + username + "] ")
    sys.stdout.flush()

    while True:
        socket_list = [sys.stdin, s]

        try:
            # Get the list sockets which are readable
            ready_to_read, ready_to_write, in_error = select.select(socket_list, [], [])
        except KeyboardInterrupt:
            system("clear")
            sys.stdout.write("\nYou have logged off\n")
            sys.stdout.flush()
            sys.exit()

服务器

HOST = ""
SOCKET_LIST = []
RECV_BUFFER = 4096
PORT = 9009
CONVERSATION = ""

def chat_server():
    global CONVERSATION
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((HOST, PORT))
    server_socket.listen(10)

    # Add server socket object to the list of readable connections
    SOCKET_LIST.append(server_socket)

    print("Chat server started on port " + str(PORT))

    while True:
        try:
            # Get the list sockets which are ready to be read through select
            # 4th arg, time_out = 0 : poll and never block
            ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0)

            for sock in ready_to_read:
                # A new connection request recieved
                if sock == server_socket:
                    sockfd, addr = server_socket.accept()
                    SOCKET_LIST.append(sockfd)
                    print("Client (%s, %s) connected" % addr)

                    broadcast(server_socket, sockfd, "[%s, %s] entered our chatting room\n" % addr)
                # A message from a client, not a new connection
                else:
                    # Process data recieved from client
                    try:
                        # Recieving data from socket
                        data = sock.recv(RECV_BUFFER)
                        if data:
                            # there is something in the socket
                            # broadcast(server_socket, sock, "\r" + '[' + str(sock.getpeername()) + '] ' + data) # old
                            broadcast(server_socket, sock, "\r" + data)
                        else:
                            # Remove the socket that's broken
                            if sock in SOCKET_LIST:
                                SOCKET_LIST.remove(sock)

                            # at this stage, no data probably means the connection has been broken
                            broadcast(server_socket, sock, "Client (%s, %s) is offline\n" % addr)
                    except:
                        broadcast(server_socket, sock, "Client (%s, %s) is offline\n" % addr)
                        continue
        except KeyboardInterrupt:
            server_socket.close()
            sys.exit()

    server_socket.close()


# broadcast chat messages to all connected clients
def broadcast(server_socket, sock, message):
    for socket in SOCKET_LIST:
        # send the message only to peer
        if socket != server_socket and socket != sock:
            try:
                socket.send(message)
            except:
                # Broken socket connection
                socket.close()
                # Broken socket, remove it
                if socket in SOCKET_LIST:
                    SOCKET_LIST.remove(socket)

if __name__ == "__main__":
    sys.exit(chat_server())

【问题讨论】:

    标签: python windows sockets


    【解决方案1】:

    来自selectdocumentation

    Windows 上的文件对象是不可接受的,但套接字是可接受的。在 Windows,底层的 select() 函数由 WinSock 提供 库,并且不处理不是源自的文件描述符 来自 WinSock。

    这排除了使用sys.stdin

    替代方案

    • 使用Cygwin(无需修改代码)
    • 创建一个等待sys.stdin 的线程(如here
    • 走完整的 Windows 路线并使用WaitForMultipleObjects
    • 使用一些将这些细节抽象出来的库,我喜欢libuv,但没有在 python 中使用它

    另一件事:不要在无限循环中使用具有零超时的select。这种忙碌的等待实在是太低效了。而是省略超时,让select 阻塞,直到描述符准备好。

    【讨论】:

    • 对于 Python 3,ctypes.pythonapi._PyOS_SigintEvent() 返回为 SIGINT 设置的 Windows 事件的句柄。将此附加到句柄数组以允许 Ctrl+C 中断 WaitForMultipleObjects 调用。 Python 3 的 _winapi.WaitForMultipleObjects 会为您执行此操作,但前提是不要要求它等待所有对象,因为要求等待 Ctrl+C 是没有意义的。
    猜你喜欢
    • 2021-11-04
    • 1970-01-01
    • 2021-09-01
    • 2020-07-19
    • 2013-02-19
    • 2012-10-12
    • 1970-01-01
    • 2014-09-04
    • 1970-01-01
    相关资源
    最近更新 更多