【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にメッセージを送信するとエラーが発生するなど、多くの欠点がある.ブロガーは怠け者なので、このバグは修復しません~