记一次远程CMD开发过程
开发初衷:
有些同学电脑老是要出问题,但又不是什么大问题,通常几句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开发过程的更多相关文章
- 记一个社交APP的开发过程——基础架构选型(转自一位大哥)
记一个社交APP的开发过程——基础架构选型 目录[-] 基本产品形态 技术选型 最近两周在忙于开发一个社交App,因为之前做过一点儿社交方面的东西,就被拉去做API后端了,一个人头一次完整的去搭这么一 ...
- 记一次解决cmd中执行java提示"找不到或无法加载主类"的问题
今天遇到一个问题:在cmd命令行中,用javac编译java文件可以成功,但是用java执行却提示“找不到或无法加载主类”.现将该问题的原因以及解决办法记录一下. 先理解一下系统变量path和clas ...
- 10.17小作业 基于TCP开发一款远程CMD程序
基于TCP开发一款远程CMD程序 客户端连接服务器后,可以向服务器发送命令 服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端 注意: 执行系统指令使用subprocess ...
- 远程cmd操作
<<PSTools.zip>><<Install_PowerCmd.exe>><<cmder_mini.zip>><< ...
- python练习-Socket实现远程cmd命令
需求:基于tcp的套接字实现远程执行命令的操作 代码示例: # 编辑者:闫龙 #Client端部分 import socket #导入骚凯特模块 CmdObj = socket.socket(sock ...
- 匿名管道 远程cmd
管道是单向的,传送数据的方向是固定的,所以互相通信需要两个管道. STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.dwFlags = STARTF ...
- C语言Socket-模拟远程CMD(客户端向服务器发送命令,服务器执行该命令)
服务端(server) #include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.li ...
- 记一次使用cmd执行java文件遇到的坑...包括“使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题”
今天写了一个java文件,类似聊天软件的东西.在eclipse里输入输出显得没感觉,于是乎就准备在cmd里输入和显示输出.如下图,我准备运行的是ChatDemo.class文件.路径是:D:\work ...
- Springcloud踩坑记---使用feignclient远程调用服务404
公司项目进行微服务改造,由之前的dubbo改用SpringCloud,微服务之间通过FeignClient进行调用,今天在测试的时候,eureka注册中心有相应的服务,但feignclient就是无法 ...
随机推荐
- SQL 游标知识整理
游标声明格: declare 游标名称 cursor (游标关键字) for 游标操作对象(select * from 表名称)游标使用: open 游标名称; fetch first from 游标 ...
- c++用参数返回堆上的空间
<高质量c++和c编程>7.4 指针参数是如何传递内存的一节中写道 void GetMemory(char *p, int num) { p = (char *)malloc(sizeof ...
- shell多线程(3)while循环
start="2018-06-17" end="2018-07-01" min=`date -d "${start}" +%Y%m%d` m ...
- C#最新功能(6.0、7.0)
一直用C#开发程序,.NET的功能越来越多,变化也挺大的,从最初的封闭,到现在的开源,功能不断的增加,一直在进步.作为C#的强烈支持者,C#的变化,我不能不关注,这篇文章主要介绍,C#6.0和C#7. ...
- Solr 18 - 通过SolrJ局部更新Solr中的文档 (原子操作、非覆盖操作)
目录 1 需求分析 2 需求实现 2.1 pom.xml依赖 2.2 Java代码示例 3 补充说明 3.1 关于文档中_version_的取值说明 3.2 store=true/false的区别 1 ...
- Centos7.3搭建DNS服务器--BIND
1.系统环境说明 [root@dns-server etc]# cat /etc/redhat-release CentOS Linux release (Core) 防火墙和Selinux关闭 [r ...
- 高效 MacBook 工作环境配置,超实用!
作者:正鹏 & 隃墨 http://www.xialeizhou.com/?p=71 前言 工欲善其事,必先利其器,工具永远都是用来解决问题的,没必要为了工具而工具,一切工具都是为了能快速准确 ...
- 不懂数据库索引的底层原理?那是因为你心里没点b树
本文在个人技术博客不同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩... 前几天下班回到家后正在处理一个白天没解决的bug,厕所突然传来对象的声音: ...
- 【朝花夕拾】Android自定义View篇之(九)多点触控(下)实践出真知
前言 在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数.本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识.要理 ...
- Codeforces Gym101097I:Sticks (思维)
http://codeforces.com/gym/101097/attachments 题意:现在有k种颜色的木棍,每种颜色有ni根木棍,每根木棍有一个长度,问是否有三根木棍可以组成三角形,并且这三 ...