模拟XShell的小项目
不知道大家有没有用过XShell这款工具,这款工具通过windows可以远程操作处于开机状态的linux操作系统,也就是说把你的电脑和一台服务器连入网络,你通过输入服务器所在的IP地址建立一个会话就可以远端操作linux的服务器了,十分方便。
这次这个模拟XShell的小项目就是类似的功能
执行流程:
windows客户端输入命令,通过网络传输到linux服务器端上,linux服务器端执行命令,将执行命令产生的结果保存进文件,然后再将文件传输回windows客户端进行展示。
问题思考:真的有必要将结果保存在文件当中么?可以通过管道直接把结果文件流书写到socket上,然后客户端直接读取socket上的数据,省去书写和读取文件的时间
说一下大概的思路,很简单的一个大体思路。
完成通信的方式是通过socket编程实现的,也就是说,启用在linux的服务器端和启用在windows的客户端之间交换命令和结果集都是通过socket传输的。
执行命令的方式不采用system执行系统调用的方式,因为这么做可能产生如下后果:对于shell执行命令来说,大多数时候都是产生一个子shell来执行命令,他本身不会亲自涉险,因为一旦用户编写的程序含有什么严重BUG会导致终端出现错误而停止运行,这时人和linux之间的交互方式就被切断了,尤其是在没有图形界面的纯命令操作环境下,连正常关机都很难办到,所以为了避免这种情况的发生shell会产生一个子shell进程来执行。这就意味着有可能你在切换工作目录的时候(比如说你用cd这个命令),就只会在子进程shell中切换工作目录,然后你打算在新目录下做点什么的时候会发现,你欸切换目录其实失败了,因为在父亲shell下你根本没有切换过目录。
那么我采用了popen这个函数,本身这个函数是通过管道来实现的,这个函数通过传参的方式读入命令,然后可以通过管道的另一端将返回的结果集书写进文件里面。然后我把文件传输回windows显示出来。
执行流程很简单不是什么难题。因为是tcp是流形式的,在windows一段采用二进制读写就可以了。
因为代码不是很多,就直接贴出来了
LinuxServer(Linux服务器端)
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h> //inet_ntoa()函数的头文件
#include <string>
#include <unistd.h>
#include <iostream>
using namespace std; bool Judge(char *buf,size_t size)
{
for(int i =;i<size;++i)
{
if(buf[i]!=)
{
return false;
}
}
return true;
} #define portnumber 8001//定义端口号:(0-1024为保留端口号,最好不要用) int main(int argc, char *argv[])
{
int sockfd, new_fd;
struct sockaddr_in server_addr; //描述服务器地址
struct sockaddr_in client_addr; //描述客户端地址
socklen_t sin_size;
char hello[] = "Hello! Are You Fine?\n"; /* 服务器端开始建立sockfd描述符 */
if((sockfd = socket(AF_INET, SOCK_STREAM, )) == -) // AF_INET:IPV4;SOCK_STREAM:TCP
{
fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
exit();
} /* 服务器端填充 sockaddr结构 */
bzero(&server_addr, sizeof(struct sockaddr_in)); // 初始化,置0
server_addr.sin_family = AF_INET; // Internet
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
server_addr.sin_addr.s_addr=inet_addr("192.168.84.128"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
server_addr.sin_port = htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号 if (bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -)
{
fprintf(stderr, "Bind error:%s\n\a", strerror(errno));
exit();
} /* 捆绑sockfd描述符到IP地址 */ /* 设置允许连接的最大客户端数 */
if (listen(sockfd, ) == -)
{
fprintf(stderr, "Listen error:%s\n\a", strerror(errno));
exit();
} /* 服务器阻塞,直到客户程序建立连接 */
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size);
cout<<"连接成功等待输入并接收命令字符串"<<endl;
if (new_fd == -)
{
fprintf(stderr, "Accept error:%s\n\a", strerror(errno));
exit();
}
char recvbuf[] = { };
char sendbuf[] = { };
while ()
{
//等待接受字符串,也就是等待命令的输入
cout<<"等待接收命令字符串"<<endl;
int len = recv(new_fd, recvbuf, , );
string tmp(recvbuf);
cout<<"接受字符串,等待书写结果集:"<<recvbuf<<endl; //执行传输过来的字符串并且解析成命令行命令并执行制作成结果集文件等待传输
//核心就是popen函数
FILE *stream;
FILE *wstream;
char buf[]; memset( buf, '\0', sizeof(buf) );//初始化buf,以免后面写如乱码到文件中
stream = popen( tmp.c_str(), "r" ); //将“ls -l”命令的输出 通过管道读取(“r”参数)到FILE* stream
wstream = fopen( "result.txt", "w+"); //新建一个可写的文件 size_t lengt=fread( buf, sizeof(char), sizeof(buf), stream); //将刚刚FILE* stream的数据流读取到buf中
fwrite( buf, , lengt, wstream );//不要写1024的buf的大小,在linux下会多出一部分\0,读取到多少结果就往文件中写多少 pclose( stream );
fclose( wstream );
cout<<"结果集书写完毕"<<endl;
system("iconv result.txt -f UTF-8 -t GBK -o resultfile.txt "); FILE *fp;
fp=fopen("resultfile.txt","r");
int count=;
//传输结果集文件
while ()
{
//fseek(fp,0,SEEK_SET);
//memset(sendbuf, 0, 512);//这句话是否有必要还有待考究
int len = fread(sendbuf, , ,fp );
send(new_fd, sendbuf, len, );
//cout<<"传输中"<<count<<endl;
cout<<sendbuf<<endl;
count++;
cout<<"count:"<<count<<endl;
// printf("sendBuf:%c\n",* sendBuf);
// printf("len:%d\n", len);
if (Judge(sendbuf,))
{
cout<<"退出"<<endl;
//send(new_fd,sendbuf,len,0);//发送全空字符串?!
break;
}
memset(sendbuf, , );
} cout<<"结果集传输完毕"<<endl;
memset(recvbuf, , );
}
cout<<"传输完毕"<<endl;
/* 结束通讯 */
close(new_fd);
close(sockfd);
exit();
}
WindowsClient(Windows客户端)
//客户端 #define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<windows.h>
#include<locale.h>
using namespace std; bool Judge(char *buf, size_t size)
{
for (int i = ; i < size; ++i)
{
if (buf[i] != )
{
return false;
}
}
return true;
} #pragma comment(lib, "ws2_32.lib")
int main()
{
WORD wVersionRequested; //typedef unsigned short WORD
WSADATA wsaData; //用阿里存储系统传回的关于WinSocket的资料
int err; //用来判断启动函数是否正常 wVersionRequested = MAKEWORD(, ); err = WSAStartup(wVersionRequested, &wsaData); if (err != )
{
return -;
} if (LOBYTE(wsaData.wVersion) != || HIBYTE(wsaData.wVersion) != )
{
WSACleanup();
return -;
} SOCKET socketClient = socket(AF_INET, SOCK_STREAM, ); //AF_INET tcpip的协议
//初始化连接
SOCKADDR_IN addrSrv; //服务器的地址
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.84.128");
addrSrv.sin_family = AF_INET; //使用的是TCP/IP
addrSrv.sin_port = htons(); //转为网络序 设置端口号 //连接到服务器 使用SOCKET对象连接服务器,送入服务器的地址信息 强转
if (connect(socketClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) < ) //协议参数 套接字参数
{
printf("connction faild!");
closesocket(socketClient);
return ;
} //如果连接到了 那就先传出命令字符串再去接收命令执行结果集
char recvBuf[] = { };
char sendBuf[] = { }; // FILE* fp = fopen("resultfile.txt", "w");//真的有必要再写入一个文件里面么?
// if (!fp)
// return 0; //写入文件
while ()
{
//输入并传输命令
cin >> sendBuf;
send(socketClient, sendBuf, , );
//cout << "已经传输了命令,等待接受结果集" << endl;
memset(sendBuf, , ); while ()
{
//接收结果集
int len = recv(socketClient, recvBuf, , );
// cout << "接受结果集" << endl;
//fwrite(recvBuf, 1, len, fp);
cout << recvBuf << endl;
// cout << "len:" << len << endl;
// cout << "打印结果集" << endl;
//printf("%s\n", recvBuf); if (Judge(recvBuf,))
{
// cout << "我要退出接受结果集的循环了" << endl;
break;
}
memset(recvBuf, , );
// cout << "清空缓冲区" << endl;
} } //char recvBuf[50] = {0}; //int size = 10;
//int i = 0;
//while (i<100)
//{
// //recv(socketClient, recvBuf, 50, 0); //socket对象已经接收到数据,现在开始次on个缓存区中取数据
// //printf("%s\n", recvBuf);
// send(socketClient, "123456789" , 50, 0);
// //Sleep(100);
//} closesocket(socketClient); //关闭socket连接 WSACleanup(); printf("Client exit!");
system("pause");
return ; }
模拟XShell的小项目的更多相关文章
- Django集成celery实战小项目
上一篇已经介绍了celery的基本知识,本篇以一个小项目为例,详细说明django框架如何集成celery进行开发. 本系列文章的开发环境: window 7 + python2.7 + pychar ...
- python网页爬虫小项目开发
这是我最近接的一个小项目,花了是整整四天多时间. 任务是将http://www.examcoo.com/index/detail/mid/7网站下所有的试卷里的试题全部提取出来,首先按照题型进行分类, ...
- 一个vue练手的小项目
编程路上的菜鸟一枚 : 最近接触了vue 然后写了一个练手的项目 使用vue-cli脚手架来搭建了的项目 技术: vue2 + vue-router + ES6 + axios 框架有 mint- ...
- 封闭的一个多月,老菜鸟的 机械手和AGV 自动搬运小项目总结
最近上海疫情严重,闲赋在家无事可做,手机里不断的推送一些无脑的谩骂声音,索性找点事情做,将3月份实施的一个自动搬运小项目做一个简单的汇总,便于今后项目实施中积累一些经验.项目需求非常简单,因为能力有限 ...
- 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。
最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...
- IOS-小项目(饿了么 网络部分 简单实现)
在介绍小项目之前,在此说明一下此代码并非本人所写,我只是随笔的整理者. 在介绍之前先展现一下效果图. 看过效果图大家应该很熟悉了,就是饿了么的一个界面而已,值得注意的是,实现时并没有采用本地连接,而是 ...
- Andriod小项目——在线音乐播放器
转载自: http://blog.csdn.net/sunkes/article/details/51189189 Andriod小项目——在线音乐播放器 Android在线音乐播放器 从大一开始就已 ...
- 小项目特供 贪吃蛇游戏(基于C语言)
C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第 ...
- 【PHP小项目使用MVC架构】
小项目名称是雇员管理系统. mvc是一种项目的开发模式,中文名称为模式视图控制器,是强制程序员将数据的输入.处理.输出分开的一种开发模式. 在这个小项目中,控制器使用service作为后缀名. 项目u ...
随机推荐
- 【232】◀▶ IDL显示地理图像
参考: 01 IMAGE 将图像数据以图形窗体的形式显示. 02 COLORBAR 在已经存在的IDL图形中增加一个colorbar或创建. 03 MAPGRID 在已经存在的IDL地图图 ...
- C#开发微信公众平台(附Demo)
服务号和订阅号 服务号是公司申请的微信公共账号,订阅号是个人申请的,我个人也申请了一个,不过没怎么用. 服务号 1个月(30天)内仅可以发送1条群发消息. 发给订阅用户(粉丝)的消息,会显示在对方的聊 ...
- JcClient Ip Get
##通道##123.207.157.82:18425## ##通道##112.95.251.214:18425## ##通道##110.52.233.5:18425## ##通道##119.29.19 ...
- Eclipse设置默认注释
在 windows-->preferenceJava-->Code Style-->Code Templatescode-->new Java file点编辑,覆盖原文本: $ ...
- MyBatis怎么防止SQL注入
SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...
- linux系统数据盘挂载教程
将数据盘挂载为/www命令:#mkdir /www & mount /dev/sdb1 /www ----------------------------------------------- ...
- Angularjs学习笔记(五)----显示和格式化数据
一.引用指令 在AngularJS的文档中,所有指令的名字以驼峰命名法.而在模板中,则需要以蛇形命名法.可以以冒号分割(ng:model)或下划线分割(ng_model),更常见的是以ng-model ...
- Oracle 更新表(另一张表)
Update a set(a.province,a.city)= (select province,city from b where b.mobile=a.mobile)
- HDU 4832 Chess (DP)
Chess Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- android中string.xml中%1$s、%1$d等的用法
今天在研究前辈写的代码的时候,突然发现string里面出现了<stringname="item_recent_photo">最近拍摄%1$s</string> ...