开发初衷:

  有些同学电脑老是要出问题,但又不是什么大问题,通常几句cmd就能搞定。之前解决方案有2:一是远程演示,我口述别人操作;一是我写个cmd脚本,但毕竟不在本机不好调试。(吐槽一下常用的远程控制实在是难用至极)

  解决:远程cmd,能够实时的反馈执行结果,带宽占用少

软件环境:

  windows10, Microsoft Visual C# 2010 Express, 资料都是百度的!

设计以及有些我觉得该思考的问题:

  思考:

    1、终端应该做成能在所有能联网的设备上跑,而不是内网版

    2、假设终端只在内网上运行,那么一个终端需要知道另一个终端的ip和端口(可通过广播,但麻烦了)

    3、做成广域网版使用什么技术(主要指消息是通过转发还是打洞),最后选择转发,因为打洞我还不是很会 -_-''

    4、终端应该区分控制端和被控制端?不用,软件内部功能区分就行了;如果终端分开那么服务器端还要做识别,就设计麻烦了

    5、先就这么多吧...

  大概是这么设计的:

    1、服务器开启后可接受2个客户端连接,不区分【控制端】或【被控制端】,只做消息转发(提高效率);只有当两端都连上后才能传输消息,当只有一端存在时该端发送消息会收到服务器提示 "另一端不在线"

    2、客户端进入时选择是【控制端】还是【被控制端】,连上后提示另一端是否连接上服务器

    3、【控制端】向【被控制端】发送控制命令,【控制端】将收到的消息(期待收到的回显)显示

    4、【被控制端】等待命令,收到后执行并将回显显示于该端,最后将回显发送回【控制端】

实现:

 const int PORT = ; //Server PORT
const string HOST = "127.0.0.1"; //Server IP
static TcpClient tcp = new TcpClient();
static bool isAlive = false; //连接标识
static bool s=false; //控制端标识,需要传入参数[u999]进入 //包中第一字节为0x04解析为聊天消息,否则为控制消息;别问为什么用0x04,问就是缘分 static void Main(string[] args)
{
//控制端判断
if(args.Length!=)
if(string.Compare(args[],"u999")==) //控制端
{
Console.Write("Welcome, controller.");
s=true;
}
else
Console.WriteLine("Hello loser."); //想爆破密码的?
try{
tcp.Connect(HOST, PORT); //连接阻塞
isAlive = true;
Console.WriteLine("-------已连接=" + tcp.Client.RemoteEndPoint + "------");
}catch(Exception e){
Console.WriteLine(e.Message+"\n任意键退出...");
Console.ReadKey();
return;
} if(!s) //被控制端
{
//发送线程
ThreadStart ts=new ThreadStart(sendToCtrl);
Thread th=new Thread(ts);
th.Start(); Process p = new Process();
p.StartInfo.FileName = "cmd.exe"; //程序名
p.StartInfo.UseShellExecute = false; //不使用程序外壳
p.StartInfo.RedirectStandardInput = true; //重定向输入
p.StartInfo.RedirectStandardOutput = true; //重定向输出
p.StartInfo.RedirectStandardError = true; //错误
p.StartInfo.CreateNoWindow = true; //创建窗口
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //订阅输出
p.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //错误输出
p.Start();
p.BeginOutputReadLine(); //程序开始后开始订阅
p.BeginErrorReadLine(); byte[] recvBuf = new byte[];
string command = "";
int recvNum;
while(!p.HasExited) //循环执行
{
recvNum = ;
try{
recvNum = tcp.Client.Receive(recvBuf); //服务等待阻塞
}catch(Exception){ //断开连接
break;
}
if(recvNum == ) //主动断开:空消息
break;
if(recvBuf[]!=0x04) //控制消息
{
command = Encoding.UTF8.GetString(recvBuf, , recvNum); //接收远程命令
p.StandardInput.WriteLine(command); //执行
}
else
Console.WriteLine(Encoding.UTF8.GetString(recvBuf,,recvNum-));
Thread.Sleep(); //检测exit命令,while循环条件检查
}
Console.WriteLine("-------End.------\n任意键退出...");
//先关程序,再关连接
p.Close();
p = null;
tcp.Client.Close();
tcp.Close();
tcp=null;
}
else //控制端
{
//接收线程
ThreadStart ts = new ThreadStart(recvMsg);
Thread th = new Thread(ts);
th.Start(); string input = "";
while(true) //循环发送
{
//这里也输入的是什么消息,反正都当控制消息吧(区分要解析输入)
input = Console.ReadLine();
if(isAlive)
tcp.Client.Send(Encoding.UTF8.GetBytes(input+'\n'));
else
break;
Thread.Sleep();
}
}
Console.ReadKey(); //这一句没什么卵用但我不想删
} //控制端回显消息
static void recvMsg()
{
byte[] recvBuf = new byte[];
int recvNum;
while(true) //循环接收
{
recvNum = ;
try{
recvNum = tcp.Client.Receive(recvBuf);
}catch(Exception){
break;
}
if(recvNum == )
break;
//以上为TCP断开判断 //以下为什么要分开写呢?因为合起来麻烦了...
if(recvBuf[]!=0x04) //被控端回显
Console.Write(Encoding.UTF8.GetString(recvBuf,, recvNum));
else //聊天消息
Console.WriteLine(Encoding.UTF8.GetString(recvBuf,, recvNum-));
}
tcp.Client.Close();
tcp.Close();
isAlive = false;
Console.WriteLine("-------Server down------");
} //被控端消息发送
static string output = "";
static void sendToCtrl()
{
int outputFlag = ; //被控端超时发送标记
string outputNow = "";
byte[] sendBytesT = null, sendBytes=null;
while(true) //循环检测缓冲池
{
Thread.Sleep();
if(!isAlive || output.Length == ) //未连接 或 无缓存数据
continue;
if(output.Length > || outputFlag > ) //缓存满4k 或 超时传送150ms
{
outputNow = output;
output = ""; //这段可能有点low, 望大佬告知该怎么写
sendBytesT = Encoding.UTF8.GetBytes(outputNow);
sendBytes = new byte[sendBytesT.Length + ];
sendBytes[] = 0x04;
sendBytesT.CopyTo(sendBytes, ); try{
tcp.Client.Send(sendBytes);
}catch(Exception){
break;
}
outputFlag = ;
continue;
}
++outputFlag;
}
} //被控端输出入池
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if(!string.IsNullOrEmpty(e.Data))
{
Console.WriteLine(e.Data);
output += e.Data + '\n';
}

客户端代码

 //服务器端不区分控制端被控制端,只作数据转发
const int PORT = ; //Server PORT
static TcpClient[] tcp=new TcpClient[];
static int alive=; //二进制位标识该终端是否在线=00|01|10|11
static void Main()
{
//消息线程
ParameterizedThreadStart ts=new ParameterizedThreadStart(user); TcpListener listen=new TcpListener(IPAddress.Any,PORT);
TcpClient tcpT=null;
while(true) //循环监听
{
if(alive==) //两个终端==11
{
Thread.Sleep();
continue;
}
listen.Start();
Console.WriteLine("Listen="+PORT);
tcpT=listen.AcceptTcpClient(); //监听阻塞
Console.WriteLine("Accept="+tcpT.Client.RemoteEndPoint);
if(alive<) //==00|01
{
tcp[]=tcpT;
alive|=;
Thread th=new Thread(ts);
th.Start();
}
else //==10,不可能==11(当==11时两个线程都在消息循环中)
{
tcp[]=tcpT;
alive|=;
Thread th=new Thread(ts);
th.Start();
}
listen.Stop();
}
Console.ReadKey();
} //处理一个终端,传入本段代码控制的tcp序号==0|1
static void user(object index0)
{
int index=(int)index0;
if(index!= && index!=) //非法验证
return;
byte[] sendBytesT=Encoding.UTF8.GetBytes(alive==?"Another is connected.\n":"Only you, wait.\n");
byte[] sendBytes=new byte[sendBytesT.Length+];
sendBytes[]=0x04;
sendBytesT.CopyTo(sendBytes,);
tcp[index].Client.Send(sendBytes); byte[] recvBuf = new byte[];
int recvNum;
while(true) //接收[转发]消息
{
recvNum=;
try{
recvNum = tcp[index].Client.Receive(recvBuf);
}catch(Exception){
break;
}
if(recvNum == )
break;
//以上为TCP断开判断 被控制端断开 //Console.WriteLine(recvNum+","+Encoding.UTF8.GetString(recvBuf,0,recvNum));
if(alive==) //两端在线
{
byte[] sendBuf=new byte[recvNum];
Array.Copy(recvBuf,sendBuf,recvNum);
tcp[index==?:].Client.Send(sendBuf);
}
else //一端不在线
{
sendBytesT=Encoding.UTF8.GetBytes("Another is disconnected.Ctrl+C to EXIT.\n");
sendBytes=new byte[sendBytesT.Length+];
sendBytes[]=0x04;
sendBytesT.CopyTo(sendBytes,);
tcp[index].Client.Send(sendBytes);
}
}
tcp[index].Client.Close();
tcp[index].Close();
tcp[index]=null;
alive&=index==?:;
}

服务器代码

一些解释:

  1、服务器只允许两个终端连接应该能满足一般使用场景,至少能解决开始叙述的问题

  2、客户端开始那个密码区分控制端的东西,其实没多大用,编译出来的exe我用16进制打开都能找到 "密码",希望会密码学的大神不要喷,我确实不大会

  3、【被控制端】那段调用cmd的代码网上不少,略有借鉴...

  4、【被控制端】消息回显为什么不直接发送回【控制端呢】?因为【被控制端】每输出一行就会产生一个输出重定向,如果每一个重定向都直接使用网络传送,那么将导致网络负荷过大(我猜的),所以根据网络原理我优化了发送界定为:150ms的超时传送 或 4k的超限传送

  5、服务器端可以做成多终端连接,但消息转发/群发会麻烦,可改进用于一个终端控制多个终端,考虑使用特征编号之类的识别控制群

  6、消息前加不可打印字符最开始是由于:测试开启两个【被控制端】时,会造成消息循环发送,当区分控制消息和显示消息后,显示消息就不会执行并循环控制

  7、之后测试开启两个【控制端】,【控制端】之间可以互相发送消息,并且都不会当成控制消息(因为该流程里没有执行这个步骤),所以只能分类处理一下显示出来;估计...大概相当于个能聊天的东西了吧

  8、最后我觉得数据转发应该有更牛逼的办法,比如服务器直接把网络流量重定向之类的;说实话我觉得我写的服务器这种接收再转发——实在是low爆了,真的希望大神能帮我改进下

记一次远程CMD开发过程的更多相关文章

  1. 记一个社交APP的开发过程——基础架构选型(转自一位大哥)

    记一个社交APP的开发过程——基础架构选型 目录[-] 基本产品形态 技术选型 最近两周在忙于开发一个社交App,因为之前做过一点儿社交方面的东西,就被拉去做API后端了,一个人头一次完整的去搭这么一 ...

  2. 记一次解决cmd中执行java提示"找不到或无法加载主类"的问题

    今天遇到一个问题:在cmd命令行中,用javac编译java文件可以成功,但是用java执行却提示“找不到或无法加载主类”.现将该问题的原因以及解决办法记录一下. 先理解一下系统变量path和clas ...

  3. 10.17小作业 基于TCP开发一款远程CMD程序

    基于TCP开发一款远程CMD程序 客户端连接服务器后,可以向服务器发送命令 服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端 注意: 执行系统指令使用subprocess ...

  4. 远程cmd操作

    <<PSTools.zip>><<Install_PowerCmd.exe>><<cmder_mini.zip>><< ...

  5. python练习-Socket实现远程cmd命令

    需求:基于tcp的套接字实现远程执行命令的操作 代码示例: # 编辑者:闫龙 #Client端部分 import socket #导入骚凯特模块 CmdObj = socket.socket(sock ...

  6. 匿名管道 远程cmd

    管道是单向的,传送数据的方向是固定的,所以互相通信需要两个管道. STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.dwFlags = STARTF ...

  7. C语言Socket-模拟远程CMD(客户端向服务器发送命令,服务器执行该命令)

    服务端(server) #include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.li ...

  8. 记一次使用cmd执行java文件遇到的坑...包括“使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题”

    今天写了一个java文件,类似聊天软件的东西.在eclipse里输入输出显得没感觉,于是乎就准备在cmd里输入和显示输出.如下图,我准备运行的是ChatDemo.class文件.路径是:D:\work ...

  9. Springcloud踩坑记---使用feignclient远程调用服务404

    公司项目进行微服务改造,由之前的dubbo改用SpringCloud,微服务之间通过FeignClient进行调用,今天在测试的时候,eureka注册中心有相应的服务,但feignclient就是无法 ...

随机推荐

  1. 高性能嵌入式核心板新标杆!米尔推出基于NXP i.MX8M处理器的MYC-JX8MX核心板

    随着嵌入式及物联网技术的飞速发展,高性能计算的嵌入式板卡已经成为智能产品的基础硬件平台.为响应行业应用和满足客户需求,米尔电子推出基于NXP公司i.MX8M系列芯片的开发平台MYD-JX8MX系列开发 ...

  2. 基于 Roslyn 实现动态编译

    基于 Roslyn 实现动态编译 Intro 之前做的一个数据库小工具可以支持根据 Model 代码文件生成创建表的 sql 语句,原来是基于 CodeDom 实现的,最近改成使用基于 Roslyn ...

  3. 【POJ - 3414】Pots(bfs)

    Pots 直接上中文 Descriptions: 给你两个容器,分别能装下A升水和B升水,并且可以进行以下操作 FILL(i)        将第i个容器从水龙头里装满(1 ≤ i ≤ 2); DRO ...

  4. 报错:java.sql.SQLException: The server

    报错:java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized 在IDEA运行是报出例如相识的错误时: ...

  5. 【React】遍历的两种方式

    1.foreach(推荐) list.forEach((item)=>{ }); eg: dataSource.forEach((item) => { const est = item.e ...

  6. [AI开发]目标跟踪之计数

    基于视频结构化的应用中,目标在经过跟踪算法后,会得到一个唯一标识和它对应的运动轨迹,利用这两个数据我们可以做一些后续工作:测速(交通类应用场景).计数(交通类应用场景.安防类应用场景)以及行为检测(交 ...

  7. 音频算法speex中的aec分析以及解析

    算法原理: Speex的AEC是以NLMS(Normalized Least Mean Square)为基础,用MDF(multidelay block frequency domain)频域实现,最 ...

  8. C++学习书籍推荐《More Effective C++》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <More Effective C++:35个改善编程与设计的有效方法(中文版)>:传世经典书丛 媒体推荐 <Effective c++&g ...

  9. 绝对是全网最好的Splay 入门详解——洛谷P3369&BZOJ3224: Tyvj 1728 普通平衡树 包教包会

    平衡树是什么东西想必我就不用说太多了吧. 百度百科: 一个月之前的某天晚上,yuli巨佬为我们初步讲解了Splay,当时接触到了平衡树里的旋转等各种骚操作,感觉非常厉害.而第二天我调Splay的模板竟 ...

  10. SQL Server 函数的定义及使用

    一.定义函数 1. 标量值函数: 返回一个确定类型的标量值,例如:int,char,bit等 --创建标量值函数 create function func_1(@func_parameter_1 in ...