远程执行的艺术:客户端和服务器之间的Shell对话(C/C++代码实现)

Shell是用户与操作系统交互的接口,它主要负责接收用户输入的命令,并将这些命令解释给内核执行。在远程管理中,Shell充当了非常重要的角色。

Shell的定义和作用

Shell是用户与操作系统交互的接口,它管理和解释了用户输入的命令,并将这些命令传递给操作系统执行。

首先,Shell作为操作系统的最外层,是用户与系统内核之间沟通的桥梁。它接收用户的输入,解释这些输入的含义,并将其转换为操作系统能够理解的形式去执行。这个过程包括了对用户键入的命令进行解析,并调用内核中相应的功能来完成任务。

其次,Shell不仅是一个命令解释器,还提供了自己的编程语言,允许用户编写脚本以实现更复杂的自动化任务。这种编程能力使得Shell不仅限于执行简单命令,还能处理逻辑判断、循环等编程结构,从而扩展了其功能。

此外,Shell支持两种执行模式:交互式和非交互式。交互式模式下,用户可以即时输入命令并得到结果;而在非交互式模式下,命令被存储在脚本文件中,可以批量执行,这种方式通常用于自动化任务和系统管理。

最后,不同的操作系统可能默认使用不同的Shell。例如,在Linux系统中,缺省的Shell通常是Bash,但还有其他多种Shell可供选择,如sh、csh、ksh等,每种Shell都有其独特的特点和功能。

Shell命令是与操作系统交互的文本命令,它允许用户执行各种任务,如文件管理、进程控制、系统监控等。了解Shell命令的基本语法和常用命令对于有效使用Shell环境至关重要。以下是一些基础的Shell命令和它们的用法:

Shell命令的基本语法和常用命令

基本语法
Shell命令的基本语法通常遵循以下结构:

command [options] [arguments]
  • command:要执行的命令名。
  • options:提供给命令的选项或标志,通常以连字符(-)开始。
  • arguments:传递给命令的参数,可以是文件名、目录名或其他值。

文件和目录操作

  • ls:列出目录内容。使用-l选项可以显示详细信息,-a选项显示所有文件(包括隐藏文件)。

    ls -la
    
  • cd:更改当前工作目录。例如,cd /home/user将用户的工作目录更改为/home/user。

    cd /path/to/directory
    
  • pwd:显示当前工作目录的完整路径。

    pwd
    
  • mkdir:创建一个新目录。

    mkdir new_directory
    
  • rmdir:删除空目录。

    rmdir directory_name
    
  • rm:删除文件或目录。使用-r递归删除目录及其内容,-f强制删除。

    rm file.txt
    rm -r directory
    rm -f file.txt
    
  • cp:复制文件或目录。

    cp source_file.txt destination_file.txt
    cp -r source_directory destination_directory
    
  • mv:移动或重命名文件和目录。

    mv old_name.txt new_name.txt
    

进程管理

  • ps:显示当前用户的进程。使用-aux选项可以显示所有用户的进程。

    ps aux
    
  • kill:终止一个或多个进程。需要指定进程ID(PID)。

    kill PID
    
  • top:实时显示系统进程的资源使用情况。

    top
    

系统监控

  • df:显示文件系统的磁盘空间使用情况。

    df -h
    
  • du:显示文件或目录的磁盘使用情况。使用-h选项以易读的格式显示大小。

    du -h file_or_directory
    
  • free:显示系统的内存使用情况。

    free -m
    

网络操作

  • ping:测试到指定IP地址或域名的网络连接。

    ping example.com
    
  • ifconfig(或ip addr):显示网络接口的配置信息。

    ifconfig
    ip addr
    
  • netstat:显示网络连接、路由表、接口统计等信息。

    netstat -tuln
    

文件搜索和文本处理

  • find:在目录树中搜索文件和目录。

    find /path/to/search -name "file_name"
    
  • grep:搜索文件中匹配特定模式的行。

    grep "pattern" file.txt
    
  • cat:查看文件内容。

    cat file.txt
    
  • more/less:分页显示文本文件内容。

    more file.txt
    less file.txt
    

这些仅仅是Shell命令的一小部分,但它们都非常重要且常用。

了解网络基础

网络基础涉及到多个关键概念,包括TCP/IP协议、端口和套接字等。具体介绍如下:

  1. TCP/IP协议:这是互联网上使用的核心协议套件,它定义了数据如何在网络中传输以及如何在网络设备之间进行路由。其中,TCP(传输控制协议)负责确保数据的可靠传输,而IP(互联网协议)则负责将数据分包发送到正确的目的地。
  2. 端口:端口可以看作是计算机上的一个虚拟点,用于区分不同的网络服务或会话。端口号是一个16位的数字,用来标识特定的应用程序或服务。例如,HTTP服务通常运行在80端口,而SSH服务则通常运行在22端口。
  3. 套接字:套接字是网络编程中的一个基本概念,它是网络通信的端点,用于在不同的主机之间进行数据传输。每个套接字都有一个唯一的标识,由一个IP地址和一个端口号组成。套接字的工作方式是,一个程序在服务器端创建一个套接字,然后等待客户端的连接。一旦客户端连接上来,服务器端的套接字就会与客户端的套接字建立一条通信路径,数据就可以在这两个套接字之间双向传输。

了解这些网络基础知识对于理解远程Shell连接至关重要,因为它们构成了客户端与服务器之间通信的基础。

客户端和服务器之间的通信流程

客户端和服务器之间的通信流程涉及多个步骤,这些步骤共同确保了数据能够在两端之间可靠地传输。以下是该通信流程的详细步骤:

  1. 客户端创建套接字:客户端调用socket函数创建一个新的套接字,这个套接字是文件描述符的形式,用于网络通信。
  2. 客户端发起连接请求:客户端使用connect函数向服务器发送连接请求。在这个过程中,客户端会发送一个SYN(同步序列编号)数据段给服务器,并等待服务器的响应。这被称为TCP的三次握手过程中的第一次握手。
  3. 服务器响应连接请求:服务器接收到客户端的SYN后,会发送一个ACK(确认应答)给客户端,同时也会发送一个SYN数据段,以建立从服务器到客户端的连接。这是三次握手中的第二次握手。
  4. 客户端确认连接:客户端收到服务器的SYN-ACK后,会发送一个ACK给服务器,确认已经准备好开始数据传输。这是三次握手中的第三次握手。
  5. 数据传输:一旦连接建立,客户端和服务器就可以开始双向的数据交换。TCP协议确保了数据的顺序性和可靠性。
  6. 释放连接:当数据传输完成后,客户端和服务器需要关闭连接。通常这涉及到四次挥手过程,包括一方发送FIN请求断开连接,另一方确认并最终双方释放资源。
  7. 异常处理:在整个通信过程中,可能会出现各种错误或异常情况,如连接失败、数据传输错误等。客户端和服务器需要有相应的错误处理机制来应对这些问题。

客户端和服务器之间的数据传输

数据传输是客户端和服务器之间通信的一个关键组成部分。在Shell环境中,有多种方法可以用来在客户端和服务器之间发送和接收数据。以下是一些常用的Shell命令和工具,它们可以用于数据传输:

1. SCP (Secure Copy Protocol)

SCP是一个基于SSH的协议,用于在本地和远程主机之间安全地传输文件。SCP提供了数据加密和认证功能,确保传输过程中的数据安全。

发送数据(从客户端到服务器):

scp local_file.txt user@remote_host:/path/to/destination

接收数据(从服务器到客户端):

scp user@remote_host:/path/to/file local_file.txt

在这个例子中,local_file.txt是本地文件,user是远程服务器的用户名,remote_host是远程服务器的地址,/path/to/destination是远程服务器上的目的地路径。

2. SFTP (SSH File Transfer Protocol)

SFTP是一个安全文件传输协议,它基于SSH协议。SFTP提供了一个交互式会话,允许用户执行文件和目录的上传、下载、重命名、删除等操作。

启动SFTP会话:

sftp user@remote_host

在SFTP会话中,你可以使用getput命令来传输文件:

发送数据:

put local_file.txt

接收数据:

get remote_file.txt

3. rsync (Remote Sync)

rsync是一个非常强大的工具,用于同步文件和目录。它可以高效地传输变化的数据,只发送文件的更改部分而不是整个文件,这使得它特别适合于大型文件和频繁更新的场景。

发送数据(从客户端到服务器):

rsync -avz -e ssh local_directory/ user@remote_host:/path/to/destination

接收数据(从服务器到客户端):

rsync -avz -e ssh user@remote_host:/path/to/file local_directory/

这里的选项解释如下:

  • -a: 归档模式,保留原文件的属性。
  • -v: 详细模式,显示详细信息。
  • -z: 压缩传输数据,减少传输大小。
  • -e: 指定使用SSH作为传输层。

4. tar (Tape Archive)

tar命令用于创建和解压tar包,经常与压缩工具(如gzip和bzip2)结合使用,以便在传输前压缩数据。

发送数据前压缩文件:

tar -czvf archive_name.tar.gz directory_to_compress/

在服务器上解压数据:

tar -xzvf archive_name.tar.gz -C /path/to/destination

5. wget 和 curl

wgetcurl是常用的命令行工具,用于从网络上下载文件或发送数据到服务器。

使用wget从服务器下载文件:

wget http://example.com/file_to_download

使用curl发送数据到服务器:

curl -T file_to_upload.txt http://example.com/upload

这些工具和命令为客户端和服务器之间的数据传输提供了多种选择。你可以根据具体的使用场景和需求选择最合适的方法。例如,如果你需要传输大量数据,rsync可能是最佳选择;如果你需要交互式文件传输,SFTP可能更合适。安全性也是一个重要的考虑因素,因此建议总是使用如SSH这样的加密协议来保护数据传输。

远程执行的艺术:客户端和服务器之间的Shell对话(C/C++代码实现)

这个服务器程序可以在Linux系统上运行,用于实现一个简单的远程shell功能。客户端可以通过连接到服务器并发送命令来执行远程命令。服务器将命令的输出发送回客户端。

server:


...
void sig_handler(int signo, const int sockfd)
{
	if (signo == SIGINT)
	{
		fprintf(stderr, "\n(@%d) Server Exiting.", getpid());
		close(sockfd);
		exit(0);
	}
}

// 执行命令并将输出存储在缓冲区中
char *execute_command(char *cmd)
{
...

	FILE *fp = popen(cmd, "r");

	if (fp == NULL)
	{
		fprintf(stderr, "\nError opening file");
		exit(1);
	}

	while (fgets(chunk, sizeof(char) * BUFFER_LEN, fp) != 0)
	{
		int chunk_len = strlen(chunk);
		response = realloc(response, strlen(response) + chunk_len);
		memcpy(&response[len], chunk, chunk_len);
		len += chunk_len;
		bzero(chunk, BUFFER_LEN);
	}
...

	printf("RESP: %1fms - %lu bytes\n%s",
				 (double)(end - start) / CLOCKS_PER_SEC / 1000,
				 strlen(response),
				 response);

	pclose(fp);

	return response;
}


void remote_shell(int sockfd, pid_t pid)
{
...

	while (1)
	{
		bzero(cmd, BUFFER_LEN);

		// read the command from client and copy it in buffer
		if (read(sockfd, cmd, sizeof(cmd)))
		{
			// exit command close server
			if (strncmp("exit", cmd, 4) == 0)
			{
				printf("(%d) Conexon closed.\n", pid);
				break;
			}

			// 包含客户端内容的打印缓冲区
			printf("\n(@%d) RCMD: %s", pid, cmd);

			response = execute_command(cmd);
			...
		}
	}
}

int main(int argc, char **argv)
{
...

	// 从命令行获取端口号
	while ((opt = getopt(argc, argv, "p:")) != -1)
	{
		switch (opt)
		{
		case 'p':
			port = atoi(optarg);
			break;
		}
	}

	// 套接字创建和验证
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (sockfd == -1)
	{
		printf("Socket creation failed...\n");
		exit(1);
	}
	else
		printf("Socket successfully created..\n");

...


	if ((bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) != 0)
	{
		printf("Socket bind failed in port %d.\n", port);
		exit(1);
	}
	else
		printf("Socket successfully binded at port %d.\n", port);

	if ((listen(sockfd, 5)) != 0)
	{
		printf("Listen failed...\n");
		exit(1);
	}
	else
		printf("Server listening..\n");

...

	while (1)
	{

		connfd = accept(sockfd, (struct sockaddr *)&cli, &len);

		if (connfd < 0)
		{
			printf("Server accept failed...\n");
			exit(1);
		}
		else
		{
			// show connection
			strcpy(ipstr, inet_ntoa(cli.sin_addr));
			printf("(@%d) Connection accepted from %s\n", getpid(), ipstr);
		}

		// fork-子级处理此连接,父级侦听另一个连接
...

		if (up_pid == 0)
		{
			remote_shell(connfd, getpid());
		}
	}
...
}

这是一个基于TCP的客户端程序,用于与服务器进行通信。程序首先通过命令行参数获取服务器地址和端口号,然后创建一个套接字并连接到服务器。在连接成功后,程序进入一个循环,等待用户输入命令并发送给服务器。服务器执行命令后将结果返回给客户端,客户端接收到结果后打印出来。
client:

...

char *get_command()
{
...
	printf("\nCMD: ");
	while ((cmd[n++] = getchar()) != '\n')
		;
	return cmd;
}

char *get_response(int sockfd)
{
..

	while (1)
	{
...
		response = realloc(response, len + response_len);
		memcpy(&response[len], chunk, response_len);
		len += response_len;
		if (response_len < BUFFER_LEN)
			break;
	}
..
	return response;
}

void client(int sockfd)
{
...

	while (1)
	{
		cmd = get_command();

		if ((strncmp(cmd, "exit", 4)) == 0)
		{
			printf("Client Exit...\n");
			free(cmd);
			break;
		}
		else
		{

...

			response = get_response(sockfd);
			printf("\nRESP: %lu bytes\n%s", strlen(response), response);
...
		}
	}
}

int main(int argc, char **argv)
{
...

	while ((opt = getopt(argc, argv, "s:p:")) != -1)
	{
		switch (opt)
		{
		case 's':
			server = optarg;
			break;
		case 'p':
			port = atoi(optarg);
			break;
		default:
			if (opt == 's')
				fprintf(stderr, "Option -%c requires an argument.\n", opt);
			else if (isprint(opt))
				fprintf(stderr, "Unknown option `-%c'.\n", opt);
			exit(0);
		}
	}

	if (!server)
	{
		printf("Usage: %s -s <server> [-p <port>]\n", argv[0]);
		exit(0);
	}

	printf("Connecting to %s:%d\n", server, port);

...

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		printf("Socket creation failed...\n");
		exit(0);
	}
	else
		printf("Socket successfully created..\n");

	bzero(&servaddr, sizeof(servaddr));
...


	if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
	{
		printf("Connection failed.\n");
		exit(0);
	}
	else
		printf("Connected.\n");

...
}

If you need the complete source code, please add the WeChat number (c17865354792)

运行效果:

client:
它从控制台获取命令,将其发送到服务器,然后等待显示回复。

server:


每个接受的连接都在一个子进程中运行。您可以使用SIGINT(^C)结束服务器。每个接受的连接都显示在控制台中,并使用@pid管理此连接和远程客户端的IP。当收到一个命令时,它也会显示出来,每次执行后,所花费的时间和发送到客户端的字节也会显示。

总结

总的来说,Shell是连接用户与操作系统的桥梁,无论是本地还是远程管理,它都是不可或缺的工具。了解和掌握Shell的使用,对于进行有效的系统管理至关重要。

We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值