Python Socket Chat:客户端recv()挂起

时间:2017-09-26 14:50:02

标签: python sockets networking

我正在尝试构建的聊天程序存在一个小问题: 有2个客户端和一个服务器,客户端发送消息,服务器接收它,并将其广播给每个人(通常是两个客户端,但可以有更多)。问题是,当客户端(通过服务器)向另一个客户端发送数据时,接收客户端必须自己发送一些信息才能看到该消息。

这是我的server.py:

import socket
from thread import start_new_thread
import threading


def thread(c,clients, c_lock,buf=1024,):
    c.send("You are connected! \n")
    while True:
        try:
            data = c.recv(buf)
            print data
            if not data:
                break
            broadcast(clients,c_lock,data)
        except socket.error as e:
            exit(0)
    c.close()

def broadcast(clients,c_lock,data):
    with c_lock:
        for c in clients:
            c.sendall("- " + data)

def main():
    host = ''
    port = 10000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    clients = set()
    clients_lock = threading.Lock()
    s.bind((host, port))
    s.listen(5)
    print "%s listening on %s" % (host, str(port))
    while True:
            c, addr = s.accept()
            print "got connection from ", addr
            with clients_lock:
                clients.add(c)
            start_new_thread(thread, (c, clients, clients_lock, ))
    s.close()

if __name__ == "__main__":
    main()

和client.py:

import socket
import time
from sys import exit

def main():
    host = socket.gethostname()
    port = 10000
    name = str(raw_input("Enter your name: "))


    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('192.168.1.12', port))
    except Exception as e:
        print 'Connection not working... %s' % (e)
        exit()

    print s.recv(1024)  # greeting msg
    print "Send q to stop the connection \n"
    out = ''
    while out != 'q':
        out = str(raw_input('->'))
        recv =  s.recv(1024)
        s.send(name + ": " + out)
        time.sleep(0.1)
        print recv
    s.close()
if __name__ == '__main__':
    main()

感谢您的帮助:) (请原谅我这个烂摊子:/)

1 个答案:

答案 0 :(得分:2)

你有两个问题。

recv()是阻止通话 raw_input() / input()也是阻止通话。

意味着客户端将“挂起”这两个调用(recv()是异常如果管道中有数据,那么它将不会阻塞)。

要解决这个问题,就像在你的例子中一样。您可以在服务器中使用threading来创建一个类/实例,它负责从套接字中检索数据,一个用于读取用户输入。或者在Linux上,如果数据在管道中,您可以使用select.selectselect.epoll轮询套接字,然后调用recv()。对于输入,您最好在select/epoll上使用sys.stdin或使用线程。

但如果没有可供读取的数据,recv()将永远是Python(以及许多其他语言)的阻塞调用。 raw_input() / input()也是如此。

使用epoll,这是一个简短的例子:

from select import epoll, EPOLLIN
import sys

polly = epoll()
polly.register(s.fileno(), EPOLLIN)
polly.register(sys.stdin.fileno(), EPOLLIN)

for fid, eid in polly.poll(1):
    # s is your socket, s.fileno() on linux is the file number that socket has been assigned to.
    # And epoll returns a list of all filenumbers that has data waiting.
    # So we can check if the fid from epoll == the sockets filenumber.
    if fid == s.fileno(): 
        data = s.recv(8192)
    # of if it's sys.stdin's filenumber
    # (stdin is user input, this is where raw_input normally gets its data from)
    elif fid == sys.stdin.fileno():
        user_input = sys.stdin.readline()
        print('User wrote:', user_input)

或者如果你想使用线程:

from threading import Thread, enumerate as t_enum
class userInput(Thread):
    def __init__(self, socket, username):
        Thread.__init__(self)
        self.socket = socket
        self.username = username
        self.start()

    def run(self):
        mainThread = None
        for thread in t_enum():
            if thread.name == 'MainThread':
                mainThread = thread
                break

        while mainThread and mainThread.isAlive():
            out = raw_input('->') # str() is redundant, it's type(str) already.
            self.socket.send(self.username + ": " + out)

再往下一点代码:

name = str(raw_input("Enter your name: "))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect(('192.168.1.12', port))
except Exception as e:
    print 'Connection not working... %s' % (e)
    exit()

userInput(s, name)

请注意,这不是正确的线程处理。但这是一个最小的例子,它将向您展示如何解决问题的一般要点。

作为最后一个选项,fcntl也是一个不错的选择。

未来提示的进一步编程

使用print(...)代替print ...
使用'Connection not working... {}'.format(e)代替'Connection not working... %s' % (e)
总的来说,尝试使用Python3而不是Python2。如果你的旧环境充满了遗留的东西(阅读:debian,旧开发的应用程序等),那么Python2应该只被使用。