Python 2.6 Chat Loop Issue. Cant Receive And Send

2020-04-24 11:12发布

Im trying to make a console chat program, but have a problem with my loop. I cant get input and receive the other persons input at the same time. If two message or more are sent from one end, the other end cant receive the next message until after it sends one. Im fairly new to python, and was looking for a nudge in the right direction. Ive thought of multi-threading, but thats a little outta my grasp atm. Any other ideas?

import EncMod
from socket import *

#Get User Info
Ip = raw_input('IP>>>')
Port = int(raw_input('Port>>>'))
User = raw_input('Username>>>')

#Open Socket To Server
EncCon = socket(AF_INET, SOCK_STREAM)
EncCon.connect((Ip, Port))

print '\nStarting Chat....'
print '\n<-------------------------------------------->\n\n'

#Send/Receive Loop
while 1:
   MsgOut = raw_input()
   if MsgOut: EncCon.send(MsgOut)

   MsgIn = EncCon.recv(1024)
   if MsgIn: print MsgIn

EncCon.close()

3条回答
做个烂人
2楼-- · 2020-04-24 11:43

The Twisted framework can be used to help accomplish this task. The code below initiates a chat server that clients can then connect to and communicate back and forth, based off of the server instance settings. You can make the appropriate modifications to fit your requirements:

from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class Chat(LineReceiver):

    def __init__(self, users):
        self.users = users
        self.name = None
        self.state = "GETNAME"

    def connectionMade(self):
        self.sendLine("What's your name?")

    def connectionLost(self, reason):
        if self.users.has_key(self.name):
            del self.users[self.name]

    def lineReceived(self, line):
        if self.state == "GETNAME":
            self.handle_GETNAME(line)
        else:
            self.handle_CHAT(line)

    def handle_GETNAME(self, name):
        if self.users.has_key(name):
            self.sendLine("Name taken, please choose another.")
            return
        self.sendLine("Welcome, %s!" % (name,))
        self.name = name
        self.users[name] = self
        self.state = "CHAT"

    def handle_CHAT(self, message):
        message = "<%s> %s" % (self.name, message)
        for name, protocol in self.users.iteritems():
            if ':' in message:
                self.exc(message.split(':')[0])
            if protocol != self:
                protocol.sendLine(message)

    def exc(self, cmd):
        print cmd
        if cmd == 'who':
            for i in self.users:
                print i


class ChatFactory(Factory):

    def __init__(self):
        self.users = {} # maps user names to Chat instances

    def buildProtocol(self, addr):
        return Chat(self.users)


reactor.listenTCP(8123, ChatFactory())
reactor.run()
查看更多
3楼-- · 2020-04-24 11:48

Threading isn't as hard as you think, and mastering it is an invaluable addition to your toolchest.

Just make a class that is a subclass of Thread and make sure it has a run() method. then instantiate the class and call its start() method.

Making the thread stop is more difficult to do right. Best to set a flag and make sure you check it regularly in your while loop, hence you need a timeout on your blocking recv(), of, say, 1 second.

from socket import *
from threading import Thread


#Get User Info
Ip = raw_input('IP>>>')
Port = int(raw_input('Port>>>'))
User = raw_input('Username>>>')

#Open Socket To Server
EncCon = socket(AF_INET, SOCK_STREAM)
EncCon.connect((Ip, Port))

print '\nStarting Chat....'
print '\n<-------------------------------------------->\n\n'


class ReceiveThread(Thread):

    def __init__(self, sock):
        Thread.__init__(self)
        self.sock = sock
        self.shouldstop = False

    def run(self):
        self.sock.settimeout(1)
        while not self.shouldstop:
            try:
                data = self.sock.read()
                print data
            except socket.timeout:
                continue

    def stop(self):
        self.shouldstop = True


# start receive loop:
r = ReceiveThread(EncCon).start()


#Send Loop
while 1:
    MsgOut = raw_input()
    if MsgOut: EncCon.send(MsgOut)

    if MsgOut == '.':
        r.stop()
        r.join()


EncCon.close()

Now, this program still has the original problem that it would be impossible to start two instances, as you do not listen, but immediately connect. But that, I believe, was not the main part of your question.

查看更多
虎瘦雄心在
4楼-- · 2020-04-24 11:50

The problem is that your recv() call blocks until some data is received, and while recv() is blocking, your program isn't checking to see if there is any input from stdin. The traditional single-threaded solution to this is to set the socket to non-blocking I/O (via EncCon.setblocking(False)) and then have your program block inside select() instead. Pass both EncCon and stdin to select() (as part of its read-socket-set argument) so that select() will return whenever either of them has some data to give to you. (Note that this method doesn't work under Windows, as windows doesn't allow select() to block on stdin :P )

查看更多
登录 后发表回答