Python 编写 Windows 服务,实时发送动态IP至手机

    最近TeamViewer不能正常使用(检测到商业用途,无法启动新的会话;又想骗我去用商业付费版),家里宽带的公网IP是动态分配的,远程很不方便。为摆脱被不能远程支配的恐惧,我使用python编写了一个脚本来获取动态的IP地址,并利用windows服务来管理。

在这里碰到了一些坑,一起来了解一下。

(这里也可以不用创建windows服务,直接设置任务计划定期执行脚本就行,只是执行的时候会有一个小黑窗弹出来,可以将脚本打包为exe文件并屏蔽黑窗)

创建Windows服务

1.python实现windows服务需要借助第三方库pywin32

pip install pywin32

2.这里先来一段简单的创建服务的代码(Create_WindowsService.py),暂不涉及功能

import win32serviceutil
import win32service
import win32event
import time


class PythonService(win32serviceutil.ServiceFramework):
    _svc_name_ = "Send IP"                              # 服务名
    _svc_display_name_ = "Send IP"                      # 服务在windows系统中显示的名称
    _svc_description_ = "Send My Computer IP to me"     # 服务的描述

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.run = True

    def SvcDoRun(self):
        while self.run:
            # 功能函数()
            time.sleep(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.run = False
        
        
if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(PythonService) 
    这段代码中PythonService类继承了win32serviceutil.ServiceFramework类,初始化后系统服务开始启动,并调用函数SvcDoRun。由于属性self.run为真,因此SvcDoRun函数不会停止,直到我们触发停止服务,调用SvcStop函数,属性self.run被赋予假,循环跳出,windows服务停止。我们需要实现的功能也就放在该循环中。

3.这里是一些创建服务命令,当然我们也可以安装后直接在windows服务里进行操作

#1.安装服务
python Create_WindowsService.py install

#2.让服务自动启动
python Create_WindowsService.py --startup auto install 

#3.启动服务
python Create_WindowsService.py start

#4.重启服务
python Create_WindowsService.py restart

#5.停止服务
python Create_WindowsService.py stop

#6.删除/卸载服务
python Create_WindowsService.py remove

4.这里可能会遇到一些问题,简单说下我遇到的一些

    (1)首先,在cmd下操作以上命令,如安装服务 python Create_WindowsService.py install时,报错:拒绝服务

    

    这是因为cmd需要管理员权限(我使用的不是administrator用户),很简单,开始——cmd——右键 以管理员身份运行

    

    (2)出现 “1063 服务没有及时响应启动或控制请求” 类型的错误

    将安装目录Python3\Lib\site-packages\win32路径下的pythonservice.exe注册一下。

    这需要win32目录下有pywintypes36.dll文件,或者有该文件的环境变量

    复制Python36\Lib\site-packages\pywin32_system32\pywintypes36.dll到Python3\Lib\site-packages\win32下

    最后,注册命令:pythonservice.exe /register

    可能会报sys.winver数据类型的错误,不会有影响,可以查看下该值,是字符型(版本号)

    

实现获取动态IP并发送至手机

    这里采用的是把获取的IP发送到手机钉钉(也可以短信或邮件、微信等),以方便随时查看。

1.使用钉钉创建一个群聊或者团队,添加自定义机器人,复制机器人地址,之后调用api接口就可以通过机器人发送信息。

def message(text):
    headers = {'Content-Type': 'application/json;charset=utf-8'}
    api_url = "你的机器人webhok地址"
    json_text = {
        "msgtype": "text",
        "at": {
            "atMobiles": [“可以添加你的手机号,机器人会@你”],
            "isAtAll": False
        },
        "text": {
            "content": text
        }
    }
    response_code = requests.post(api_url, json.dumps(json_text), headers=headers).status_code
    print(response_code)

2.获取你的公网IP地址

def get_public_ip():
    url = "https://ip.cn/"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers)
        flag_num = 0
        public_ip = None
        while flag_num < 3:
            if response.status_code == 200:
                public_ip = re.search('\d+\.\d+\.\d+\.\d+', str(response.text)).group()
                break
            else:
                flag_num += 1
        return public_ip
    except Exception as e:
        return None

3.定义一个属性,获取并赋予其公网ip值,如果ip变化,则更新类变量的值(每次服务停止后,该属性会重新初始化)

def __init__(self, args):
    …………
    # 定义属性
    self.local_ip = None

4.下面给出完整代码(最新升级版)

import datetime
import requests
import re
import json
import win32serviceutil
import win32service
import win32event
import time


def get_public_ip():
    url = "https://ip.cn/"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers)
        flag_num = 0
        public_ip = None
        while flag_num < 3:
            if response.status_code == 200:
                public_ip = re.search('\d+\.\d+\.\d+\.\d+', str(response.text)).group()
                break
            else:
                flag_num += 1
        return public_ip
    except Exception as e:
        return None


def message(text):
    headers = {'Content-Type': 'application/json;charset=utf-8'}
    api_url = "https://oapi.dingtalk.com/robot/send?" \
              "access_token=你的机器人webhok地址"
    json_text = {
        "msgtype": "text",
        "at": {
            "atMobiles": [],
            "isAtAll": False
        },
        "text": {
            "content": text
        }
    }
    try:
        response_code = requests.post(api_url, json.dumps(json_text), headers=headers).status_code
        print(response_code)
    except Exception as e:
        print("Send Message to DingDing failed!")
        print(e)


class PythonService(win32serviceutil.ServiceFramework):
    _svc_name_ = "Send IP"  # 服务名
    _svc_display_name_ = "Send IP"  # 服务在windows系统中显示的名称
    _svc_description_ = "Send My Computer IP to me"  # 服务的描述

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.run = True
        self.local_ip = None

    def SvcDoRun(self):
        while self.run:
            try:
                t_number = datetime.datetime.now().strftime('%M%S')
                # 每小时0分、30分检测一次ip变化
                if t_number == "0000" or t_number == "3000":
                    IP = get_public_ip()
                    if self.local_ip != IP:
                        self.local_ip = IP
                        message(IP)
            except Exception as e:
                print(e)
            time.sleep(1)  # 不加此延迟会占用很高的cpu

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.run = False


if __name__ == '__main__':
    try:
        win32serviceutil.HandleCommandLine(PythonService)
    except Exception as e:
        print(e)

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值