python实现一对一聊天_Python实现多用户全双工聊天(一对一)

多用户全双工聊天简陋版

简单实现了两个客户端之间的通信,客户端发送消息,先由服务器接收,然后服务器转发到另一客户端。

该版本功能非常简陋,仅仅实现了最简单的聊天,有很多地方需要注意。

工作步骤:

服务器端运行

一个客户端运行,连接成功后输入用户名,服务器会保存该用户名在一个字典中,字典的对应关系是 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就会被挂起。

主要的问题还是在于客户端非正常登出时的应对,目前解决了一部分问题,但是应该还有不少缺陷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值