概要
本文介绍几个能实现几台设备(一台server,若干台client)之间通信的python案例。
例1、socket库:顺序通信(1个server, 1个client)
server端和client端双向互相发送消息的情况(都为主动发送):
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import socket import time
HOST = '0.0.0.0' # 监听所有可用的接口 PORT = 8887 # 非特权端口号 max_client_num = 5 # 最大连接client数
# 创建服务器套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定服务器地址和端口 server_socket.bind((HOST,PORT))
# 监听连接 server_socket.listen(max_client_num) print('服务器已启动,等待客户端连接...')
while True: # 接受客户端连接 client_socket, client_address = server_socket.accept() print('客户端已连接:', client_address)
# 向已连接的客户端发送消息 message = 'server data: 123' message = message.encode() # string to byte client_socket.send(message) # 向特定的client发送 print('已向客户端发送消息')
# 向已连接的客户端发送消息 message = 'server data: 456' message = message.encode() # string to byte client_socket.send(message) print('已向客户端发送消息')
# 等待接收来自客户端的消息: print("等待接收客户端消息...") message = client_socket.recv(1024) message = message.decode() print("收到来自客户端的消息:",message) # 关闭客户端连接 client_socket.close() time.sleep(0.1)
|
client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import socket
HOST = '192.168.1.8' # 服务器IP地址 PORT = 8887 # 服务器开放的端口号
# 创建客户端套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器 client_socket.connect((HOST, PORT)) print('已经连接到服务器:'+repr((HOST, PORT)))
# 接收服务器消息1 print('等待服务器消息...') message = client_socket.recv(1024) # 等待,直到收到消息(byte格式) message = message.decode() # byte to string print('收到消息:', message)
# 接收服务器消息2 print('等待服务器消息...') message = client_socket.recv(1024) # 等待,直到收到消息(byte格式) message = message.decode() # byte to string print('收到消息:', message)
# 向服务器发送消息 print('向服务器发送消息...') message = "client data: 789" message = message.encode() # byte to string client_socket.send(message)
# 关闭客户端套接字 print('退出连接') client_socket.close()
# Notes: # server端的client_socket=server_socket.accept(): # client_socket.send()为server向client发送消息(被发送) # client_socket.recv()为server等待来自该client的消息(被接收)
# client端的client_socket(先connect(HOST,PORT)): # client_socket.send()为client向server发送消息(主动发送) # client_socket.recv()为本client等待来自server的消息(主动接收)
|
先在server端执行server.py,再在client端执行client.py,可以看到通信效果:
server端:
client端:
例2、socket库:顺序通信(1个server, 2个client)
1个server向2个client发送不同的消息,需要server自己维护client列表。
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import socket import time
HOST = '0.0.0.0' # 监听所有可用的接口 PORT = 8887 # 非特权端口号 max_client_num = 2 # 最大连接client数
# 创建服务器套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定服务器地址和端口 server_socket.bind((HOST,PORT))
# 监听连接 server_socket.listen(max_client_num) print('服务器已启动,等待客户端连接...')
# 维护客户端列表:[ip => socket obj] client_dict = {}
# 等待直到所有客户端连接成功: client_num = 0 while True: # 接受客户端连接 client_socket, client_address = server_socket.accept() client_ip = client_address[0] client_dict[client_ip] = client_socket print('客户端已连接:', client_ip) client_num += 1 if client_num >= max_client_num: break
# 提取ip_list和socket_list: ips = list(client_dict.keys()) client_sockets = list(client_dict.values())
# 向所有已连接的客户端发送消息: # 向client0 发送2,4,6,8,向client1发送1,3,5,7,9 for ii in range(10): message = 'server data: {}'.format(ii) message = message.encode() # string to byte ind = ii%2 client_sockets[ind].send(message) # 向特定的client发送 print('已向客户端{}发送消息:{}'.format(ind,message)) time.sleep(1)
# 关闭客户端连接 for client in client_sockets: client.close()
|
client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| # 注意:如果有多个客户端依次运行此代码,则后来先得:等其退出 后,前面的client才会接收
import socket import time
HOST = '192.168.1.8' # 服务器IP地址 PORT = 8887 # 服务器开放的端口号
# 创建客户端套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器 client_socket.connect((HOST, PORT)) print('已经连接到服务器:'+repr((HOST, PORT)))
# 接收服务器消息1 for ii in range(5): print('等待服务器消息...') message = client_socket.recv(1024) # 等待,直到收到消息(byte格式) message = message.decode() # byte to string print('收到消息:', message)
# 关闭客户端套接字 print('退出连接') client_socket.close()
|
先在server端执行server.py,再在2个client端都执行client.py,可以看到通信效果:
server端:(ip=192.168.1.8)
client[0]端:(ip=192.168.1.11)
client[1]端:(ip=192.168.1.9)
例3、tornado.websocket库:server端ioLoop异步监听client消息
server端开启ioLoop循环,被动监听来自client端的消息并作出回应。
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import tornado.web import tornado.websocket import tornado.ioloop
class WebSocketHandler(tornado.websocket.WebSocketHandler): client_list = {} # 维护所有clients的列表
def open(self): # 当新的client连接时,在server端自动创建一个对应的WebSocketHandler对象找表该client self.id = self.get_argument("id") # 从连接参数中获取客户端ID self.client_list[self.id] = self print("Client connected:", self.id)
def on_message(self, message): # 被动监听来自client的消息,触发本函数执行操作 print("Received message from client:", message) # 操作例子:向该客户端回发消息并double self.send_message_to_client(self.id, message + message)
def on_close(self): # 释放对象,并从client_list中移除 if self.id in self.client_list: del self.client_list[self.id] print("Client disconnected:", self.id)
def send_message_to_client(self, client_id, message): # 主动向client_id发送消息: if client_id in self.client_list: self.client_list[client_id].write_message(message)
if __name__ == "__main__": print("Hi! I'm server") app = tornado.web.Application([(r"/websocket", WebSocketHandler)]) app.listen(8887) # 监听8887端口(与client.py中一致) tornado.ioloop.IOLoop.current().start()
|
client.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import time import tornado.websocket from tornado import gen
# server address: server_ip_port = "100.10.100.00:8887" # 这里修改成你的server ip和已经开放的port
# client address: client_id = "jack001"
def send_message_to_server(): message = input("Enter message to send: ") client.write_message(message)
def send_message_to_specific_client(): client_id = input("Enter client ID to send message to: ") message = input("Enter message to send: ") client.write_message("SEND_TO:{}:{}".format(client_id, message))
@gen.coroutine def run(): # 连接到服务器: client = yield tornado.websocket.websocket_connect("ws://"+server_ip_port+"/websocket?id="+client_id) print("connected to server") # 发送消息:(每隔0.5秒发送一个string消息,并等待接收) ii = 1 while True: if ii % 3 == 1: msg = 'Msg-001' elif ii % 3 == 2: msg = 'Msg-002' else: msg = 'Msg-003' client.write_message(msg) print('send message to server: ' + msg) print('waiting for data from server...') msg2 = yield client.read_message() print("receive message from server: " + msg2) ii += 1 time.sleep(0.5) if __name__ == "__main__": print("Hi! I'm client") tornado.ioloop.IOLoop.current().run_sync(run)
|
先在server端执行server.py,再在client端执行client.py,可以看到通信效果:
server端:(最后一步手动断开client连接)
client端:(收到返回的double的消息)
附录
对于string类型的数据,可以直接用encode()/decode()编码解码,对于dictionary等其他类型,可以使用json再多一层封装:
1 2 3 4 5 6 7 8 9
| import json
data = {'key': 'value'}
encoded = json.dumps(data).encode() socket.send(encoded)
received = socket.recv(1024) decoded = json.loads(received.decode())
|