使用ORAY蒲公英异地组网实现树莓派异地SSH访问
前情提要
- 上手了一块树莓派4B,希望能够在公司和家里都能调试开发;
- 公司和家里没有公网IP(所以说还是电信大法好),没办法实现公网访问树莓派;
- 虚拟机开发环境准备太麻烦,杀鸡用牛刀;
- 不愿意每天带着板子来回跑。
需求
- 上电开机
- 开机后检测是否联网(自动执行脚本);
- 确认网络连接成功后登录蒲公英(自动执行脚本);
- 联网后邮件发送局域网IP地址(自动执行python文件);
- 通过蒲公英实现异地组网访问树莓派。
准备项(重要)
- 树莓派开启ssh;
- 设置WiFi自动连接;
- 安装蒲公英并设置好自动登录;
- 智能插座。
正式开始
第一步:创建启动蒲公英服务脚本
在/home/pi/Desktop目录下创建启动蒲公英服务脚本pgyvpnservice.sh:
#!/bin/sh
#/etc/init.d/pgyvpnservice.sh
### BEGIN INIT INFO
# Provides:testboot
# Required-Start:$remote_fs $syslog
# Required-Stop:$remote_fs $syslog
# Default-Start:2 3 4 5
# Default-Stop:0 1 6
# Short-Description: testboot
# Description: This service is used to start my applaction
### END INIT INFO
case "$1" in
start)
echo "启动蒲公英异地组网"
su root -c "pgyvpn" # 以root模式执行pgyvpn指令打开蒲公英
;;
stop)
echo "执行完毕"
;;
esac
第二步:创建网络检测脚本
在/etc/init.d目录下创建网络检测脚本network_test.sh:
#!/bin/bash
#检测网络链接&&ftp上传数据
declare -i n=0 #创建变量n并声明为数值
while [ $n -ne 1 ] #判断n是否不等于1
do
ret_code=`curl -I -s --connect-timeout 5 baidu.com -w %{http_code} | tail -n1` #网络值
if [ "$ret_code" = "200" ]; then
nohup /home/pi/Desktop/pgyvpnservice.sh & #网络连接成功后需要启动的程序脚本,即pgyvpnservice.sh
n=1;
else
n=0; #失败等待
fi
done
第三步:配置开机启动项
实现开机检测网络连接,网络连接正常后运行蒲公英。
- 编辑rc.local文件:
sudo nano /etc/rc.local
- 在exit0前输入:
nohup /etc/init.d/network_test.sh & #后台启动网络检测脚本
第四步:自动将局域网IP地址发送至指定邮箱
目的:
- 当无法登录路由器查看分配的IP地址时,了解树莓派所在位置。
- 收到邮件即可确认树莓派已联网并顺利开启蒲公英,可以实现公网访问(这一步通过蒲公英控制台或客户端也可以实现,但通过邮件可以有效避免缓存等问题带来的困扰)。
- 编写ip_address.py,用于获取树莓派地址并发送邮件:
# -*- coding: utf-8 -*-
from multiprocessing import connection
import socket
import smtplib
import time
import socket
# 分割线内部分为网络检测,如在树莓派设置网络检测脚本,本部分可省略
# ------------------------------------------------------------------
# 测试网络连接情况
def isNetOK(testserver):
s=socket.socket()
s.settimeout(3)
try:
status = s.connect_ex(testserver)
if status == 0:
s.close()
return True
else:
return False
except Exception as e:
return False
# 测试百度是否可以访问
def isNetChainOK(testserver=('www.baidu.com',443)):
isOK = isNetOK(testserver)
return isOK
if __name__ == '__main__':
connection_attempt=True
while connection_attempt:
chinanet = isNetChainOK()
if chinanet==True:
break
# ------------------------------------------------------------------
# 邮件发送方邮箱地址
MAIL_USER = ''
# MAIL_PASS要使用授权码而非密码
MAIL_PASS = ''
# 邮箱stmp服务器地址
SMTP_SERVER = ''
SMTP_PORT = 25
# 邮件接收方邮箱地址
recipient1=''
# 邮件主题
subject1 = 'ip_address'
#获取IP地址
def get_host_ip():
try:
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.connect(('8.8.8.8',80))
ip = s.getsockname()[0]
finally:
s.close()
return ip
def send_mail(input_text):
text = input_text
smtpserver = smtplib.SMTP(SMTP_SERVER,SMTP_PORT)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
smtpserver.login(MAIL_USER,MAIL_PASS)
header = 'To:'+recipient1+'\n'+'From:'+MAIL_USER
header = header + '\n' +'Subject:' + subject1 +'\n'
timeStr = time.strftime(' %Y_%m_%d %H:%M:%S',time.localtime(time.time()))
msg = header +'\n'+text+'\n'+timeStr+'\n\n'
smtpserver.sendmail(MAIL_USER,recipient1,msg)
smtpserver.close()
time.sleep(10)
send_mail(get_host_ip())
- 将ip_address.py复制到/home/pi/Desktop。
- 将ip_address.py设置为开机启动:
# 编辑rc.local文件
sudo nano /etc/rc.local
# 在exit0前、nohup /etc/init.d/network_test.sh &后输入:
sudo python /home/pi/Desktop/ip_address.py
第五步:实现通过邮件向树莓派发送指令
- command_trans.py可通过邮件方式与树莓派终端进行有限的临时通讯,以备不时之需:
import os
import datetime
from calendar import TextCalendar
from cgitb import text
from operator import truediv
import poplib
import base64
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
from time import sleep
import smtplib
from email import encoders
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from unittest import result
def get_email_content():
useraccount = ''
password = ''
# 邮件服务器地址,以下为网易邮箱
pop3_server = 'pop.163.com'
# 开始连接到服务器
server = poplib.POP3(pop3_server)
# 打开或者关闭调试信息,为打开,会在控制台打印客户端与服务器的交互信息
server.set_debuglevel(0)
# 打印POP3服务器的欢迎文字,验证是否正确连接到了邮件服务器
# print(server.getwelcome().decode('utf8'))
# 开始进行身份验证
server.user(useraccount)
server.pass_(password)
# 返回邮件总数目和占用服务器的空间大小(字节数), 通过stat()方法即可
email_num, email_size = server.stat()
# print("消息的数量:{0}, 消息的总大小:{1}".format(email_num, email_size))
# 使用list()返回所有邮件的编号,默认为字节类型的串
rsp, msg_list, rsp_siz = server.list()
# print("服务器的响应: {0},\n消息列表: {1},\n返回消息的大小: {2}".format(rsp, msg_list, rsp_siz))
# print('邮件总数:{}'.format(len(msg_list)))
# 获取最新的一封邮件
total_mail_numbers = len(msg_list)
rsp, msglines, msgsiz = server.retr(total_mail_numbers)
# print("服务器的响应: {0},\n原始邮件内容: {1},\n该封邮件所占字节大小: {2}".format(rsp, msglines, msgsiz))
msg_content = b'\r\n'.join(msglines).decode('gbk')
# 邮件信息(未解码)
msg = Parser().parsestr(text=msg_content)
# print('解码后的邮件信息:\n{}'.format(msg))
# 关闭与服务器的连接,释放资源
server.close()
return msg
# 用来解析邮件主题
def parser_subject(msg):
subject = msg['Subject']
value, charset = decode_header(subject)[0]
if charset:
value = value.decode(charset)
# print('邮件主题:{0}'.format(value))
return value
# 用来解析邮件来源
def parser_address(msg):
hdr, addr = parseaddr(msg['From'])
# name 发送人邮箱名称, addr 发送人邮箱地址
name, charset = decode_header(hdr)[0]
if charset:
name = name.decode(charset)
# print('发送人邮箱地址: {0}'.format(addr))
return addr
def parser_content(msg):
content = msg.get_payload()
# 文本信息
content_charset = content[0].get_content_charset() # 获取编码格式
text = content[0].as_string().split('base64')[-1]
text_content = base64.b64decode(text).decode(content_charset) # base64解码
# 添加了HTML代码的信息
# content_charset = content[1].get_content_charset()
# text = content[1].as_string().split('base64')[-1]
# html_content = base64.b64decode(text).decode(content_charset)
# print('文本信息: {0}'.format(text_content))
return text_content
def send_email(command, execution_feedback):
# sender是邮件发送人邮箱,pw是服务器授权码,mail_host是服务器地址
sender = ''
pw = ''
mail_host = ''
# receivers是邮件接收人,用列表保存,可以添加多个
receivers = ['']
# 设置email信息
msg = MIMEMultipart()
time_stamp = '{0:%Y-%m-%d-%H-%M}'.format(datetime.datetime.now())
# 邮件主题
msg['Subject'] = command + ' -- Execution result -- '+time_stamp
# 发送方信息
msg['From'] = sender
# 邮件正文是MIMEText:
msg_content = execution_feedback
msg.attach(MIMEText(msg_content, 'plain', 'utf-8'))
# 登录并发送邮件
try:
#QQsmtp服务器的端口号为465或994
s = smtplib.SMTP_SSL(mail_host, 465)
s.set_debuglevel(1)
s.login(sender,pw)
#给receivers列表中的联系人逐个发送邮件
for item in receivers:
msg['To'] = to = item
s.sendmail(sender,to,msg.as_string())
print('Success!')
s.quit()
# print ("All emails have been sent over!")
except smtplib.SMTPException as e:
print ("Falied,%s",e)
if __name__ == '__main__':
# 返回解码的邮件详情
msg = get_email_content()
# 解析邮件主题
value=parser_subject(msg)
# 解析发件人详情
addr=parser_address(msg)
# 解析内容
text_content=parser_content(msg)
# 保证每次开机读取邮件时不会被已有邮件干扰
command_line=text_content
send_email('设备已开机,网络连接正常,已准备好通过邮件接收指令。', '最新一封邮件内容:'+text_content)
while True:
# 返回解码的邮件详情
msg = get_email_content()
# 解析邮件主题
value=parser_subject(msg)
# 解析发件人详情
addr=parser_address(msg)
# 解析内容
text_content=parser_content(msg)
if text_content!=command_line:
command_line=text_content
statement_execution = os.popen(command_line.replace('\n', '').replace('\r', ''))
feedback_line = statement_execution.read()
send_email(command_line,feedback_line)
else:
pass
# 每间隔5分钟进行一次邮件收发
sleep(300)
- 将command_trans.py复制到/home/pi/Desktop。
- 将command_trans.py设置为开机启动:
# 编辑rc.local文件
sudo nano /etc/rc.local
# 在exit0前、sudo python /home/pi/Desktop/ip_address.py &后输入:
sudo python /home/pi/Desktop/command_trans.py
第六步:上电开机,等待蒲公英服务上线
使用智能插座远程控制上电,上电后视网络情况等待一段时间,收到局域网IP地址邮件(推荐)或在蒲公英控制台/客户端上看到树莓派端已登录并能够ping通,即可通过蒲公英分配的IP地址进行公网访问。
注意事项
- 提前完成准备项内各项工作,蒲公英要保证自动登录能够执行,否则脚本无法开启该服务。
- 务必关注rc.local文件中启动项顺序,顺序错误可能导致后面启动项无法正常启动。
- 待补充。