【python网络编程】多线程实现多用户全双工聊天

    在上一篇文章中,我们实现了1对1的异步通信,在文章结尾,给出了多对多通信的思路。

    既然说了,咱就动手试一试,本次用的是多线程来实现,正好练练手~

    首先讲一下思路:

    我们将服务器做为中转站来处理信息,一方面与客户端互动,另一方面进行消息转发。

    大体思路确定下来后,需要确定一些通信规则:

    1. 客户端与服务器建立连接后,需要输入用户名登入,若用户名已存在,将reuse反馈给用户,用户输出错误信息,退出

    2. 用户输入正确的用户名后,即可进行通信了。如果未选择通信对象,则服务器会反馈信息,提示用户选择通信对象

    3. 选择通信对象的方法为,输入to:username,如果所选择的对象不存在,反馈错误信息,重新输入

    4.当正确选择通信对象后,双方建立连接,通过服务器中转信息进行通信

    5.在通信中,若发送‘quit’,则结束发送消息的线程,并指示服务器该用户准备登出,服务器删除该用户后,反馈消息给用户,用户结束接收消息的线程并退出

    6.如果A正在与C通信,此时B向A发送信息,则A的通信窗口变为与B的通信窗口,即接收到B消息后,A发出的消息不再是给C,而是默认给B


#!/usr/bin/python

'test TCP server'

from socket import *
from time import ctime
import threading    #多线程模块
import re     #正则表达式模块

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

def Deal(sock, user):
	while True:
		data = sock.recv(BUFSIZ)    #接收用户的数据
		if data == 'quit':    #用户退出
			del clients[user]
			sock.send(data)
			sock.close()
			print '%s logout' %user
			break
		elif re.match('to:.+', data) is not None:    #选择通信对象
			data = data[3:]
			if clients.has_key(data):
				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('Please input the user who you want to chat with')


tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)

clients = {}    #提供 用户名->socket 映射
chatwith = {}    #提供通信双方映射

while True:
	print 'waiting for connection...'
	tcpCliSock, addr = tcpSerSock.accept()
	print '...connected from:',addr
	username = tcpCliSock.recv(BUFSIZ)    #接收用户名
	print 'The username is:',username
	if clients.has_key(username):    #查找用户名
		tcpCliSock.send("Reuse")    #用户名已存在
		tcpCliSock.close()
	else:
		tcpCliSock.send("Welcome!")    #登入成功
		clients[username] = tcpCliSock
		chat = threading.Thread(target = Deal, args = (tcpCliSock,username))    #创建新线程进行处理
		chat.start()    #启动线程
tcpSerSock.close()



#!/usr/bin/python

'test tcp client'

from socket import *
import threading

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
threads = []

def Send(sock, test):    #发送消息
	while True:
		data = raw_input('>')
		tcpCliSock.send(data)
		if data == 'quit':
			break

def Recv(sock, test):     #接收消息
	while True:
		data = tcpCliSock.recv(BUFSIZ)
		if data == 'quit':
			sock.close()     #退出时关闭socket
			break		
		print data
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

print 'Please input your username:',
username = raw_input()
tcpCliSock.send(username)
data = tcpCliSock.recv(BUFSIZ)
if data == 'Reuse':
	print 'The username has been used!'
else:
	print 'Welcome!'
	chat = threading.Thread(target = Send, args = (tcpCliSock,None))    #创建发送信息线程
	threads.append(chat) 
	chat = threading.Thread(target = Recv, args = (tcpCliSock,None))    #创建接收信息线程
	threads.append(chat)
	for i in range(len(threads)):    #启动线程
		threads[i].start()
	threads[0].join()    #在我们的设计中,send线程必然先于recv线程结束,所以此处只需要调用send的join,等待recv线程的结束。


    当然,本程序还有许多不足之处,比如通信双方中A退出时,另一方B的通信列表中仍然又A,此时如果B再向已经登出的B发送消息,就会出错。博主比较懒,就不修复这个bug啦~

你可能感兴趣的:(python)