一. 開發背景:
- 孩子玩電腦游戲,我要用電腦
- 老婆用電腦看電視劇,我要用電腦 ( 哎,電腦怎么關機了啊,看來是不想給你用)
- 本來准備再擼一會兒代碼,結果躺在床上玩手機不想下來了,所以..
已經存在了很多實現了該功能的軟件,為什么要自己寫?
就是想自己寫,有源碼就感覺安全,沒有那么多別的功能,另外方便自己擴展,比如上傳孩子手機位置信息,東西不保存到第三方服務器
為什么不用不用Http ,通過Http協議通信,定義2個API接口不就得了?
開機就啟動Tomcat 需要消耗幾十M內存,另外我需要雙工,這個以后說
為什么不用應用程序設置開機啟動而使用Window服務?
怕不加入白名單360給提示那就尷尬了,另外啟動項里面看到也不好,而服務里面的內容比較多,一般人懶得看,服務更穩定
二. 功能描述:
2.1 服務端:
開機啟動,接收手機發送過來的消息根據類別進行處理
2.2 客戶端:
一個確定關機 和取消關機按鈕,一個設置延遲時間的文本框
一個圖標顯示程序是否正常的連接到服務器端,2秒鍾刷新一次
一個設置服務端IP跟端口 並且可以保存的界面;
三. 別人能夠拿來用的:
-
日志記錄 Logger
-
通用Window服務幫助類 (安裝 卸載 檢查服務)
-
關機批處理 CmdHelper
-
防火牆
把應用程序添加到防火牆白名單中 (注意代碼中這個路徑,我在這里繞了幾圈)
四. 運行環境以及使用說明:
4.1 電腦: C#
(.netframework 4.6.1 可降 需要管理員權限 )
第一步: 點開 Release 文件夾下面的 PhoneControl.exe.config 修改其中的 <add key="IPEndpoint" value="192.168.3.16:1337" /> ,把value換成你自己的
第二步: 點擊運行 Release 文件夾下面的 PhoneControl.exe,第一次安裝按1 服務端操作結束 (我設置了延遲時間,需要等待10秒才能安裝完成,並不是程序運行慢,設置延遲的原因看上一篇文章)

4.2 Android:
Java API 15 Android 4.0.3以上 需要網絡權限
到下面的地址安裝phonecontrol-debug.apk ,完成后再界面上會出現一個非常原始的圖標,MainActivty,出現下圖:

第一步: 先把服務端的IP和端口填寫上去,完成后點擊設置
第二步: 修改多久后關機,並點擊關機按鈕
4.3 應用下載:
若有不能正常運行的把報錯信息發到評論里面
五. 源碼展示:
5.1 服務端:
/// <summary> /// 應用程序的主入口點。 /// </summary> static void Main(string[] args) { //判斷是否已經存在日志記錄目錄 若不存在以第一次運行目錄做為日志記錄 Window服務運行目錄會存在不同 string l_strLogPath = System.Configuration.ConfigurationManager.AppSettings["LogPath"]; if (string.IsNullOrEmpty(l_strLogPath)) { //記錄程序當前目錄 Configuration cfa = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); cfa.AppSettings.Settings["LogPath"].Value = Environment.CurrentDirectory; cfa.Save(); } //添加應用程序目錄到防火牆規則列表里面 try { FireWareHelper fireWareHelper = new FireWareHelper(); // string l_strPath = Path.Combine(Environment.CurrentDirectory, "PhoneControl.exe"); fireWareHelper.RemoveRule(Application.ExecutablePath); //這里的目錄只能用 Application.ExecutablePath 而不能用 l_strPath 雖然你輸出的時候這兩個地址一樣 到Window服務里面就不一樣 fireWareHelper.AddRule(Application.ExecutablePath, "PhoneControl"); } catch (Exception e) { Logger.CreateErrorLog(e); } #if DEBUG Logger.WriteAndShowLog("程序啟動"); StartClass.StartMain(); return; #endif string l_strServiceName = System.Configuration.ConfigurationManager.AppSettings["ServiceName"]; //帶參啟動運行服務 if (args.Length > 0) { try { ServiceBase[] serviceToRun = new ServiceBase[] {new WindowService()}; ServiceBase.Run(serviceToRun); } catch (Exception ex) { Logger.CreateErrorLog(ex); } } //不帶參啟動配置程序 else { StartLable: Console.WriteLine("請選擇你要執行的操作——1:自動部署服務,2:安裝服務,3:卸載服務,4:驗證服務狀態,5:退出"); Console.WriteLine("————————————————————"); ConsoleKey key = Console.ReadKey().Key; if (key == ConsoleKey.NumPad1 || key == ConsoleKey.D1) { if (ServiceHelper.IsServiceExisted(l_strServiceName)) { ServiceHelper.ConfigService(l_strServiceName, false); } if (!ServiceHelper.IsServiceExisted(l_strServiceName)) { ServiceHelper.ConfigService(l_strServiceName, true); } ServiceHelper.StartService(l_strServiceName); goto StartLable; } else if (key == ConsoleKey.NumPad2 || key == ConsoleKey.D2) { if (!ServiceHelper.IsServiceExisted(l_strServiceName)) { ServiceHelper.ConfigService(l_strServiceName, true); } else { Console.WriteLine("\n服務已存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad3 || key == ConsoleKey.D3) { if (ServiceHelper.IsServiceExisted(l_strServiceName)) { ServiceHelper.ConfigService(l_strServiceName, false); } else { Console.WriteLine("\n服務不存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad4 || key == ConsoleKey.D4) { if (!ServiceHelper.IsServiceExisted(l_strServiceName)) { Console.WriteLine("\n服務不存在......"); } else { Console.WriteLine("\n服務狀態:" + ServiceHelper.GetServiceStatus(l_strServiceName).ToString()); } goto StartLable; } else if (key == ConsoleKey.NumPad5 || key == ConsoleKey.D5) { } else { Console.WriteLine("\n請輸入一個有效鍵!"); Console.WriteLine("————————————————————"); goto StartLable; } } }
public static void StartMain() { try { string amqEndpoint = System.Configuration.ConfigurationManager.AppSettings["IPEndpoint"].ToString(); System.Net.IPAddress IPadr = System.Net.IPAddress.Parse(amqEndpoint.Split(':')[0]); System.Net.IPEndPoint EndPoint = new System.Net.IPEndPoint(IPadr, int.Parse(amqEndpoint.Split(':')[1])); //傳遞IPAddress和Port using (var listener = new SocketListener(EndPoint)) // Start listening { Logger.WriteAndShowLog("啟動監聽....."); for (;;) { using (var remote = listener.Accept()) // Accepts a connection (blocks execution) { var data = remote.Receive(); // Receives data (blocks execution) Method(data); // remote.Send("命令已經執行!"); } } } } catch (System.Exception ex) { Logger.CreateErrorLog(ex); Console.ReadLine(); } }
5.2 Android 客戶端:
public void btn_CloseComputer(View view) { //獲取IP跟端口信息 //把信息顯示到界面上 SharedPreferences sPreferences = getSharedPreferences("config", MODE_PRIVATE); final String l_strIp = sPreferences.getString(ConstantModel.ip, ""); final String l_strEndPoint = sPreferences.getString(ConstantModel.endPoint, "1337"); new Thread() { @Override public void run() { super.run(); try { //1.創建監聽指定服務器地址以及指定服務器監聽的端口號 Socket socket = new Socket( l_strIp ,Integer.parseInt(l_strEndPoint) ); //2.拿到客戶端的socket對象的輸出流發送給服務器數據 OutputStream os = socket.getOutputStream(); //寫入要發送給服務器的數據 MessageModel model = new MessageModel(); model.setMessageType(1); //獲取時間 EditText editText = findViewById(R.id.txt_Seconds); String message = editText.getText().toString(); model.setDataContent(message); Gson gson = new Gson(); String jsonObject = gson.toJson(model); String s1 = new String(jsonObject.getBytes(), StandardCharsets.UTF_8); os.write(s1.getBytes()); os.flush(); socket.shutdownOutput(); //拿到socket的輸入流,這里存儲的是服務器返回的數據 InputStream is = socket.getInputStream(); //解析服務器返回的數據 int lenght = 0; byte[] buff = new byte[1024]; final StringBuffer sb = new StringBuffer(); while ((lenght = is.read(buff)) != -1) { sb.append(new String(buff, 0, lenght, StandardCharsets.UTF_8)); } runOnUiThread(new Runnable() { @Override public void run() { //這里更新UI } }); //3、關閉IO資源(注:實際開發中需要放到finally中) is.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }.start(); }
public void btn_Setting(View view) { /** * SharedPreferences將用戶的數據存儲到該包下的shared_prefs/config.xml文件中, * 並且設置該文件的讀取方式為私有,即只有該軟件自身可以訪問該文件 */ SharedPreferences sPreferences = this.getSharedPreferences("config", MODE_PRIVATE); SharedPreferences.Editor editor = sPreferences.edit(); //當然sharepreference會對一些特殊的字符進行轉義,使得讀取的時候更加准確 TextView txt_EndPoint = findViewById(R.id.txt_Port); String l_strPort = txt_EndPoint.getText().toString(); editor.putString(ConstantModel.endPoint, l_strPort); IPEditText ipEditText = findViewById(R.id.ip_text); String IP = ipEditText.getText(this); editor.putString(ConstantModel.ip, IP); editor.commit(); Toast.makeText(this, "設置成功!", Toast.LENGTH_LONG) .show(); }
5.3源碼下載 :
服務端源碼地址保存七天:
鏈接:https://pan.baidu.com/s/14hm8bM4Qx7ZwyxhxRLlWkw
提取碼:41n5
Android 客戶端源碼地址:
https://gitee.com/maanshan1/Android-PhoneControl.git
