有些同窗電腦總是要出問題,但又不是什麼大問題,一般幾句cmd就能搞定。以前解決方案有2:一是遠程演示,我口述別人操做;一是我寫個cmd腳本,但畢竟不在本機很差調試。(吐槽一下經常使用的遠程控制實在是難用至極)windows
解決:遠程cmd,可以實時的反饋執行結果,帶寬佔用少緩存
windows10, Microsoft Visual C# 2010 Express, 資料都是百度的!服務器
一、終端應該作成能在全部能聯網的設備上跑,而不是內網版網絡
二、假設終端只在內網上運行,那麼一個終端須要知道另外一個終端的ip和端口(可經過廣播,但麻煩了)tcp
三、作成廣域網版使用什麼技術(主要指消息是經過轉發仍是打洞),最後選擇轉發,由於打洞我還不是很會 -_-''ide
四、終端應該區分控制端和被控制端?不用,軟件內部功能區分就好了;若是終端分開那麼服務器端還要作識別,就設計麻煩了測試
五、先就這麼多吧...優化
一、服務器開啓後可接受2個客戶端鏈接,不區分【控制端】或【被控制端】,只作消息轉發(提升效率);只有當兩端都連上後才能傳輸消息,當只有一端存在時該端發送消息會收到服務器提示 "另外一端不在線"spa
二、客戶端進入時選擇是【控制端】仍是【被控制端】,連上後提示另外一端是否鏈接上服務器線程
三、【控制端】向【被控制端】發送控制命令,【控制端】將收到的消息(期待收到的回顯)顯示
四、【被控制端】等待命令,收到後執行並將回顯顯示於該端,最後將回顯發送回【控制端】
1 const int PORT = 58888; //Server PORT 2 const string HOST = "127.0.0.1"; //Server IP 3 static TcpClient tcp = new TcpClient(); 4 static bool isAlive = false; //鏈接標識 5 static bool s=false; //控制端標識,須要傳入參數[u999]進入 6 7 //包中第一字節爲0x04解析爲聊天消息,不然爲控制消息;別問爲何用0x04,問就是緣分 8 9 static void Main(string[] args) 10 { 11 //控制端判斷 12 if(args.Length!=0) 13 if(string.Compare(args[0],"u999")==0) //控制端 14 { 15 Console.Write("Welcome, controller."); 16 s=true; 17 } 18 else 19 Console.WriteLine("Hello loser."); //想爆破密碼的? 20 try{ 21 tcp.Connect(HOST, PORT); //鏈接阻塞 22 isAlive = true; 23 Console.WriteLine("-------已鏈接=" + tcp.Client.RemoteEndPoint + "------"); 24 }catch(Exception e){ 25 Console.WriteLine(e.Message+"\n任意鍵退出..."); 26 Console.ReadKey(); 27 return; 28 } 29 30 if(!s) //被控制端 31 { 32 //發送線程 33 ThreadStart ts=new ThreadStart(sendToCtrl); 34 Thread th=new Thread(ts); 35 th.Start(); 36 37 Process p = new Process(); 38 p.StartInfo.FileName = "cmd.exe"; //程序名 39 p.StartInfo.UseShellExecute = false; //不使用程序外殼 40 p.StartInfo.RedirectStandardInput = true; //重定向輸入 41 p.StartInfo.RedirectStandardOutput = true; //重定向輸出 42 p.StartInfo.RedirectStandardError = true; //錯誤 43 p.StartInfo.CreateNoWindow = true; //建立窗口 44 p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //訂閱輸出 45 p.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //錯誤輸出 46 p.Start(); 47 p.BeginOutputReadLine(); //程序開始後開始訂閱 48 p.BeginErrorReadLine(); 49 50 byte[] recvBuf = new byte[1024]; 51 string command = ""; 52 int recvNum; 53 while(!p.HasExited) //循環執行 54 { 55 recvNum = 0; 56 try{ 57 recvNum = tcp.Client.Receive(recvBuf); //服務等待阻塞 58 }catch(Exception){ //斷開鏈接 59 break; 60 } 61 if(recvNum == 0) //主動斷開:空消息 62 break; 63 if(recvBuf[0]!=0x04) //控制消息 64 { 65 command = Encoding.UTF8.GetString(recvBuf, 0, recvNum); //接收遠程命令 66 p.StandardInput.WriteLine(command); //執行 67 } 68 else 69 Console.WriteLine(Encoding.UTF8.GetString(recvBuf,1,recvNum-1)); 70 Thread.Sleep(100); //檢測exit命令,while循環條件檢查 71 } 72 Console.WriteLine("-------End.------\n任意鍵退出..."); 73 //先關程序,再關鏈接 74 p.Close(); 75 p = null; 76 tcp.Client.Close(); 77 tcp.Close(); 78 tcp=null; 79 } 80 else //控制端 81 { 82 //接收線程 83 ThreadStart ts = new ThreadStart(recvMsg); 84 Thread th = new Thread(ts); 85 th.Start(); 86 87 string input = ""; 88 while(true) //循環發送 89 { 90 //這裏也輸入的是什麼消息,反正都當控制消息吧(區分要解析輸入) 91 input = Console.ReadLine(); 92 if(isAlive) 93 tcp.Client.Send(Encoding.UTF8.GetBytes(input+'\n')); 94 else 95 break; 96 Thread.Sleep(100); 97 } 98 } 99 Console.ReadKey(); //這一句沒什麼卵用但我不想刪 100 } 101 102 //控制端回顯消息 103 static void recvMsg() 104 { 105 byte[] recvBuf = new byte[8192]; 106 int recvNum; 107 while(true) //循環接收 108 { 109 recvNum = 0; 110 try{ 111 recvNum = tcp.Client.Receive(recvBuf); 112 }catch(Exception){ 113 break; 114 } 115 if(recvNum == 0) 116 break; 117 //以上爲TCP斷開判斷 118 119 //如下爲何要分開寫呢?由於合起來麻煩了... 120 if(recvBuf[0]!=0x04) //被控端回顯 121 Console.Write(Encoding.UTF8.GetString(recvBuf,0, recvNum)); 122 else //聊天消息 123 Console.WriteLine(Encoding.UTF8.GetString(recvBuf,1, recvNum-1)); 124 } 125 tcp.Client.Close(); 126 tcp.Close(); 127 isAlive = false; 128 Console.WriteLine("-------Server down------"); 129 } 130 131 //被控端消息發送 132 static string output = ""; 133 static void sendToCtrl() 134 { 135 int outputFlag = 0; //被控端超時發送標記 136 string outputNow = ""; 137 byte[] sendBytesT = null, sendBytes=null; 138 while(true) //循環檢測緩衝池 139 { 140 Thread.Sleep(50); 141 if(!isAlive || output.Length == 0) //未鏈接 或 無緩存數據 142 continue; 143 if(output.Length > 4096 || outputFlag > 2) //緩存滿4k 或 超時傳送150ms 144 { 145 outputNow = output; 146 output = ""; 147 148 //這段可能有點low, 望大佬告知該怎麼寫 149 sendBytesT = Encoding.UTF8.GetBytes(outputNow); 150 sendBytes = new byte[sendBytesT.Length + 1]; 151 sendBytes[0] = 0x04; 152 sendBytesT.CopyTo(sendBytes, 1); 153 154 try{ 155 tcp.Client.Send(sendBytes); 156 }catch(Exception){ 157 break; 158 } 159 outputFlag = 0; 160 continue; 161 } 162 ++outputFlag; 163 } 164 } 165 166 //被控端輸出入池 167 static void p_OutputDataReceived(object sender, DataReceivedEventArgs e) 168 { 169 if(!string.IsNullOrEmpty(e.Data)) 170 { 171 Console.WriteLine(e.Data); 172 output += e.Data + '\n'; 173 }
1 //服務器端不區分控制端被控制端,只做數據轉發 2 const int PORT = 58888; //Server PORT 3 static TcpClient[] tcp=new TcpClient[2]; 4 static int alive=0; //二進制位標識該終端是否在線=00|01|10|11 5 static void Main() 6 { 7 //消息線程 8 ParameterizedThreadStart ts=new ParameterizedThreadStart(user); 9 10 TcpListener listen=new TcpListener(IPAddress.Any,PORT); 11 TcpClient tcpT=null; 12 while(true) //循環監聽 13 { 14 if(alive==3) //兩個終端==11 15 { 16 Thread.Sleep(4000); 17 continue; 18 } 19 listen.Start(1); 20 Console.WriteLine("Listen="+PORT); 21 tcpT=listen.AcceptTcpClient(); //監聽阻塞 22 Console.WriteLine("Accept="+tcpT.Client.RemoteEndPoint); 23 if(alive<2) //==00|01 24 { 25 tcp[0]=tcpT; 26 alive|=2; 27 Thread th=new Thread(ts); 28 th.Start(0); 29 } 30 else //==10,不可能==11(當==11時兩個線程都在消息循環中) 31 { 32 tcp[1]=tcpT; 33 alive|=1; 34 Thread th=new Thread(ts); 35 th.Start(1); 36 } 37 listen.Stop(); 38 } 39 Console.ReadKey(); 40 } 41 42 //處理一個終端,傳入本段代碼控制的tcp序號==0|1 43 static void user(object index0) 44 { 45 int index=(int)index0; 46 if(index!=0 && index!=1) //非法驗證 47 return; 48 byte[] sendBytesT=Encoding.UTF8.GetBytes(alive==3?"Another is connected.\n":"Only you, wait.\n"); 49 byte[] sendBytes=new byte[sendBytesT.Length+1]; 50 sendBytes[0]=0x04; 51 sendBytesT.CopyTo(sendBytes,1); 52 tcp[index].Client.Send(sendBytes); 53 54 byte[] recvBuf = new byte[8192]; 55 int recvNum; 56 while(true) //接收[轉發]消息 57 { 58 recvNum=0; 59 try{ 60 recvNum = tcp[index].Client.Receive(recvBuf); 61 }catch(Exception){ 62 break; 63 } 64 if(recvNum == 0) 65 break; 66 //以上爲TCP斷開判斷 被控制端斷開 67 68 //Console.WriteLine(recvNum+","+Encoding.UTF8.GetString(recvBuf,0,recvNum)); 69 if(alive==3) //兩端在線 70 { 71 byte[] sendBuf=new byte[recvNum]; 72 Array.Copy(recvBuf,sendBuf,recvNum); 73 tcp[index==0?1:0].Client.Send(sendBuf); 74 } 75 else //一端不在線 76 { 77 sendBytesT=Encoding.UTF8.GetBytes("Another is disconnected.Ctrl+C to EXIT.\n"); 78 sendBytes=new byte[sendBytesT.Length+1]; 79 sendBytes[0]=0x04; 80 sendBytesT.CopyTo(sendBytes,1); 81 tcp[index].Client.Send(sendBytes); 82 } 83 } 84 tcp[index].Client.Close(); 85 tcp[index].Close(); 86 tcp[index]=null; 87 alive&=index==0?1:2; 88 }
一、服務器只容許兩個終端鏈接應該能知足通常使用場景,至少能解決開始敘述的問題
二、客戶端開始那個密碼區分控制端的東西,其實沒多大用,編譯出來的exe我用16進制打開都能找到 "密碼",但願會密碼學的大神不要噴,我確實不大會
三、【被控制端】那段調用cmd的代碼網上很多,略有借鑑...
四、【被控制端】消息回顯爲何不直接發送回【控制端呢】?由於【被控制端】每輸出一行就會產生一個輸出重定向,若是每個重定向都直接使用網絡傳送,那麼將致使網絡負荷過大(我猜的),因此根據網絡原理我優化了發送界定爲:150ms的超時傳送 或 4k的超限傳送
五、服務器端能夠作成多終端鏈接,但消息轉發/羣發會麻煩,可改進用於一個終端控制多個終端,考慮使用特徵編號之類的識別控制羣
六、消息前加不可打印字符最開始是因爲:測試開啓兩個【被控制端】時,會形成消息循環發送,當區分控制消息和顯示消息後,顯示消息就不會執行並循環控制
七、以後測試開啓兩個【控制端】,【控制端】之間能夠互相發送消息,而且都不會當成控制消息(由於該流程裏沒有執行這個步驟),因此只能分類處理一下顯示出來;估計...大概至關於個能聊天的東西了吧
八、最後我以爲數據轉發應該有更牛逼的辦法,好比服務器直接把網絡流量重定向之類的;說實話我以爲我寫的服務器這種接收再轉發——實在是low爆了,真的但願大神能幫我改進下