Python實現多用戶全雙工聊天(一對一)
阿新 • • 發佈:2017-12-09
nobody mes nec 分開 ctrl ces connect rgs welcome
多用戶全雙工聊天簡陋版
簡單實現了兩個客戶端之間的通信,客戶端發送消息,先由服務器接收,然後服務器轉發到另一客戶端。
該版本功能非常簡陋,僅僅實現了最簡單的聊天,有很多地方需要註意。
工作步驟:
- 服務器端運行
- 一個客戶端運行,連接成功後輸入用戶名,服務器會保存該用戶名在一個字典中,字典的對應關系是
username --> socket
- 輸入用戶名之後,該客戶端需要確定一個聊天用戶,客戶端輸入
To:user
即可;如果客戶端發送其他文本的話,會收到來自服務器的提示:“Nobody is chatting with you. Maybe the one talked with you is talking with someone else” - 當兩個客戶端成功連接之後就可以互相發送消息
服務器端代碼如下:
#!/usr/bin/python
#coding:utf-8
#server.py
from socket import *
from time import ctime
import threading
import re
HOST = ‘‘
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST,PORT)
tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
clients = {} # username -> socket
chatwith = {} # user1.socket -> user2.socket
# clients字典中記錄了連接的客戶端的用戶名和套接字的對應關系
# chatwith字典中記錄了通信雙方的套接字的對應
# messageTransform()處理客戶端確定用戶名之後發送的文本
# 文本只有四種類型:
# None
# Quit
# To:someone
# 其他文本
def messageTransform(sock,user):
while True:
data = sock.recv(BUFSIZ)
if not data:
if chatwith.has_key(sock):
chatwith[sock].send(data)
del chatwith[chatwith[sock]]
del chatwith[sock]
del clients[user]
sock.close()
break
if data==‘Quit‘:
sock.send(data)
if chatwith.has_key(sock):
data = ‘%s.‘ % data
chatwith[sock].send(data)
del chatwith[chatwith[sock]]
del chatwith[sock]
del clients[user]
sock.close()
break
elif re.match(‘^To:.+‘, data) is not None:
data = data[3:]
if clients.has_key(data):
if data==user:
sock.send(‘Please don\‘t try to talk with yourself.‘)
else:
chatwith[sock] = clients[data]
chatwith[clients[data]] = sock
else:
sock.send(‘the user %s is not exist‘ % data)
else:
if chatwith.has_key(sock):
chatwith[sock].send(‘[%s] %s: (%s)‘ % (ctime(),user,data))
else:
sock.send(‘Nobody is chating with you. Maybe the one talked with you is talking with someone else‘)
# 每個客戶端連接之後,都會啟動一個新線程
# 連接成功後需要輸入用戶名
# 輸入的用戶名可能會:
# 已存在
# (客戶端直接輸入ctrl+c退出)
# 合法用戶名
def connectThread(sock,test): # client‘s socket
user = None
while True: # receive the username
username = sock.recv(BUFSIZ)
if not username: # the client logout without input a name
print(‘The client logout without input a name‘)
break
if clients.has_key(username): # username existed
sock.send(‘Reuse‘)
else: # correct username
sock.send(‘OK‘)
clients[username] = sock # username -> socket
user = username
break
if not user:
sock.close()
return
print(‘The username is: %s‘ % user)
# get the correct username
messageTransform(sock,user)
if __name__==‘__main__‘:
while True:
print(‘...WAITING FOR CONNECTION‘)
tcpCliSock, addr = tcpSerSock.accept()
print(‘CONNECTED FROM: ‘, addr)
chat = threading.Thread(target = connectThread, args = (tcpCliSock,None))
chat.start()
客戶端代碼如下:
#!/usr/bin/python
#coding:utf-8
#client.py
from socket import *
from time import ctime
# from termios import tcflush,TCIFLUSH
import threading
import sys
HOST = ‘127.0.0.1‘
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST,PORT)
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
‘‘‘
因為每個客戶端接收消息和發送消息是相互獨立的,
所以這裏將兩者分開,開啟兩個線程處理
‘‘‘
def Send(sock,test):
while True:
try:
data = raw_input()
sock.send(data)
if data==‘Quit‘:
break
except KeyboardInterrupt:
sock.send(‘Quit‘)
break
def Recv(sock,test):
while True:
data = sock.recv(BUFSIZ)
if data==‘Quit.‘:
print(‘He/She logout‘)
continue
if data==‘Quit‘:
break
print(‘ %s‘ % data)
if __name__==‘__main__‘:
print(‘Successful connection‘)
while True:
username = raw_input(‘Your name(press only Enter to quit): ‘)
tcpCliSock.send(username)
if not username:
break
# username is not None
response = tcpCliSock.recv(BUFSIZ)
if response==‘Reuse‘:
print(‘The name is reuse, please set a new one‘)
continue
else:
print(‘Welcome!‘)
break
if not username:
tcpCliSock.close()
recvMessage = threading.Thread(target = Recv, args = (tcpCliSock,None))
sendMessage = threading.Thread(target = Send, args = (tcpCliSock,None))
sendMessage.start()
recvMessage.start()
sendMessage.join()
recvMessage.join()
總結:
功能簡陋,後續會有所改進。這裏還有很多地方需要註意。
比如說兩個客戶端A成功連接後,和客戶端B聊天。A發送消息時直接輸入ctrl+c
退出程序(sendMessage
線程會結束),我將這種情況模擬成A發送Quit
登出。服務器接收到A登出信息之後,會回發一個Quit
給A,A成功登出(recvMessage
線程結束)。此外如果A和B建立了聊天關系,就要接觸這個關系,服務器發送Quit.
給B,B會繼續接收信息,但是服務器端的chatwith
字典中已經不存在A.socket --> B.socket
關系。
但是還有很多沒有解決的問題,比如說客戶端A並沒有輸入信息,直接點擊關閉按鈕退出,就會發生異常(與之聊天的B客戶端會崩潰)。
如果當前存在 A-->B的聊天關系,這時有一個C登錄,並且確定了C-->A的聊天關系,那麽A會和C聊天,這時客戶端B就會被掛起。
主要的問題還是在於客戶端非正常登出時的應對,目前解決了一部分問題,但是應該還有不少缺陷。
Python實現多用戶全雙工聊天(一對一)