不知道大家有没有用过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服务器端)
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <netdb.h> 6 #include <sys/types.h> 7 #include <netinet/in.h> 8 #include <sys/socket.h> 9 #include <arpa/inet.h> //inet_ntoa()函数的头文件 10 #include <string> 11 #include <unistd.h> 12 #include <iostream> 13 using namespace std; 14 15 16 17 bool Judge(char *buf,size_t size) 18 { 19 for(int i =0;i<size;++i) 20 { 21 if(buf[i]!=0) 22 { 23 return false; 24 } 25 } 26 return true; 27 } 28 29 30 #define portnumber 8001//定义端口号:(0-1024为保留端口号,最好不要用) 31 32 int main(int argc, char *argv[]) 33 { 34 int sockfd, new_fd; 35 struct sockaddr_in server_addr; //描述服务器地址 36 struct sockaddr_in client_addr; //描述客户端地址 37 socklen_t sin_size; 38 char hello[] = "Hello! Are You Fine? "; 39 40 41 /* 服务器端开始建立sockfd描述符 */ 42 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) // AF_INET:IPV4;SOCK_STREAM:TCP 43 { 44 fprintf(stderr, "Socket error:%s a", strerror(errno)); 45 exit(1); 46 } 47 48 /* 服务器端填充 sockaddr结构 */ 49 bzero(&server_addr, sizeof(struct sockaddr_in)); // 初始化,置0 50 server_addr.sin_family = AF_INET; // Internet 51 //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP 52 server_addr.sin_addr.s_addr=inet_addr("192.168.84.128"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip 53 server_addr.sin_port = htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号 54 55 56 if (bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1) 57 { 58 fprintf(stderr, "Bind error:%s a", strerror(errno)); 59 exit(1); 60 } /* 捆绑sockfd描述符到IP地址 */ 61 62 /* 设置允许连接的最大客户端数 */ 63 if (listen(sockfd, 5) == -1) 64 { 65 fprintf(stderr, "Listen error:%s a", strerror(errno)); 66 exit(1); 67 } 68 69 70 /* 服务器阻塞,直到客户程序建立连接 */ 71 sin_size = sizeof(struct sockaddr_in); 72 new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size); 73 cout<<"连接成功等待输入并接收命令字符串"<<endl; 74 if (new_fd == -1) 75 { 76 fprintf(stderr, "Accept error:%s a", strerror(errno)); 77 exit(1); 78 } 79 char recvbuf[512] = { 0 }; 80 char sendbuf[512] = { 0 }; 81 while (1) 82 { 83 //等待接受字符串,也就是等待命令的输入 84 cout<<"等待接收命令字符串"<<endl; 85 int len = recv(new_fd, recvbuf, 512, 0); 86 string tmp(recvbuf); 87 cout<<"接受字符串,等待书写结果集:"<<recvbuf<<endl; 88 89 //执行传输过来的字符串并且解析成命令行命令并执行制作成结果集文件等待传输 90 //核心就是popen函数 91 FILE *stream; 92 FILE *wstream; 93 char buf[1024]; 94 95 memset( buf, '