繁体   English   中英

python套接字tchat问题

[英]python socket tchat issue

我开始使用套接字在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

在客户终端上,他不会向我打招呼,但会向我发送bluebeel。 我做了几次测试,每次上一条消息,他都会把我带走。 看起来他来晚了。

有人可以帮我吗? :)

问题分析

您的代码在_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. 客户端在_send方法中调用print(self.__socket.recv(1024).decode())
  8. 但是 .recv检索堆栈在套接字上的第一条消息 在这种情况下,它不是hello ,而是bluebeel

现在,该架构继续起作用。 您发送邮件,服务器将其ping回去,但是在收到的邮件之前总是有1条邮件。 “后期”消息。


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

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

._join 方法.connect 请记住从_send方法中删除print(self.__socket.recv(1024).decode()) ,否则它将阻止stdin

当然,发出多个/join命令时您会遇到问题。 若要正确解决该问题,您必须跟踪_receive线程并在._join方法开始时将其._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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM