近日为了满足2个屋子里人的通信需要,需要将本地的外网IP告诉给另一个屋子里的人。以前都是在群里喊一句,我这里的IP是XXXX,有一天自己突发奇想,想通过程序来实现这个自动化通知的过程。于是自己给自己定了需求。
需求:基本目标实现开机将本地的路由WAN口IP通知对方。
思考:如果才能实现自动通知呢?
首先,要有一个公共的空间,可以作为通知的载体。
其次,需要传输机制把通知发出去。
经过思考,我想到了电子邮件,邮箱是每个人都有的,email是可以用程度发的。于是需求变成了通过发邮件的方式把最新获取到的IP发出来。解决的思路有 了,最大的问题是如何获取外网IP,开始尝试获取本地IP,这个显然是没用的,因为机器是在内网环境,获取到的只是内网IP,后来试图想通过获取外网 IP,比如访问一个XXX网站的形式来获取IP,这种网站还真不少,但是获取到的都是公网IP,学过网络的人都知道公网IP很少,往往只是在网络节点上的 IP,这种IP对我一个ADSL用户来说几乎就没任何意义。其实我需要的只是路由的WAN口IP。网上搜了一下,也没找到合适的。在几乎绝望的时候,我想 到了代理,我能不能通过访问路由器的方式来获取IP呢,我试图用telnet登陆路由,试图用路由命令来操作路由,结果和我想象的一样,家用路由毕竟不是 服务器级别的路由,根本没有对外提供的访问命令,只能通过web的方式来实现对路由的设置。自己再次陷入了绝望,后来我打开fireBug,试图找到IP 那个查看IP跳转的页面。结果被我找到了,http://192.168.1.1/userRpm/StatusRpm.htm,点开页面查看响应,传过 来的就是网页的部分信息。OK,这就是我想要的IP。我突然兴奋起来,但是这似乎还不够,因为每次登陆路由时要输入用户名和密码。网上搜搜,这个其实不 难,代理服务程序就那样写的。经过一晚上的思考和实践WAN口IP算是被我搞出来了。
package com.eehome.app.mail.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.eehome.app.mail.IMailConstant;
import com.eehome.app.mail.model.RouterPassAuth;
/**
* @author wensong
* 2010-9-4 下午03:34:58
*/
public class RouteIpUtils {
private final static RouteIpUtils routeIpUtils = new RouteIpUtils();
private RouteIpUtils() {
}
public static RouteIpUtils getInstance() {
//验证器工具的实例进行注册
Authenticator.setDefault(new RouterPassAuth());
return routeIpUtils;
}
public String getRouteIp() throws IOException {
StringBuffer wanPacket = getWanPacket();
return getFirstIp(wanPacket);
}
/**
* 获得路由Web中的状态页面上的数据
*
* @return
* @throws IOException
*/
private StringBuffer getWanPacket() throws IOException {
URL url = new URL(IMailConstant.ROUTE_WEB_STATE);
InputStream ins = null;
try {
ins = url.openConnection().getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
String str;
boolean flag = false;
StringBuffer wanPacket = new StringBuffer();
int num = 3;
while ((str = reader.readLine()) != null && num > 0) {
if (str.contains("var wanPara = new Array(")) {
flag = true;
}
if (flag) {
wanPacket.append(str);
num--;
}
}
return wanPacket;
}finally{
if(ins!=null){
ins.close();
}
}
}
private String getFirstIp(StringBuffer wanPacket) {
// 找出数据包中第一个匹配的IP,即为Ip
Pattern p = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
Matcher m = p.matcher(wanPacket);
if (m.find()) {
return m.group();
} else {
return null;
}
}
public static void main(String[] args) {
try {
System.out.println(RouteIpUtils.getInstance().getRouteIp());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.eehome.app.mail;
public interface IMailConstant {
/**
* 发件人地址
*/
public final static String NOTIFY_EMAIL_MESSAGE_FROM = "[email protected]";
/**
* 提醒邮件标题前缀
*/
public final static String NOTIFY_EMAIL_MESSAGE_PRE_TITLE = "通知:服务器IP提醒————";
/**
* 路由用户名
*/
public final static String ROUTE_USER = "admin";
/**
* 路由密码
*/
public final static String ROUTE_PWD = "huang";
/**
* 路由Web记录状态的页面,里面包含了Wan口ip
*/
public final static String ROUTE_WEB_STATE = "http://192.168.1.1:83/userRpm/StatusRpm.htm?Connect=连 接&wan=1";
/**
* 轮询时间 10分钟
*/
public final static long POLLING_TIME = 10 * 60 * 1000;
/**
* email文件地址
*/
public final static String EMAIL_FILE_PATH = "/root/mail/mail.txt";
/**
* log4j存放地址
*/
public final static String LOG4J_FILE_PATH = "/root/mail/log/log4j.properties";
/**
* 匹配email的正则
*/
public final static String EMAIL_REG = "[A-Za-z0-9](?:[0-9a-zA-Z_]?\\.?){4,24}@[0-9a-zA-Z_-]{1,59}\\.(?:[0-9a-zA-Z]\\.?[0-9a-zA-Z]?){2,3}";
}
package com.eehome.app.mail.model;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import com.eehome.app.mail.IMailConstant;
public class RouterPassAuth extends Authenticator {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(IMailConstant.ROUTE_USER,
IMailConstant.ROUTE_PWD.toCharArray());
}
}
考虑到路由IP是动态IP,过一段时间就会变化,我给程序设计了一个轮询机制来检测IP,变化,用一个调度任务来定时获取IP,进行比较,再决定是否发送邮件。
package com.eehome.app.mail.task;
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.javamail.JavaMailSender;
import com.eehome.app.mail.IMailConstant;
import com.eehome.app.mail.model.IpNotifyMessage;
import com.eehome.app.mail.utils.RouteIpUtils;
/**
* 邮件提醒任务
* @author wensong
* 2010-9-5 下午11:12:17
*/
public class NotifyEmailSendTask implements Runnable {
private String[] toMails;
private Log logger = LogFactory.getLog(this.getClass());
private static String ip = "192.168.1.110";
static {
PropertyConfigurator.configure(IMailConstant.LOG4J_FILE_PATH);
}
private static BeanFactory factory = new ClassPathXmlApplicationContext(
"application-context.xml");
public NotifyEmailSendTask(String[] toMails) {
this.toMails = toMails;
}
public void run() {
String routerIp = null;
try {
routerIp = RouteIpUtils.getInstance().getRouteIp();
} catch (IOException e1) {
logger.warn("连接路由中断,或者路由连接异常:" + e1);
}
try {
if (routerIp != null&&!routerIp.equals("0.0.0.0")) {
if (!routerIp.equals(ip)) {
if (!ip.equals("192.168.1.110"))
logger.info("路由IP发生变化,执行发送邮件任务");
StringBuffer context = new StringBuffer().append(
"今日Host:\n").append("eehome.com ")
.append(routerIp);
if (!ip.equals("192.168.1.110")) {
context = new StringBuffer().append("Host变化如下:\n")
.append("eehome.com ").append(routerIp);
}
// 构造邮件消息对象
IpNotifyMessage message = new IpNotifyMessage(toMails,
context.toString());
// 发邮件
JavaMailSender mailSender = (JavaMailSender) factory
.getBean("javaMailSender");
logger.info("发送邮件,发送邮箱为:" + Arrays.toString(toMails));
mailSender.send(message);
ip = routerIp;
} else {
logger.debug("上次记录的IP为:" + ip + ", 最新路由IP为:" + routerIp
+ " 轮询结果将不发送邮件");
}
}
} catch (Exception e) {
logger.warn("发送邮件异常:" + e);
}
}
}
package com.eehome.app.mail.task;
import java.util.concurrent.TimeUnit;
import com.eehome.app.mail.IMailConstant;
import com.eehome.app.mail.utils.EmailsProvider;
import com.eehome.core.task.EeHomeScheduledExecutor;
/**
* 邮件任务调度,采用轮询的方式来检查路由IP是否发生改变,如果改变发送邮件
*
* @author wensong 2010-9-4 下午03:34:58
*/
public class IpNotifyEmailSchedule {
public static void main(String[] args) {
String[] mails = EmailsProvider.getInstance().getEmailArrays();
EeHomeScheduledExecutor.getScheduledExecutor().scheduleWithFixedDelay(
new NotifyEmailSendTask(mails), 0, IMailConstant.POLLING_TIME,
TimeUnit.MILLISECONDS);
}
}
package com.eehome.app.mail.model;
import java.util.Date;
import org.springframework.mail.SimpleMailMessage;
import com.eehome.app.mail.IMailConstant;
import com.eehome.app.mail.utils.DateUtils;
public class IpNotifyMessage extends SimpleMailMessage {
private static final long serialVersionUID = -3236307360187426650L;
public IpNotifyMessage(String toMail, String toText) {
// 设置邮件标题:提醒邮件标题前缀+当前时间
setSubject(IMailConstant.NOTIFY_EMAIL_MESSAGE_PRE_TITLE
+ DateUtils.getTimeNow());
setFrom(IMailConstant.NOTIFY_EMAIL_MESSAGE_FROM);
setSentDate(new Date());
setTo(toMail);
setText(toText);
}
public IpNotifyMessage(String[] toMails,String toText){
// 设置邮件标题:提醒邮件标题前缀+当前时间
setSubject(IMailConstant.NOTIFY_EMAIL_MESSAGE_PRE_TITLE
+ DateUtils.getTimeNow());
setFrom(IMailConstant.NOTIFY_EMAIL_MESSAGE_FROM);
setSentDate(new Date());
setTo(toMails);
setText(toText);
}
}
package com.eehome.app.mail.exception;
public class FileIsNotExistException extends Exception {
/**
* 文件不存在异常
*/
private static final long serialVersionUID = -2545287258814097123L;
public FileIsNotExistException() {
super();
}
public FileIsNotExistException(String message, Throwable cause) {
super(message, cause);
}
public FileIsNotExistException(String message) {
super(message);
}
public FileIsNotExistException(Throwable cause) {
super(cause);
}
}
package com.eehome.app.mail.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import com.eehome.app.mail.IMailConstant;
public class EmailsProvider {
private final static EmailsProvider provider = new EmailsProvider();
private Log logger = LogFactory.getLog(this.getClass());
static {
PropertyConfigurator.configure(IMailConstant.LOG4J_FILE_PATH);
}
private EmailsProvider() {
}
public static EmailsProvider getInstance() {
return provider;
}
/**
* 获得Email数组
*
* @return
*/
public String[] getEmailArrays() {
StringBuilder sb = getEmailStrBuilder();
if (sb != null) {
List mailArrays = getEmails(sb);
return (String[]) mailArrays.toArray(new String[mailArrays.size()]);
} else {
return null;
}
}
/**
* 从指定email配置中获取email
*
* @return
*/
private StringBuilder getEmailStrBuilder() {
File file = new File(IMailConstant.EMAIL_FILE_PATH);
InputStream ins = null;
StringBuilder stringBuilder = new StringBuilder();
try {
ins = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(
ins));
String str;
while ((str = reader.readLine()) != null) {
stringBuilder.append(str).append(",");
}
return stringBuilder;
} catch (FileNotFoundException e) {
logger.info(file.getPath() + "文件不存在");
return null;
} catch (IOException e) {
logger.info("文件操作异常:" + e);
return null;
} finally {
try {
ins.close();
} catch (IOException e) {
logger.info("文件关闭异常:" + e);
return null;
}
}
}
/**
* 通过正则匹配字符
* @param stringBuilder
* @return
*/
private List getEmails(StringBuilder stringBuilder) {
List mails = new ArrayList();
// 创建正则表达式
Pattern p = Pattern.compile(IMailConstant.EMAIL_REG);
Matcher m = p.matcher(stringBuilder);
if (logger.isDebugEnabled()) {
logger.debug("文件读取的字符串stringBuilder:" + stringBuilder);
}
// 找到匹配的字符串
while (m.find()) {
mails.add(m.group());
}
return mails;
}
}
package com.eehome.app.mail.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtils {
public static final SimpleDateFormat sdf = new SimpleDateFormat(
"yyyyMMddHHmmss");
/**
* 获得当前时间组成的字符串,格式为yyyyMMddHHmmss
* @return
*/
public static String getTimeNow() {
return sdf.format(new Date());
}
}
package com.eehome.app.mail.exception;
public class FileIsNotExistException extends Exception {
/**
* 文件不存在异常
*/
private static final long serialVersionUID = -2545287258814097123L;
public FileIsNotExistException() {
super();
}
public FileIsNotExistException(String message, Throwable cause) {
super(message, cause);
}
public FileIsNotExistException(String message) {
super(message);
}
public FileIsNotExistException(Throwable cause) {
super(cause);
}
}
接着为了写了一个shell脚本,把它作为一个后台执行的脚本,放到rc.local下,让其开机启动,这样IP邮件提醒的程序大功告成,呵呵。