2016-03-09 49 views
2

在你說這是重複的之前,我已經看過很多關於這個的文章,但仍然無法修復它。 我正在製作一個非常基本的聊天客戶端和服務器python程序。一個操作嘗試了一些不是套接字的東西(試圖修復很多)

但是,通過我的客戶端連接後,它在服務器控制檯上顯示'已連接',但在聊天窗口中立即斷開連接,出現錯誤'OSError:[WinError 10038]嘗試對某個非 套接字「

CHAT

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

回答

2

selectdocumentation

File objects on Windows are not acceptable, but sockets are. On Windows, the underlying select() function is provided by the WinSock library, and does not handle file descriptors that don’t originate from WinSock.

這就排除了使用sys.stdin

替代

  • 使用Cygwin(無修改所需的代碼)
  • 創建上等待線程sys.stdin(如here
  • 轉到完整的Windows路線,並使用WaitForMultipleObjects
  • 使用一些抽象出這些細節的庫,我喜歡libuv,但沒有用它與python

另一件事:不要使用select與無限循環中的零超時。這種繁忙的等待效率非常低。相反,請忽略超時,讓select阻塞,直到描述符準備就緒。

+0

對於Python 3,'ctypes.pythonapi._PyOS_SigintEvent()'返回爲'SIGINT'設置的Windows事件的句柄。將它追加到句柄數組中以允許通過Ctrl + C中斷「WaitForMultipleObjects」調用。 Python 3的'_winapi.WaitForMultipleObjects'爲你做這件事,但只有在不要求它等待所有對象時,因爲需要等待Ctrl + C是沒有意義的。 – eryksun

相關問題