用Python创建一个简单的聊天应用程序(套接字)

时间:2016-03-17 12:17:45

标签: python multithreading sockets synchronization chat

我正在尝试使用套接字(python)创建一个简单的聊天应用程序。客户端可以向服务器发送消息,服务器只是将消息广播到除发送消息之外的所有其他客户端。

客户端有两个线程,它们永远在运行

  

send:发送只是将cleints消息发送到服务器。

     

receive:从服务器接收消息。

服务器还有两个线程,它们永远运行

  

accept_cleint:接受来自客户端的传入连接。

     

broadcast_usr:接受来自客户端的消息,并将其广播给所有其他客户端。

但是输出错误(请参考下图)。所有线程都假设始终处于活动状态,但有时客户端可以发送消息,有时则不能。比如说,Tracey发送'hi'4次,但没有广播,当John说'再见'2次然后1次它的消息被广播。似乎在服务器上有一些thread synchronization问题,我不确定。请告诉我出了什么问题。

enter image description here

以下是代码。

chat_client.py

import socket, threading

def send():
    while True:
        msg = raw_input('\nMe > ')
        cli_sock.send(msg)

def receive():
    while True:
        sen_name = cli_sock.recv(1024)
        data = cli_sock.recv(1024)

        print('\n' + str(sen_name) + ' > ' + str(data))

if __name__ == "__main__":   
    # socket
    cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # connect
    HOST = 'localhost'
    PORT = 5023
    cli_sock.connect((HOST, PORT))     
    print('Connected to remote host...')
    uname = raw_input('Enter your name to enter the chat > ')
    cli_sock.send(uname)

    thread_send = threading.Thread(target = send)
    thread_send.start()

    thread_receive = threading.Thread(target = receive)
    thread_receive.start()

chat_server.py

import socket, threading

def accept_client():
    while True:
        #accept    
        cli_sock, cli_add = ser_sock.accept()
        uname = cli_sock.recv(1024)
        CONNECTION_LIST.append((uname, cli_sock))
        print('%s is now connected' %uname)

def broadcast_usr():
    while True:
        for i in range(len(CONNECTION_LIST)):
            try:
                data = CONNECTION_LIST[i][1].recv(1024)
                if data:
                    b_usr(CONNECTION_LIST[i][1], CONNECTION_LIST[i][0], data)
            except Exception as x:
                print(x.message)
                break

def b_usr(cs_sock, sen_name, msg):
    for i in range(len(CONNECTION_LIST)):
        if (CONNECTION_LIST[i][1] != cs_sock):
            CONNECTION_LIST[i][1].send(sen_name)
            CONNECTION_LIST[i][1].send(msg)

if __name__ == "__main__":    
    CONNECTION_LIST = []

    # socket
    ser_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # bind
    HOST = 'localhost'
    PORT = 5023
    ser_sock.bind((HOST, PORT))

    # listen    
    ser_sock.listen(1)
    print('Chat server started on port : ' + str(PORT))

    thread_ac = threading.Thread(target = accept_client)
    thread_ac.start()

    thread_bs = threading.Thread(target = broadcast_usr)
    thread_bs.start()

2 个答案:

答案 0 :(得分:1)

好的我早些时候在评论中说谎了,抱歉。问题实际上在服务器上的broadcast_usr()函数中。它在recv()方法中阻塞,并阻止除当前所选用户之外的所有用户在for循环中一直进行通话。为了解决这个问题,我更改了server.py程序,为它接受的每个客户端连接生成一个新的broadcast_usr线程。我希望这会有所帮助。

import socket, threading

def accept_client():
    while True:
        #accept    
        cli_sock, cli_add = ser_sock.accept()
        uname = cli_sock.recv(1024)
        CONNECTION_LIST.append((uname, cli_sock))
        print('%s is now connected' %uname)
        thread_client = threading.Thread(target = broadcast_usr, args=[uname, cli_sock])
        thread_client.start()

def broadcast_usr(uname, cli_sock):
    while True:
        try:
            data = cli_sock.recv(1024)
            if data:
                print "{0} spoke".format(uname)
                b_usr(cli_sock, uname, data)
        except Exception as x:
            print(x.message)
            break

def b_usr(cs_sock, sen_name, msg):
    for client in CONNECTION_LIST:
        if client[1] != cs_sock:
            client[1].send(sen_name)
            client[1].send(msg)

if __name__ == "__main__":    
    CONNECTION_LIST = []

    # socket
    ser_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # bind
    HOST = 'localhost'
    PORT = 5023
    ser_sock.bind((HOST, PORT))

    # listen    
    ser_sock.listen(1)
    print('Chat server started on port : ' + str(PORT))

    thread_ac = threading.Thread(target = accept_client)
    thread_ac.start()

    #thread_bs = threading.Thread(target = broadcast_usr)
    #thread_bs.start()

答案 1 :(得分:1)

我试图解决你说@Atinesh的错误。将向客户端询问一次用户名,并且此“uname”将包含在要发送的数据中。看看我对“ 发送 ”功能做了什么。

为了便于可视化,我在所有收到的消息中添加了'\ t'。

import socket, threading

def send(uname):
    while True:
        msg = raw_input('\nMe > ')
        data = uname + '>' + msg
        cli_sock.send(data)

def receive():
    while True:
        data = cli_sock.recv(1024)
        print('\t'+ str(data))

if __name__ == "__main__":   
    # socket
    cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # connect
    HOST = 'localhost'
    PORT = 5023

    uname = raw_input('Enter your name to enter the chat > ')

    cli_sock.connect((HOST, PORT))     
    print('Connected to remote host...')


    thread_send = threading.Thread(target = send,args=[uname])
    thread_send.start()

    thread_receive = threading.Thread(target = receive)
    thread_receive.start()

您还必须相应地修改服务器代码。

server.py

import socket, threading

def accept_client():
    while True:
        #accept    
        cli_sock, cli_add = ser_sock.accept()
        CONNECTION_LIST.append(cli_sock)
        thread_client = threading.Thread(target = broadcast_usr, args=[cli_sock])
        thread_client.start()

def broadcast_usr(cli_sock):
    while True:
        try:
            data = cli_sock.recv(1024)
            if data:
               b_usr(cli_sock, data)
         except Exception as x:
            print(x.message)
            break

def b_usr(cs_sock, msg):
    for client in CONNECTION_LIST:
        if client != cs_sock:
            client.send(msg)

if __name__ == "__main__":    
    CONNECTION_LIST = []

    # socket
    ser_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # bind
    HOST = 'localhost'
    PORT = 5023
    ser_sock.bind((HOST, PORT))

    # listen    
    ser_sock.listen(1)
    print('Chat server started on port : ' + str(PORT))

    thread_ac = threading.Thread(target = accept_client)
    thread_ac.start()

服务器端发生变化的事情是:连接的用户和发言的用户不再可见。如果您的目的是连接客户,我不知道这意味着什么。也许如果你想通过服务器严格监控客户端,可能还有另一种方式。