缘起
本人由于胃不太好,从17年11月份离职一在家里养病.由于父母在家里养了些猪.所以有时,可能自己在房间用电脑做点东西.忽然要去猪圈里干话.一忙就是一上午或者一下午,结果忘记关电脑了.不是心痛电的问题,而是家里猪圈由于多年了,一些电路或者插线板老化了,就要停电去重新接.这样造成了忽然断电.对电脑硬盘损伤很大(已经报废了一个老硬盘了).本事自己安装一个teamviewer可以搞定的事,但是不知道为什么最近都是登录不进去(还有就是父母也不太会用).所以就想写一个通过手机可以控制电脑开关机的工具.还有一个问题,有时候孩子要在电脑看动画片或者他喜欢看的视频,本来是陪这他一起看,但是临时有事,出去了做了(如母猪产崽).结果时间长了孩子就过来找我.结果电脑就忘记关了.所以就更加迫使想做一个在手机上可以开关家里电脑的工具.可以在我自己我家人的手机上都安装一个.这样就可以不用在回到房间去关电脑了.只要拿出手机打开app几秒中就可以关闭家里的电脑省事.
设计分析
由于在局域网中手机是不知道每台电脑的IP,所以我们就用UDP广播的方式来确定每台开机的电脑的IP和MAC地址(要mac地址的目的就是为了开机用的)
然后说干就干C++写个WIN的简单UDP服务 核心代码如下:
C++代码
DWORD UDPService::DoTask() { sockSrv = socket( AF_INET , SOCK_DGRAM , 0 ) ; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY) ; addrSrv.sin_family = AF_INET ; addrSrv.sin_port = htons(Get_GSet()->GetUdpPort()) ; bind(sockSrv , (SOCKADDR*)&addrSrv , sizeof(SOCKADDR)); DWORD dwWaitRet = 3; while ( 1 ) { dwWaitRet = WaitForMultipleObjects(3, h_Events, FALSE, 100); if (dwWaitRet == WAIT_OBJECT_0 + 1) break; DoDispatcher(); } closesocket( sockSrv ); bIsRun = false; return 0; } void UDPService::DoDispatcher() { string strCmd; do { if(!GetRecvData(strCmd)) break; if(!Dispatcher(strCmd)) break; } while (0); } bool UDPService::GetRecvData(string &strCmd) { int len = sizeof(SOCKADDR); int recvSize = sizeof(recvBuf); ZeroMemory(recvBuf, recvSize); recvfrom(sockSrv,recvBuf, recvSize,0,(SOCKADDR*)&addrClient, &len); strFromIP = inet_ntoa(addrClient.sin_addr); strCmd = recvBuf; LOG_INFO(L"recvfrom[%s]: %s\n", C2W(strFromIP).c_str(), C2W(strCmd).c_str()); if(!GetCmdStrs(strCmd)) { LOG_ERR(L"获取命令错误!"); return false; } ZeroMemory(recvBuf, recvSize); strcpy(recvBuf, strCmd.c_str()); return strCmd.length() > 2; } int UDPService::SendData(const string &sendDataStr) { if (sendDataStr.length() GetCNameA(), Get_GSet()->GetURLA(), Get_GSet()->GetLOpenPort(), Get_GSet()->GetOpenPort(), Get_GSet()->GetClosePort() ); } else { sprintf_s(cmdBuffer,nBuffSize, "GetMacByIps Fial"); OpenSuccess = false; } }else if(nAt != string::npos) { //<shutdown:sToIp <reboot:sToIp do { string strCuuMac = strCmd.substr(nAt+1); string strCmd1 = strCmd.substr(0, nAt); ZeroMemory(cmdBuffer, nBuffSize); strcpy(cmdBuffer, strCmd1.c_str()); LOG_INFO(C2W(cmdBuffer).c_str()); if(!IpMgr.IsCuuPcByMac(strCuuMac)) { bNeedSend = false; sprintf_s(cmdBuffer, sizeof(cmdBuffer), "IpMgr.IsCuuPcByMac(%s) == false ", strCuuMac.c_str()); break; } if(stricmp(cmdBuffer, "shutdown") == 0) { if(SystemShutdown()) { strcpy(cmdBuffer, "shutdown windows success"); }else{ sprintf_s(cmdBuffer, sizeof(cmdBuffer), "shutdown windows failed! ,error code is: %d", GetLastError()); OpenSuccess = false; } }else if(stricmp(cmdBuffer, "reboot") == 0) { if(SystemShutdown(true)) { strcpy(cmdBuffer, "reboot windows success"); }else{ sprintf_s(cmdBuffer, sizeof(cmdBuffer), "reboot windows failed! ,error code is: %d", GetLastError()); OpenSuccess = false; } } } while (0); }else { sprintf_s(cmdBuffer, nBuffSize, "unkonw command [%s]", cmdBuffer); OpenSuccess = false; } string strData = cmdBuffer; if (OpenSuccess) LOG_INFO(C2W(strData).c_str()); else LOG_ERR(C2W(strData).c_str()); if(!bNeedSend) strData = ""; return SendData(strData); }
手机端的核心代码如下:
java代码
package vip.caipiao365.CloseAssistant; import android.os.Handler; import android.os.Message; import android.util.Log; import org.json.JSONObject; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.SocketTimeoutException; public class udpBroadCast extends Thread { static public Handler uiHandler; MulticastSocket sendSocket = null; DatagramPacket dj = null; InetAddress group = null; // 确定要发送的消息: String mes = ""; // 确定发送方的IP地址及端口号,地址为本地机器地址 int port = 8910; String host = "255.255.255.255";// 广播地址 int mType = 0; // 0获取信息 // 1 关机 2 重启 3开机 String sMac = ""; // 接受反馈数据的缓冲存储器 byte[] getBuf; // 接受类型的数据报 DatagramPacket getPacket; public udpBroadCast(int nType, String _sToIp, String _sMac, int _port) { mType = nType; host = _sToIp; port = _port; sMac = _sMac; // 确定接受反馈数据的缓冲存储器,即存储数据的字节数组 getBuf = new byte[2048]; // 创建接受类型的数据报 getPacket = new DatagramPacket(getBuf, getBuf.length); GetCmd(); } protected HistoryItem JS2HistoryItem(JSONObject jsObj) { HistoryItem _item = new HistoryItem(); _item.id = 0; try { _item.ip = jsObj.getString("I"); _item.mac = jsObj.getString("M"); _item.title = jsObj.getString("N"); _item.cname = jsObj.getString("D"); _item.url = jsObj.getString("U"); _item.oport = jsObj.getInt("O"); _item.port = jsObj.getInt("LO"); _item.cport = jsObj.getInt("C"); } catch (Exception e) { e.printStackTrace(); _item = null; } return _item; } private HistoryItem parseJSONData(String jsonData) { HistoryItem _item = null; try { JSONObject jsonObject = new JSONObject(jsonData); _item = JS2HistoryItem(jsonObject); } catch (Exception e) { e.printStackTrace(); _item = null; } return _item; } protected String GetData() { String strJson = ""; try { // 通过套接字接受数据 sendSocket.receive(getPacket); // 解析反馈的消息,并打印 strJson = new String(getBuf, 0, getPacket.getLength()); } catch (SocketTimeoutException e) { strJson = ""; } catch (Exception e) { e.printStackTrace(); strJson = ""; } return strJson; } protected void GetCmd() { switch (mType) { // 0获取信息 // 1 关机 2 重启 3开机 case 0: mes = ""; MainActivity.mainAct.histHandler.ReSetComputStatus(); break; case 1: mes = " 0) { ncount--; strJson = GetData(); if (strJson.length() < 2) { sleep(50); continue; } HistoryItem _item = null; if (mType == 0) { _item = parseJSONData(strJson); if (_item != null) { _item.starred = true; Message msg = new Message(); msg.what = 1; msg.obj = _item; uiHandler.sendMessage(msg); nCount++; } } else if (mType != 0) { Message msg = new Message(); msg.what = 6; msg.obj = strJson; uiHandler.sendMessage(msg); } } //sendSocket.leaveGroup(group); sendSocket.close(); } catch (SocketTimeoutException e) { } catch (Exception e) { e.printStackTrace(); } Message msg1 = new Message(); msg1.what = 4; msg1.arg1 = nCount; uiHandler.sendMessage(msg1); } }
本来设计的是从UDP获取的IP地址,然后在用TCP去连接发送关机重启指令,但是在我调试程序时候,有时候会报错误 No route to host.
想想UDP可以扫描有应答的报文,TCP链接有时候却抛这个异常.反正是局域网,而且数据量也不大就干脆用UDP广播得了.里面加上MAC地址,要是那台MAC地址配对上了就执行指令.
注 要是那位知道如何解决 No route to host.这个异常麻烦请指教
android端下载: https://gitee.com/zuzong/Projects/raw/master/CloseAssistant/android/CloseAssistant.apk
电脑端下载: https://gitee.com/zuzong/Projects/raw/master/CloseAssistant/PC/CloseAssistant.exe
详细说明: http://caipiao365.vip/CloseAssistant
csdn: https://blog.csdn.net/canye/article/details/79699906