一.关于中间人模式
在两台内网设备的通讯中,有时需要一台公网设备(提供公网IP)参与转发,此时,这台公网设备(服务器)充当“中间人”。
参考:(7条消息) 设计模式:中间人模式_leonhover的博客-优快云博客
二.我们的案例
在我们的案例中,两台内网设备我们称作client(客户端)和slave(从机),(我之所以以client和slave命名,是因为在我的项目中,client是一台遥控装置,而slave是被操控的设备)。在client和slave通信的过程中,需要一台公网服务器帮忙转发,同时要求client和slave在连接服务器是有密码验证。鉴此,我写了如下python脚本
#-*- coding:utf-8 -*-
import socket
import threading
PASSWORDA = "1234" #用不同的密码确定slave和client两个客户端的身份
PASSWORDB = "5678"
# global server_socket
# global slave_socket
class ClientThread(threading.Thread):
def __init__(self, con, addr):
super().__init__()
self.con = con
self.addr = addr
self.index = -1
def run(self):
try:
# 请求客户端输入密码
self.con.send("开始验证 ".encode('UTF-8'))
# 接收客户端输入的密码
input_password = self.con.recv(1024).strip().decode('UTF-8')
# 验证密码是否正确
if input_password == PASSWORDA:
if "client" in list(socket_list.keys()):
del socket_list["client"]
socket_list.update({"client":self.con})
global client_socket
client_socket = self.con
self.index = list(socket_list.keys()).index("client")
self.con.send("验证成功!".encode('UTF-8'))
print(f"验证成功,连接来自 {self.addr} !")
elif input_password == PASSWORDB:
if "slave" in list(socket_list.keys()):
del socket_list["slave"]
socket_list.update({"slave":self.con})
global slave_socket
slave_socket = self.con
self.index = list(socket_list.keys()).index("slave")
self.con.send("验证成功!".encode('UTF-8'))
print(f"验证成功,连接来自 {self.addr} !")
else:
print(f"验证失败,拒绝 {self.addr} !")
self.con.close()
while True:
# 接收客户端发送的消息
if len(socket_list) == 1:
self.con.send(("等待另一端连接").encode('UTF-8'))
while len(socket_list) == 1:
None
data = self.con.recv(1024)
if not data:
break
message = data.decode('UTF-8')
print(f"接收 {self.addr}: {message}")
socket_list[list(socket_list.keys())[1-self.index]].send(data)
print(f"丢失连接 {self.addr} !")
except Exception as e:
print(f"连接错误 {self.addr}: {e} !")
finally:
self.con.close()
try:
del socket_list[list(socket_list.keys())[self.index]]
except:
None
self.index=-1
host = ""
port = 1000
socket_list = {}
if __name__ == '__main__':
host_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host_socket.bind((host, port))
host_socket.listen(2)
while True:
con, addr = host_socket.accept()
client_thread = ClientThread(con, addr)
client_thread.start()
三.注意
在使用端口1000时,需要确保服务器防火墙打开1000端口,(如果是云服务器,配置好安全规则),以Ubuntu为例,打开1000端口:
sudo ufw allow 1000