Python-socket网络编程(二)–基于 TCP实现聊天功能
在Python-socket网络编程(一)中介绍的socket的基本使用
在了解了基本使用后,考虑实现一个点到点的小聊天工具
基本思路:
服务器:用来监听客户端,当客户端发送消息时,接收客户端消息并显示,同时发送消息给客户端
客户端:连接服务器,发送消息给服务器,并接收服务器返回的消息
TCP服务器代码:
from socket import *
from time import ctime
# 地址和端口
HOST = ''
PORT = 21567
BUFFER_SIZE = 1024
ADDR = (HOST, PORT)
tcp_socket_server = socket(AF_INET, SOCK_STREAM) # 创建服务器套接字
tcp_socket_server.bind(ADDR) # 套接字与地址绑定
tcp_socket_server.listen(5) # 监听连接, 其中参数为最大等待数
# 服务器无限循环等待连接
while True:
print('waiting for connection....')
tcp_client_server, addr = tcp_socket_server.accept() # 接受客户端连接
print('... connect from: ', addr)
# 通信循环
while True:
data = tcp_client_server.recv(BUFFER_SIZE) # 接受客户端发送的消息
print('客户端消息:', data.decode('utf-8'))
if not data:
break
message = input('> ')
tcp_client_server.send(message.encode('utf-8')) # 客户端发送消息
tcp_client_server.close() # 关闭客户端套接字
tcp_socket_server.close() # 关闭服务器套接字(可选)
TCP客户端代码:
from socket import *
HOST = '127.0.0.1'
PORT = 21567
PORT2 = 6666
BUFFER_SIZE = 1024
ADDR = (HOST, PORT)
tcp_client_server = socket(AF_INET, SOCK_STREAM) # 创建客户端套接字
tcp_client_server.bind((HOST, PORT2))
tcp_client_server.connect(ADDR) # 尝试连接服务器
address = tcp_client_server.getsockname() # 返回当前套接字的地址
print('当前套接字的地址:', address)
# 通信循环
while True:
msg = input("> ")
# if msg == "quit":
# break
# data = str({"msg": msg, "target_address": ('127.0.0.1', 7777), "owner_address": address})
tcp_client_server.send(msg.encode('utf-8')) # 客户端发送消息
rev_data = tcp_client_server.recv(BUFFER_SIZE) # 客户端接受消息
if not rev_data:
break
print('服务器消息:', rev_data.decode('utf-8'))
tcp_client_server.close() # 关闭客户端连接
运行发现,可以实现基本的收发功能,但是会发现,当客户端发送消息后,就会处于阻塞状态,持续等待服务器返回消息,同样服务器也是如此。而且这两段代码实现的只是客户端和服务器两点之间的通信,效果不是很理想。做个简单的升级。
升级思路:
服务器:
- 能够支持两个客户端进行通信
- 负责转发两个客户端之间的消息
客户端:
- 通过服务器向指定客户端发送消息
代码如下:
TCP服务器
from socket import *
from threading import Thread
HOST = ''
PORT = 21567
BUFFER_SIZE = 1024
ADDR = (HOST, PORT)
# 连接池
g_conn_pool = dict()
def accept_link():
"""
接受连接,并且处理消息转发
:return:
"""
while True:
print('wait connection....')
client, addr = tcp_server_socket.accept()
g_conn_pool[addr] = client
print(f'client {addr} connect')
thread = Thread(target=message_handler, args=(client, addr))
thread.setDaemon(True)
thread.start()
def message_handler(client, addr):
"""
消息转发的逻辑
:param client:
:param addr:
:return:
"""
while True:
data = client.recv(BUFFER_SIZE).decode('utf-8')
print(f'客户端 {addr} 发送的消息:', data)
data_dict = eval(data)
message = data_dict['msg']
addr = data_dict['target_address']
send_addr = data_dict['owner_address']
send_handler(g_conn_pool[addr], send_addr, message)
def send_handler(client, send_addr, message):
print("发送消息")
client.send(b'%s: %s' % (str(send_addr).encode('utf-8'), message.encode('utf-8'))) # 客户端发送消息
if __name__ == '__main__':
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
tcp_server_socket.bind(ADDR)
tcp_server_socket.listen(5)
accept_link()
TCP客户端1 代码:
from socket import *
from threading import Thread
HOST = '127.0.0.1'
PORT = 21567
PORT2 = 7777
BUFFER_SIZE = 1024
ADDR = (HOST, PORT)
def send_message(tcp_client_server, address):
while True:
msg = input('')
data = str({"msg": msg, "target_address": ('127.0.0.1', 6666), "owner_address": address})
tcp_client_server.send(data.encode('utf-8'))
def receive_message(tcp_client_server):
# 接受消息
while True:
receive_data = tcp_client_server.recv(BUFFER_SIZE) # 客户端接受消息
print(receive_data.decode('utf-8'))
def main():
# 创建对应客户端的套接字
tcp_client_server = socket(AF_INET, SOCK_STREAM) # 创建客户端套接字
tcp_client_server.bind((HOST, PORT2))
tcp_client_server.connect(ADDR) # 尝试连接服务器
address = tcp_client_server.getsockname() # 返回当前套接字的地址
print('当前套接字的地址:', address)
thread = Thread(target=send_message, args=(tcp_client_server, address))
thread.setDaemon(True)
thread.start()
recv_thread = Thread(target=receive_message, args=(tcp_client_server, ))
recv_thread.setDaemon(True)
recv_thread.start()
thread.join()
# 关闭套接字
tcp_client_server.close()
if __name__ == '__main__':
main()
TCP客户端2代码:
from socket import *
from threading import Thread
HOST = '127.0.0.1'
PORT = 21567
PORT2 = 6666
BUFFER_SIZE = 1024
ADDR = (HOST, PORT)
def send_message(tcp_client_server, address):
while True:
msg = input('')
data = str({"msg": msg, "target_address": ('127.0.0.1', 7777), "owner_address": address})
tcp_client_server.send(data.encode('utf-8'))
def receive_message(tcp_client_server):
# 接受消息
while True:
receive_data = tcp_client_server.recv(BUFFER_SIZE) # 客户端接受消息
print(receive_data.decode('utf-8'))
def main():
# 创建对应客户端的套接字
tcp_client_server = socket(AF_INET, SOCK_STREAM) # 创建客户端套接字
tcp_client_server.bind((HOST, PORT2))
tcp_client_server.connect(ADDR) # 尝试连接服务器
address = tcp_client_server.getsockname() # 返回当前套接字的地址
print('当前套接字的地址:', address)
# 发送消息
thread = Thread(target=send_message, args=(tcp_client_server, address))
thread.setDaemon(True)
thread.start()
# 接收消息
recv_thread = Thread(target=receive_message, args=(tcp_client_server,))
recv_thread.setDaemon(True)
recv_thread.start()
recv_thread.join()
# 关闭套接字
tcp_client_server.close()
if __name__ == '__main__':
main()