最近遇到一个问题:实验室内部的网络是通过路由器分配IP的,但是经常又需要通过校园网远程实验室内部的电脑,而路由器的外网IP是由DHCP服务器动态分配的,IP地址无法绑定成静态的。RadminViewer远程的速度比较快,但是没办法穿墙,必须知道直连的IP地址,通过在实验室的路由器上设置转发端口,就可以实现实验室内部多台电脑同时远程。但是由于路由器上IP会变,自然想到在服务器上运行一个程序,每隔一段时间监测下路由器的IP,如果变化,就发送邮件通知。
使用Python编写,由于是一个后台的程序,自然想到要做出服务,就不会有窗口一直显示。将Python程序以Windows 服务方式启动,需要用到pywin32。
本来想实现可以获取每一层的IP,因为网络可能经过了多层的IP地址转换。但还不知道怎么做,最后参考了这里的方法后,目前是只能针对TP-Link的路由器获取外网IP,其他路由器没测试过。
后面还可以进一步扩展,实现很多功能,然后可以发邮件通知。使用的时候,需要先安装服务,然后再启动。服务安装后,默认是手动启动,如果需要设置为自动启动,还需要到Windows管理工具,服务设置中,将该服务设置为自动启动。
在开发过程中,可能需要不断调试以检测是否有bug,因此可以使用调试模式,Service debug,这样可以看到print输出的内容,用于测试服务是否能正常运行。
以下是代码
1 #-*- encoding: utf-8 -*-
2
3 #Service install 安装
4 #Service start 启动
5 #Service stop 停止
6 #Service debug 调试
7 #Service remove 删除
8
9 import win32serviceutil
10 import win32service
11 import win32event
12 import smtplib
13 import time, traceback
14 import threading
15 import logging
16 import win32evtlogutil
17
18 class Service(win32serviceutil.ServiceFramework):
19 _svc_name_ = "IPDetector"
20 _svc_display_name_ = "IPDetector"
21 _svc_description_ = "Detect the change status of router IP, and send mail to notify user."
22 _svc_deps_ = ["EventLog"]
23 _sleep_time_ = 20 * 60 #时间以秒为单位
24 _username_ = 'admin'#路由器用户名
25 _password_ = 'admin'#路由器密码
26 _routerIP_ = '192.168.1.1'#路由器内部IP
27 _mail_list_ = [
28 "mail1@qq.com",
29 "mail2@qq.com"
30 ]
31 _currentIP_ = ''
32
33 def __init__(self, args):
34 win32serviceutil.ServiceFramework.__init__(self, args)
35 self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
36 print u'Service is running...'
37
38 def SvcDoRun(self):
39 import servicemanager
40 timer = threading.Timer(self._sleep_time_, self.process())
41 timer.start()
42 # Write a 'started' event to the event log...
43 win32evtlogutil.ReportEvent(self._svc_name_,
44 servicemanager.PYS_SERVICE_STARTED,
45 0, # category
46 servicemanager.EVENTLOG_INFORMATION_TYPE,
47 (self._svc_name_, ''))
48 # wait for beeing stopped...
49 win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
50
51 # and write a 'stopped' event to the event log.
52 win32evtlogutil.ReportEvent(self._svc_name_,
53 servicemanager.PYS_SERVICE_STOPPED,
54 0, # category
55 servicemanager.EVENTLOG_INFORMATION_TYPE,
56 (self._svc_name_, ''))
57 return
58
59 def SvcStop(self):
60 # Before we do anything, tell SCM we are starting the stop process.
61 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
62 # And set my event
63 win32event.SetEvent(self.hWaitStop)
64 return
65
66 def send_mail(self, mail_list, msg):
67 try:
68 handle = smtplib.SMTP('smtp.163.com', 25)
69 handle.login('mail@163.com','password')
70 for mail in mail_list:
71 send_msg = "To:" + mail + "\r\nFrom:mail@163.com\r\nSubject: The latest router IP \r\n\r\n"\
72 + msg +"\r\n"
73 handle.sendmail('mail@163.com', mail, send_msg)
74 handle.close()
75 return True
76 except:
77 print traceback.format_exc()
78 return False
79
80 def getRouterPublicIP(self, username, password, routerIP):
81 # this provide a way to get public ip address from tp-link
82 import httplib, re, base64
83 showErrorMessage = 0
84
85 # 192.168.1.1
86 conn = httplib.HTTPConnection(routerIP)
87 # set request headers
88 headers = {"User-Agent": "python host",
89 "Content-type": "application/x-www-form-urlencoded",
90 "Authorization": "Basic %s" % base64.encodestring('%s:%s' % (username, password))[:-1],
91 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
92 "Accept-Language": "zh-cn,zh;q=0.5",
93 "Accept-Encoding": "gzip, deflate",
94 "Accept-Charset": "GB2312,utf-8;q=0.7,*;q=0.7",
95 "Connection": "keep-alive"}
96
97 # get status page
98 conn.request("GET", "/userRpm/StatusRpm.htm", "", headers)
99 response = conn.getresponse()
100 keyword = re.search(' wanPara [^\)]*?\)', response.read())
101 response.close()
102 conn.close()
103
104 # search the public ip address
105 found = 0
106 publicIP = ""
107 if keyword:
108 arr = re.findall('([\d]*?,)|(\"[^\"]*?\",)', keyword.group(0))
109 if arr:
110 if len(arr) > 3:
111 publicIP = re.search('(?<=\")[^\"]*?(?=\")', arr[2][1])
112 if publicIP:
113 publicIP = publicIP.group(0)
114 found = 1
115
116 if found == 1:
117 return publicIP
118 else:
119 if showErrorMessage == 1:
120 logging.info('router public ip address not found.')
121 #print "router public ip address not found."
122
123 def process(self):
124 latestIP = self.getRouterPublicIP(self._username_, self._password_, self._routerIP_)
125 logging.info('the latest router ip is: ' + latestIP)
126 #print 'the latest router ip is: ' + latestIP
127 if self._currentIP_ != latestIP:
128 _currentIP_ = latestIP
129 msg = u'The latest router IP is: ' + str(_currentIP_)
130 print time.strftime('%Y-%m-%d %X',time.localtime(time.time()))
131 if self.send_mail(self._mail_list_, msg):
132 logging.info('send mail success')
133 #print 'send mail success'
134 else:
135 #print 'send mail failed'
136 logging.info('send mail failed')
137
138 if __name__ == '__main__':
139 win32serviceutil.HandleCommandLine(Service)
这是另外一个版本,用于检测所在机器的本地IP和外网IP。
需要发送通知的邮件地址,需要以每行一个保存到当前目录下的mail.txt中,如
mail1@.gmail.com
mai2.@gmail.com
最新的本地IP地址会保存在latestIP.txt中。
IPDetector.py
1 #-*- encoding: utf-8 -*-
2 '''
3 Created on 13-5-31
4 '''
5
6 import smtplib
7 import time, traceback, sys, os
8 from email.mime.text import MIMEText
9
10 class IPDetector():
11 def __init__(self):
12 pass
13
14 def send_mail(self, subject, content):
15 try:
16 handle = smtplib.SMTP('smtp.163.com', 25)
17 handle.login('mail@163.com', 'password')
18 mail_list = self.getMailList()
19 time_str = time.strftime('%Y-%m-%d %X', time.localtime(time.time()))
20 msg = '<html><body>' + content + "<br><br><span style='color:#999;font-size:"\
21 + "10px;font-family:Verdana;'>" \
22 + time_str + " by servme</span>"+'</body></html>'
23 send_msg = MIMEText(msg, 'html', 'utf-8')
24 send_msg['Subject'] = subject
25
26 for mail in mail_list:
27 handle.sendmail('servme@163.com', mail, send_msg.as_string())
28 handle.close()
29 return True
30 except:
31 print traceback.format_exc()
32 return False
33
34 #读写最新IP信息的文件
35 def operIPFile(self, mode, data):
36 #获取脚本路径
37 path = sys.path[0]
38 #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,
39 #如果是py2exe编译后的文件,则返回的是编译后的文件路径
40 if os.path.isfile(path):
41 path = os.path.dirname(path)
42
43 if mode == 'read':
44 file = open(path + u'\latestIP.txt')
45 line = file.readline()
46 file.close()
47 return line
48 else:
49 file = open(path + u'\latestIP.txt', 'w')
50 file.write(data)
51
52 def getMailList(self):
53 mailList = []
54 #获取脚本路径
55 path = sys.path[0]
56 #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,
57 #如果是py2exe编译后的文件,则返回的是编译后的文件路径
58 if os.path.isfile(path):
59 path = os.path.dirname(path)
60 file = open(path + u'\mail.txt')
61 while 1:
62 line = file.readline()
63 if not line:
64 break
65 mailList.append(line)
66
67 file.close()
68 return mailList
69
70 def getLocalPCIP(self):
71 import socket
72 localIP = socket.gethostbyname(socket.gethostname()) #得到本地ip
73 print localIP
74
75 import re, urllib2
76 #获取外网IP
77 class GetExtIP:
78 def getIP(self):
79 try:
80 extIP = self.visit("http://www.ip138.com/ip2city.asp")
81 except:
82 try:
83 extIP = self.visit("http://www.bliao.com/ip.phtml")
84 except:
85 try:
86 extIP = self.visit("http://www.whereismyip.com/")
87 except:
88 extIP = "So sorry!!!"
89 return extIP
90
91 def visit(self, url):
92 opener = urllib2.urlopen(url)
93 if url == opener.geturl():
94 str = opener.read()
95 else:
96 str = ''
97 return re.search('\d+\.\d+\.\d+\.\d+', str).group(0)
98
99 externalIP = GetExtIP().getIP()
100 print externalIP
101 return localIP, externalIP
102
103 #取本地IP
104 def process(self):
105 localIP, externalIP = self.getLocalPCIP()
106 currentIP = self.operIPFile('read', None)
107 if currentIP != localIP:
108 self.operIPFile('write', localIP)
109 time_str = time.strftime('%Y-%m-%d %X', time.localtime(time.time()))
110 import socket
111 hostname = socket.gethostname()
112 print hostname
113 content = 'Host Name: '+ hostname + '<br>' \
114 + 'Local IP address: ' + localIP + '<br>' \
115 + 'External IP address: ' + externalIP + '<br>'
116 subject = "The IP address of " + hostname + " has Changed"
117 if self.send_mail(subject, content):
118 print time_str + ' send mail success'
119 else:
120 print time_str + ' send mail failed'
121 else:
122 print 'The IP address is same with the last detection'
123
124 if __name__=='__main__':
125 ipDetector = IPDetector()
126 sleep_time = 20 * 60 #时间以秒为单位
127 while True:
128 ipDetector.process()
129 time.sleep(sleep_time)
IPService.py
1 #-*- encoding: utf-8 -*-
2 '''
3 Created on 13-6-1
4 '''
5 #IPService install 安装
6 #IPService start 启动
7 #IPService stop 停止
8 #IPService debug 调试
9 #IPService remove 删除
10
11 import win32serviceutil
12 import win32service
13 import win32event
14 import threading
15 import win32evtlogutil
16 import time
17
18 class Service(win32serviceutil.ServiceFramework):
19 _svc_name_ = "IPDetector"
20 _svc_display_name_ = "IPDetector"
21 _svc_description_ = "Detect the change status of IP, and send mail to notify user."
22 _svc_deps_ = ["EventLog"]
23 _sleep_time_ = 20 * 60 #时间以秒为单位
24
25 def __init__(self, args):
26 win32serviceutil.ServiceFramework.__init__(self, args)
27 self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
28 print 'Service is running...'
29
30 def SvcDoRun(self):
31 # import servicemanager
32 # from IPDetector import IPDetector
33 # ipDetector = IPDetector()
34 # timer = threading.Timer(self._sleep_time_, ipDetector.process())
35 # timer.start()
36 #
37 # # Write a 'started' event to the event log...
38 # win32evtlogutil.ReportEvent(self._svc_name_,
39 # servicemanager.PYS_SERVICE_STARTED,
40 # 0, # category
41 # servicemanager.EVENTLOG_INFORMATION_TYPE,
42 # (self._svc_name_, ''))
43 #
44 # # wait for beeing stopped...
45 # win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
46 #
47 # # and write a 'stopped' event to the event log.
48 # win32evtlogutil.ReportEvent(self._svc_name_,
49 # servicemanager.PYS_SERVICE_STOPPED,
50 # 0, # category
51 # servicemanager.EVENTLOG_INFORMATION_TYPE,
52 # (self._svc_name_, ''))
53
54 #----------------------------------------------------------------
55 import servicemanager
56 # Make entry in the event log that this service started
57 servicemanager.LogMsg(
58 servicemanager.EVENTLOG_INFORMATION_TYPE,
59 servicemanager.PYS_SERVICE_STARTED,
60 (self._svc_name_, '')
61 )
62 from IPDetector import IPDetector
63 ipDetector = IPDetector()
64 # Set an amount of time to wait (in milliseconds) between runs
65 self.timeout = 100
66 while 1:
67 # Wait for service stop signal, if I timeout, loop again
68 rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
69 # Check to see if self.hWaitStop happened
70 if rc == win32event.WAIT_OBJECT_0:
71 # Stop signal encountered
72 break
73 else:
74 # Put your code here
75 ipDetector.process()
76 time.sleep(self._sleep_time_)
77 # Only return from SvcDoRun when you wish to stop
78 return
79 #-----------------------------------------------------------------
80
81 def SvcStop(self):
82 # Before we do anything, tell SCM we are starting the stop process.
83 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
84 # And set my event
85 win32event.SetEvent(self.hWaitStop)
86
87 if __name__ == '__main__':
88 win32serviceutil.HandleCommandLine(Service)