python socket tchat issue

时间:2016-03-01 21:53:01

标签: python sockets

我开始使用套接字在python中编码,我的聊天脚本有点问题。

服务器脚本

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

在客户终端上,他并没有给我打印你好,但是蓝色的。我做了几次测试,他每次都带我上一条消息。看起来他迟到了。

有人可以帮帮我吗? :)

1 个答案:

答案 0 :(得分: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功能不再存在。所以消息是&#34;堆积&#34;在套接字上(它将等待下一个.recv()调用)
  5. 您致电/send hello
  6. 服务器收到hello并将其发回
  7. 客户在print(self.__socket.recv(1024).decode())方法
  8. 中致电_send
  9. 但是 .recv检索堆叠在套接字上的第一条消息。在这种情况下,它不是hello,而是bluebeel
  10. 现在这个架构继续有效。您发送消息,服务器将其恢复,但在收到消息之前总是有1条消息。 &#34;晚了&#34;消息。

    <强>解

    解决此问题的一种方法是致电

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

    ._join方法之后 .connect。请务必从print(self.__socket.recv(1024).decode())方法中删除_send,否则会阻止stdin

    当然,在发出多个/join命令时会遇到问题。要正确解决这个问题,您必须跟踪_receive线程并在._join方法开始时将其终止。然而,这超出了恕我直言的这个问题的范围。

    SIDE NOTE

    不要像你那样处理异常。这是错的:

    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