通过流式Socket(SOCK_STREAM)来开发互动聊天室,在服务器端至少需要创建两个Socket,一个Socket专门负责接收客户端的连接请求,每次成功接收客户端的连接请求,便在服务器端创建一个对应的负责与客户端进行通信的Socket。每次成功连接一个客户端便开启一个新的线程,用于和用于处理与客户端的通信。Socket在服务器端与客户端的通信过程如下:
- 服务器端创建一个用于连接的Socket,并绑定一个IP地址和端口;
- 服务器端开启监听,等待接收客户端连接;
- 客户端创建一个Socket,指明服务器的IP地址和端口;
- 服务器端监听到客户端连接,创建一个新的用于通信的Socket与客户端建立连接并进行数据传输;
- 服务器端用于连接的Socket保持继续监听;
如图所示,启动服务器后,开启多个客户端进行聊天
服务器端
'''
gethostbyname 返回的是 主机名 的IPv4 的地址格式,如果传入的参数是IPv4 的地址格式,则返回值跟
参数一样,这个函数不支持IPv6 的域名解析。
gethostbyname_ex 则是扩展后的接口,传入主机名,它能够返回 一个三元组 (原始主机名,域名列表,
IP地址列表),这个函数同样不支持IPv6的域名解析。
'''
import socket
import threading
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
mySocket.bind(('127.0.0.1', 5555))
mySocket.listen(128)
print('Server(%s) is listening' % socket.gethostbyname_ex('localhost')[2][0])
# 创建字典,用于存储客户端的用户
client = dict()
# 创建列表,用于存储客户端的连接
client_conn = list()
def chatMsgToOthers(exceptMe, chatMsg):
'''把聊天消息发送给除自己以外的所有人'''
for c in client_conn:
if c.fileno() != exceptMe:
try:
# 向客户端发送消息
c.send(chatMsg.encode())
except:
pass
def subThreading(client_socket, connNum):
'''与客户端连接的子线程的处理逻辑'''
username = client_socket.recv(1024).decode()
client[client_socket.fileno()] = username
client_conn.append(client_socket)
print('client connection number:', connNum, ' has nickname:', username)
chatMsgToOthers(connNum, '*系统提示:' + username + '已经进入聊天室,赶快和他(她)聊天吧*')
while True:
try:
# 接收客户端消息
recvedMsg = client_socket.recv(1024).decode()
if recvedMsg:
print(client[connNum], ':', recvedMsg)
chatMsgToOthers(connNum, client[connNum] + ':' + recvedMsg)
except (OSError, ConnectionResetError):
try:
client_conn.remove(client_socket)
except:
pass
print(client[connNum], 'was exit', len(client_conn), ' person left!')
chatMsgToOthers(connNum, "*系统提示:" + client[connNum] + '已经离开聊天室')
client_conn.close()
return
while True:
connection, ip_port = mySocket.accept()
print('Accept a new connection', connection.getsockname(), connection.fileno())
try:
# 接收客户端消息
buf = connection.recv(1024).decode()
if buf == '1':
connection.send(b'connection success, welcome to chat room')
myThread = threading.Thread(target=subThreading, args=(connection, connection.fileno()))
myThread.setDaemon(True)
myThread.start()
else:
# 向客户端发送消息
connection.send(b'connection failed, please go out!')
connection.close()
except:
pass
客户端
import socket
import threading
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 5555))
# 向服务器发送连接请求
sock.send(b'1')
print(sock.recv(1024).decode())
username = input('input your username:')
# 向服务器发送聊天用户名
sock.send(username.encode())
def sendThread():
'''向服务器端发送消息的处理逻辑'''
while True:
try:
msg = input('me:')
sock.send(msg.encode())
except ConnectionAbortedError:
print('Server closed this connection!')
except ConnectionResetError:
print('Server is closed')
def recvThread():
'''向服务端接收消息的处理逻辑'''
while True:
try:
otherMsg = sock.recv(1024)
if otherMsg:
print(otherMsg.decode())
else:
pass
except ConnectionAbortedError:
print('Server closed this connection')
except ConnectionResetError:
print('Server is closed')
send_task = threading.Thread(target=sendThread)
recv_task = threading.Thread(target=recvThread)
threads = [send_task, recv_task]
for t in threads:
t.setDaemon(True)
t.start()
t.join()