您的位置:首页 > 编程语言 > Python开发

python 全双工 socket聊天

2017-09-11 09:23 218 查看
自学python一段时间,一直想弄个有意思的东西,所以就拿socket做一个聊天室,可以一对多,一对一全双工聊天。后续可能完善代码在鼓弄一个带gui界面的,比较有逼格


服务端:使用socketserver模块,多线程异步处理客户端消息,接受客户消息并转发 既服务端为一个中转站。加入了 登陆 注册 多人聊天 一对一聊天 防止同时在线
客户端:主线程连接服务端,两个子线程分别负责读写
sercer:

# _*_ coding:utf-8 _*_
import SocketServer
from time import ctime
import threading, traceback
import Queue
from db import DB

lock = threading.Lock()
local_school = threading.local()

class Handler(object):
queue = []
db = DB()
user_name = {}

def __init__(self, sock):
self.sock = sock
self.input = [self.sock]
# self.queue.append(self.sock)

def recv(self):
data = self.sock.recv(1024).strip()
return data

def send(self, data):
self.sock.sendall(data)

def stop(self):
self.send('ByeBye')
self.sock.close()
self.queue.remove(self.sock)
del self.user_name[local_school.user]

def exit(self):
self.sock.close()
try:
self.queue.remove(self.sock)
del self.user_name[local_school.user]
except ValueError:
pass

def broadcast(self, user, data):
for sock in self.queue:
sock.sendall('[%s] %s::%s' % (ctime(), user, data))

def one(self, user_sock, user, data):
self.user_name[user_sock].sendall('--------------\n%s\n[%s]::(%s)\n---------------' % (ctime(), user, data))

def yiduiduo(self, user, data):
time_data = ctime()
for sock in [x for x in self.queue if x != self.sock]:
sock.sendall('----------------\n%s\n[%s]::(%s)\n----------------' % (time_data, user, data))

def handler(self):
funcdict = {
'login': self.login,
'registered': self.registered,
}
try:  ###异常处理 当客户端异常断开连接或者正常断开连接:服务端处理异常
self.sock.send('请选择::login/registered/exit')
data = self.recv()
if data == 'exit':
self.stop()
# self.send('exit')
elif data in funcdict:
funcdict[data]()
else:
self.handler()
except:
if self.queue:
self.exit()

else:
pass

def login(self):
self.send('输入账号密码 格式:  user passwd  /server')
data = self.recv()
if data == 'server':
self.send('选择 exit/handler')
data = self.recv()
if data == 'exit':
self.stop()
elif data == 'handler':
self.handler()
else:
self.login()
user_data = data.split()
if len(user_data) == 2:
user = user_data[0]
passwd = user_data[1]
user_data = self.db.get_data() or {}
data_scok = self.user_name.get(user)  # 检测该用户是否在别处登陆 存在则登陆中  获得登陆的sock
if data_scok:
try:
data_scok.sendall('账号在别处登陆,被迫下线')
data_scok.close()
self.queue.remove(data_scok)
del self.user_name[local_school.user]
except:  ##异常处理  捕获此处所有异常不做处理
pass
if user in user_data and user_data[user] == passwd:
local_school.user = user
self.send('欢迎加入聊天室')
self.queue.append(self.sock)
self.broadcast('systemctl', '[%s]加入聊天室\n' % user)
self.user_name[user] = self.sock  ##用户——sock 映射
self.send('选择:单(d)/多(s)')
data = self.recv()
if data == 's':
self.Ltian()
elif data == 'd':
self.one_to_one()
else:
self.send('错误\n')
self.handler()

else:
self.send('账号或密码不正确!\n')
self.login()

else:
self.send('格式错误!\n')
self.login()

def registered(self):
self.send('注册账号密码-格式 user passwd  /server')
data = self.recv()
if data == 'server':
self.send('选择 exit/handler')
data = self.recv()
if data == 'exit':
self.stop()
self.send('exit')
else:
self.handler()
user_data = data.split()
if len(user_data) == 2:
user = user_data[0]
passwd = user_data[1]

db_data = self.db.get_data() or {}
if user in db_data:
self.send('用户已注册!')
self.registered()
else:
db_data[user] = passwd
local_school.user = user
lock.acquire()  # 添加线程锁,防止线程同时修改  数据文件
try:
self.db.put_data(db_data)
finally:
lock.release()
self.queue.append(self.sock)
self.broadcast('system', '[%s]加入聊天室\n' % user)
self.user_name[user] = self.sock
self.send('选择:单人聊天(d)/多人聊天(s)\n')
data = self.recv()
if data == 's':
self.Ltian()
print self.queue
elif data == 'd':
self.one_to_one()
else:
self.send('错误!\n')
self.handler()
else:
self.send('格式错误\n\n')
self.registered()

def Ltian(self, ):  # 多人聊天
print self.queue
print self.user_name
self.send('kaishiliaotian')
while True:
data = self.recv()
if data == 'exit':
print 'queue1 ::%s' % self.queue
self.stop()
# self.send('关闭++++++++')

print 'queue2 ::%s' % self.queue
break
self.yiduiduo(local_school.user, data)  # 组播消息

def one_to_one(self):
self.send('选择对象:to:user')
user_data = self.recv()[3:]
if user_data == local_school.user:
self.one_to_one()
if user_data in self.db.get_data():

if self.user_name.get(user_data) and self.user_name[user_data] in self.queue:
self.send('kaishiliaotian')
while True:
data = self.recv()

# if  data is None:
if data == 'server':
self.send('选择:exit/Ltian(s)')
data = self.recv()
if data == 'exit':
self.one(user_data, local_school.user, '已下线')
self.stop()
break
elif data == 's':
self.Ltian()
elif not data == '' and self.user_name.get(user_data):  # 判断 数据不为空 且用户状态在线否
self.one(user_data, local_school.user, data)
else:
self.send('用户不在线')
self.one_to_one()
else:
self.send('用户不存在!\n')
self.one_to_one()

class MyServer(SocketServer.BaseRequestHandler):
def handle(self):
print self.client_address
self.mysock = Handler(self.request)
print self.mysock.queue
self.mysock.handler()

if __name__ == '__main__':
host = '127.0.0.1'
port = 9999
addr = (host, port)
server = SocketServer.ThreadingTCPServer(addr, MyServer)
server.request_queue_size = 4399
server.serve_forever()

server.shutdown()

client:

# _*_ coding:utf-8 _*_
from socket import *
import threading
threads=[]
class Client_Handler(object):
def __init__(self, ipadr='127.0.0.1', port=9999):
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((ipadr, port))
self.input=[self.sock]
print self.input
def send(self,data):
self.sock.sendall(data)

def recv(self):
data = self.sock.recv(1024).strip()
print data
return  data

def write(self):
while True:
try:
data=raw_input('>>>')
if data=='exit':
self.send('exit')
self.sock.close()
break
self.send(data)
except socket.error: #加入异常处理  当服务端断开sock连接时跳出while循环
break
except:
break
def read(self):
while  True:
try:
self.recv()
except socket.error:
break
except:
break
a1=Client_Handler()
chat = threading.Thread(target=a1.write)
threads.append(chat)
chat = threading.Thread(target=a1.read)
threads.append(chat)
print threads
for i in range(len(threads)):
threads[i].start()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  聊天 socket python