这个示例使用了线程来处理并发连接。
聊天服务器
import socket
import threading
# 创建一个TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置地址和端口
server_address = ('localhost', 12345)
server_socket.bind(server_address)
# 开始监听连接
server_socket.listen(5)
print("聊天服务器已启动,等待客户端连接...")
clients = [] # 保存所有客户端连接
# 处理客户端消息的函数
def handle_client(client_socket, client_address):
print(f"{client_address} 已加入聊天室")
while True:
try:
# 接收客户端发送的消息
message = client_socket.recv(1024).decode('utf-8')
if message:
print(f"{client_address}:{message}")
broadcast(message, client_socket)
else:
remove_client(client_socket)
break
except Exception as e:
print(f"客户端 {client_address} 发生错误:{e}")
remove_client(client_socket)
break
# 向所有客户端广播消息的函数
def broadcast(message, sender_socket):
for client in clients:
if client != sender_socket:
try:
client.sendall(message.encode('utf-8'))
except Exception as e:
print(f"发送消息给 {client.getpeername()} 失败:{e}")
remove_client(client)
# 移除客户端的函数
def remove_client(client_socket):
if client_socket in clients:
print(f"{client_socket.getpeername()} 已离开聊天室")
clients.remove(client_socket)
client_socket.close()
while True:
# 接受客户端连接
connection, client_address = server_socket.accept()
# 将新客户端添加到客户端列表
clients.append(connection)
# 创建一个线程来处理新客户端的消息
client_thread = threading.Thread(target=handle_client, args=(connection, client_address))
client_thread.start()
聊天客户端
import socket
import threading
# 创建一个TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到服务器
server_address = ('localhost', 12345)
client_socket.connect(server_address)
# 接收服务器消息的函数
def receive_messages():
while True:
try:
message = client_socket.recv(1024).decode('utf-8')
if message:
print(message)
else:
print("与服务器的连接已断开")
client_socket.close()
break
except Exception as e:
print(f"接收消息时发生错误:{e}")
client_socket.close()
break
# 创建一个线程来接收服务器的消息
receive_thread = threading.Thread(target=receive_messages)
receive_thread.start()
while True:
try:
# 从用户输入读取消息并发送给服务器
message = input()
if message:
client_socket.sendall(message.encode('utf-8'))
except Exception as e:
print(f"发送消息时发生错误:{e}")
client_socket.close()
break
要运行此示例,请先启动聊天服务器,然后启动一个或多个聊天客户端。服务器将接收来自客户端的消息,并将消息广播给其他所有客户端。客户端将显示来自服务器的消息。
这个示例展示了如何使用 sockets 和线程实现一个多用户聊天应用。可以根据自己的需求扩展此示例,例如添加身份验证、支持私人消息或者优化错误处理等。
sockets编程原理
套接字(sockets)编程是一种网络编程技术,用于实现计算机之间的通信。套接字提供了一种在不同计算机上运行的进程之间进行双向通信的机制。套接字的概念源于UNIX操作系统,现在它已经广泛应用于各种操作系统和编程语言中。下面我们来简要介绍套接字编程的原理。
-
套接字类型
常用的套接字类型有两种:流套接字(stream sockets)和数据报套接字(datagram sockets)。流套接字使用的是TCP(传输控制协议),提供可靠、有序、双向的字节流通信。数据报套接字使用的是UDP(用户数据报协议),提供不可靠、无连接的通信。
-
地址族
常见的地址族有两种:IPv4(AF_INET)和 IPv6(AF_INET6)。地址族决定了套接字使用的网络层协议。
-
套接字创建
套接字通过调用操作系统提供的 socket() 函数创建。创建套接字时,需要指定套接字类型和地址族。
-
服务器端套接字
服务器端套接字需要进行以下操作:
- 绑定:将套接字与服务器地址(IP地址和端口号)关联起来。这通过调用 bind() 函数实现。
- 监听:服务器套接字开始监听客户端连接请求。这通过调用 listen() 函数实现。
- 接受:当有客户端发起连接请求时,服务器通过调用 accept() 函数接受连接。accept() 函数返回一个新的套接字,用于与连接的客户端进行通信。
-
客户端套接字
客户端套接字需要进行以下操作:
- 连接:客户端通过调用 connect() 函数与服务器建立连接。connect() 函数需要指定服务器的地址(IP地址和端口号)。
-
数据传输
数据传输是通过 send()(或 sendall())和 recv() 函数实现的。send() 函数将数据发送到连接的另一端,而 recv() 函数从连接的另一端接收数据。
对于数据报套接字(UDP),可以使用 sendto() 和recvfrom() 函数发送和接收数据。 -
关闭套接字
当通信完成后,需要通过调用 close() 函数关闭套接字。这会释放套接字占用的资源。
套接字编程的原理基于 TCP/IP 协议栈的分层模型。套接字位于传输层,负责在不同计算机之间建立可靠或不可靠的通信连接。套接字编程使得开发者可以在应用层实现自定义的通信协议和服务。