概要

本文介绍几个能实现几台设备(一台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())