学校的作业,用TCPSocket实现聊天室,课程中的只是简单的一对一对话,网上有用IO多路复用select的,有多线程的。
多线程的想过,服务器为每一个客户机开一个线程,很好实现但开销太大,我当初设计的是接收新连接线程和接收新消息并转发线程,存活连接用list保存。
遇到的问题是recv阻塞问题,解决方法是设置客户端连接超时,无消息则捕获timeout异常并pass,继续处理其他连接的缓冲区。(吐槽:python还是把异常作为正常流程处理了,作为一个java基础的开发还是很难接受的),本来已经写完了,用网络测试工具做客户机总是无法断开连接,甚至能继续发消息,后来发现要把窗口叉掉才能断开,那断开按钮还有什么意义,坑啊。
以下代码
服务器:
#coding=utf-8
from threading import Thread
from socket import *
import time
tcpSocket = None
aliveConnList = []
#接受新连接线程
def acceptConn():
global aliveConnList
global tcpSocket
while True:
newSocket,clientAddr = tcpSocket.accept()
print('%s已连接'%(str(clientAddr)))
newSocket.settimeout(1)
aliveConnList.append((newSocket,clientAddr))
#接收新消息线程
def recvData():
global aliveConnList
while True:
#遍历缓冲区,有数据输出并转发,没数据超时过
for s in aliveConnList:
try:
print("recving")
recvData = s[0].recv(1024)
print("received")
if len(recvData)>0:
#有数据,向其他tcp发送该数据
for other in aliveConnList:
if other!=s:
other[0].send(('recv from %s:%s'%(s[1],recvData)).encode())
print('recv from %s:%s'%(s[1],recvData))
else:
#客户端断开
print('%s已断开连接'%(str(s[1])))
s[0].close()
aliveConnList.remove(s)
except timeout:
#该连接无数据
pass
#可怜的cpu
time.sleep(1)
def main():
global tcpSocket
tcpSocket = socket(AF_INET,SOCK_STREAM)
address = ('',1234)
tcpSocket.bind(address)
tcpSocket.listen(5)
print("欢迎使用")
ta = Thread(target=acceptConn)
tr = Thread(target=recvData)
ta.start()
tr.start()
ta.join()
tr.join()
tcpSocket.close()
if __name__ == "__main__":
main()
客户端:
from threading import Thread
from socket import *
import time
tcpClientSocket = None
#接收服务器数据
def recvData():
global tcpClientSocket
while True:
recvData = tcpClientSocket.recv(1024)
if len(recvData)==0:
print('服务器已关闭!')
tcpClientSocket.close()
break
print(recvData)
time.sleep(1)
#发送数据
def sendData():
global tcpClientSocket
while True:
sendData = input("")
tcpClientSocket.send(sendData.encode())
def main():
global tcpClientSocket
tcpClientSocket = socket(AF_INET,SOCK_STREAM)
serverIP = input("请输入服务器ip: ")
serverPort = input("请输入服务器端口号: ")
serverAdd = (serverIP,int(serverPort))
tcpClientSocket.connect(serverAdd)
tr = Thread(target=recvData)
ts = Thread(target=sendData)
tr.start()
ts.start()
tr.join()
ts.join()
tcpClientSocket.close()
if __name__ == "__main__":
main()