2016-03-01 52 views
0

我開始使用套接字編寫python代碼,並且我的聊天腳本有點問題。python socket tchat問題

服務器腳本

import pickle, socket, struct, sys, threading 

SERVERADDRESS = ("localhost", 6030) 

class helloChatServer(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 
     self.__server = socket.socket() 
     self.users = [] 

     try: 
      self.__server.bind(SERVERADDRESS) 
     except socket.error: 
      print('Bind failed {}'.format(socket.error)) 

     self.__server.listen(10) 

    def exit(self): 
     self.__server.close() 

    def run(self): 
     print("Listening... {}".format(SERVERADDRESS)) 
     while True: 
      client, addr = self.__server.accept() 
      try: 
       threading.Thread(target=self._handle, args=(client, addr)).start() 
      except OSError: 
       print('Error during processing the message') 


    def _handle(self, client, addr): 
     print('Client connected with {}:{}'.format(addr[0], str(addr[1]))) 
     self.users.append(addr) 

     while True: 

      data = client.recv(1024) 
      print(data) 
      client.send(data) 

     client.close() 

if __name__ == '__main__': 

    helloChatServer().run() 

客戶端腳本

import pickle, socket, struct, sys, threading 

SERVERADDRESS = (socket.gethostname(), 6050) 

class helloChatClient(): 

    def __init__(self, host='localhost', port=5000, pseudo="Visitor"): 
     self.__socket = socket.socket() 
     self.__socket.bind((host, port)) 
     self.__pseudo = pseudo 

     print('Listening on {}:{}'.format(host, port)) 

    def run(self): 
     handlers = { 
      '/exit': self._exit, 
      '/quit': self._quit, 
      '/join': self._join, 
      '/send': self._send 
     } 
     self.__running = True 
     self.__address = None 
     threading.Thread(target=self._receive).start() 
     while self.__running: 
      line = sys.stdin.readline().rstrip() + ' ' 
      # Extract the command and the param 
      command = line[:line.index(' ')] 
      param = line[line.index(' ')+1:].rstrip() 
      # Call the command handler 
      if command in handlers: 
       try: 
        handlers[command]() if param == '' else handlers[command](param) 
       except: 
        print("Error during the execution of the message") 
      else: 
       print('Command inconnue:', command) 

    def _exit(self): 
     self.__running = False 
     self.__address = None 
     self.__socket.close() 

    def _quit(self): 
     self.__address = None 

    def _join(self, param): 
     if self.__pseudo == "Visitor": 
      self.__pseudo = input("Choose a username: ") 

     tokens = param.split(' ') 
     if len(tokens) == 2: 
      try: 
       self.__address = (tokens[0], int(tokens[1])) 
       self.__socket.connect(self.__address) 
       print('~~~~~~~~~~~~~~~~~~~~~~~~~~') 
       print('Connected at {}:{}'.format(*self.__address)) 
       print('~~~~~~~~~~~~~~~~~~~~~~~~~~') 
      except OSError: 
       print("Error during the sending of the message") 

     self.__socket.send(self.__pseudo.encode()) 

    def _send(self, param): 
     if self.__address is not None: 
      try: 
       message = param.encode() 
       totalsent = 0 
       while totalsent < len(message): 
        sent = self.__socket.send(message[totalsent:]) 
        totalsent += sent 
       print(self.__socket.recv(1024).decode()) 
      except OSError: 
       print('Error during the reception of the message') 

    def _receive(self): 
     while self.__running: 
      try: 
       data = self.__socket.recv(1024).decode() 
       print(data) 
      except socket.timeout: 
       pass 
      except OSError: 
       return 


if __name__ == '__main__': 

    if len(sys.argv) == 4: 
     helloChatClient(sys.argv[1], int(sys.argv[2]), sys.argv[3]).run() 
    else: 
     helloChatClient().run() 

好吧,當我在終端上運行腳本,我看到這一點。

服務器

MacBook-Pro-de-Saikou:labo2 saikouah$ python3.4 helloChatServer.py 
En écoute sur... ('MacBook-Pro-de-Saikou.local', 6030) 
Client connected with 127.0.0.1:5004 
Il y a actuellement 1 connecté 
b'bluebeel' 
b'hello' 

客戶

MacBook-Pro-de-Saikou:labo2 saikouah$ python3.4 helloChatClient.py localhost 5004 bluebeel 
Écoute sur localhost:5004 
/join MacBook-Pro-de-Saikou.local 6030 
~~~~~~~~~~~~~~~~~~~~~~~~~~ 
Connecté à MacBook-Pro-de-Saikou.local:6030 
~~~~~~~~~~~~~~~~~~~~~~~~~~ 
/send hello 
bluebeel 

在客戶端,他不打印我打招呼,但bluebeel。我做了幾次測試,每次收到前一封郵件時,他都會帶我。看起來他遲到了。

有人可以幫助我嗎? :)

回答

0

故障分析

你的代碼沒有在_receive功能:

data = self.__socket.recv(1024).decode() 

此行拋出OSError因爲你試圖調用.recv之前連接到服務器。因此異常處理程序被觸發並且函數退出。那麼,什麼情況是,調用

threading.Thread(target=self._receive).start() 

功能_receive退出後,之前你叫/join。所以看看會發生什麼

  1. 您可以撥打/join
  2. bluebeel被髮送到
  3. 服務器接收它,並將其發送回客戶端
  4. _receive功能不再存在的服務器。因此,該消息在插座上的「堆疊」(它會等待下一次.recv()調用)
  5. 你叫/send hello
  6. 服務器接收hello並將其發送回
  7. 客戶端調用print(self.__socket.recv(1024).decode())_send方法
  8. .recv檢索堆疊在插座上的第一條消息。在這種情況下,它不是hello,它是bluebeel

現在這個模式繼續工作。您發送消息,服務器將其回送,但接收到的消息前面始終有1條消息。 「遲到」消息。


SOLUTION

一個解決這個問題的方法是調用

threading.Thread(target=self._receive).start() 

._join方法.connect後。請記得從_send方法中刪除print(self.__socket.recv(1024).decode()),否則將阻止stdin

當然,您在發出多個/join命令時會遇到問題。要正確解決這個問題,你必須跟蹤_receive線程,並在._join方法開始時將其終止。但這超出了恕我直言這個問題的範圍。


邊注

永遠不要處理異常像你這樣。這是錯誤的:

try: 
    data = self.__socket.recv(1024).decode() 
    print(data) 
except socket.timeout: 
    pass 
except OSError: 
    return 

至少做到這一點:

import traceback 

try: 
    data = self.__socket.recv(1024).decode() 
    print(data) 
except socket.timeout: 
    traceback.print_exc() 
except OSError: 
    traceback.print_exc() 
    return