多线程并发远程控制服务器

多线程并发远程控制服务器

实验目的

修改远程控制程序服务器模型程序,将其从循环模式或多进程模式修改为多线程模式

实验要求

  • 服务器同时能够接收客户端多次连接,向多个客户端提供服务

  • 客户输入命令,客户端将命令通过流套接字发送给客户端,服务器执行收到的命令,并将结果发送到客户端显示

  • 客户端输入“quit”,客户端程序与服务器端程序打印退出信息,终止客户端程序的执行

服务器端程序设计

  • 创建套接字: socket()
  • 绑定主机IP、端口号:将IP协议、地址、端口号封装成一个结构体,作为参数传递给bind()
  • 主循环侦听客户端连接要求listen(),并接受连接accept()
  • 没有超出最大客户数(我设置的5),则使用pthread库来创建新的线程:pthread_create()输入参数是线程标识符、线程属性、线程函数和传给线程函数的参数,这里我传的是客户端套接字
  • 新线程根据传递过来的参数进行不同的行为threadFunc,调用pthread_exit()退出
  • 线程执行命令时,调用自定义的函数execute(),其中再调用popen()函数获取命令的输出,结果放入send_buf
  • 主线程调用pthread_join()等待线程结束

实验代码

server.c

/*server*/
/*服务端实现接收执行远程命令并返回结果*/
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include <pthread.h>

/*定义端口和缓冲区大小*/
#define PORT 8888
#define MAXSIZE 4096
#define MAX_CLIENTS 10

/*定义执行客户端发来命令的函数*/
int execute(char* cmd,char* buf){//接收参数:命令、存放执行结果缓冲区
	FILE *fp;//文件指针
	int count;
	
	count = 0;

	/*建立管道*/
    //popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c 来执行参数cmd的指令。"r"代表读取
	if (NULL==(fp = popen(cmd,"r"))){
		perror("create pipe error\n");
		return -1;
	}

	/*统计命令长度*/
	while(((buf[count] = fgetc(fp))!=EOF)&&count<4095)//EOF:enf of file
		count++;
	
	/*添加结束标志*/
	buf[count]='\0';

	pclose(fp);
	return count;
}

/*定义新线程执行的函数*/
void *threadFunc(void* arg){
	
	int recvnum;
	char send_buf[MAXSIZE];
	char recv_buf[MAXSIZE];
	char cmd[MAXSIZE];
	int fd=*((int *)arg);


	while(1){
		/*接收命令*/
        if(-1==(recvnum = recv(fd,recv_buf,sizeof(recv_buf),0)))
		{
			perror("recv error\n");
			break;
		}	
		if (recvnum == 0)
    	{
    	  close(fd);
    	  break;
    	}

		recv_buf[recvnum]='\0';//添加终止符

		if (0==strcmp(recv_buf,"quit")){
			perror("quit\n");
			break;
		}
    	/*服务端打印接收到的命令*/
		printf("receive: %s\n",recv_buf);

		// 指针指向缓冲区终止符
		// 在/bin、/sbin、/usr/bin查找命令
		strcpy(cmd,"/bin/");
		strcat(cmd,recv_buf);
		execute(cmd,send_buf);

		if ('\0'==*send_buf)
		{
			memset(cmd,0,sizeof(cmd));
			strcpy(cmd,"/sbin/");
			strcat(cmd,recv_buf);
			execute(cmd,send_buf);

			if ('\0'==*send_buf){	
				memset(cmd,0,sizeof(cmd));
				strcpy(cmd,"/usr/bin/");
				strcat(cmd,recv_buf);
				execute(cmd,send_buf);
			}

			if ('\0'==*send_buf){	
				memset(cmd,0,sizeof(cmd));
				strcpy(cmd,"/usr/sbin/");
				strcat(cmd,recv_buf);
				execute(cmd,send_buf);
			}

		}

		/*命令不存在*/
		if ('\0'==*send_buf)
		{
			sprintf(send_buf,"cmd error\n");
			break;
		}
			

		printf("reply:\n%s\n",send_buf);

		if (-1==send(fd,send_buf,sizeof(send_buf),0))
		{
			perror("send error\n");
			close(fd);
			break;

		}
	}

	close(fd);	
	pthread_exit(NULL);	
}

int main(){
	/*定义变量*/
    int sockfd;
	int fd;
	struct sockaddr_in client;
	struct sockaddr_in server;
	char send_buf[MAXSIZE];
	char recv_buf[MAXSIZE];
	char cmd[MAXSIZE];
	int sendnum;
	int recvnum;
	int len;
	int i=0;
	
	
	
	/*建立基于TCP的socket*/
    if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){
		perror("create socket error");
		return -1;
	}	

    /*网络字节序转换*/
	memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = htonl(INADDR_ANY);//本地IP
	server.sin_port = htons(PORT);

	/*绑定本地IP和端口号*/
    if (-1==bind(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))){
		perror("bind error\n");
		close(sockfd);
		return -1;
	}

	 pthread_t client_threads[MAX_CLIENTS];
  	int client_count = 0;
	
    while(1)
    {	
		
		/*调用listen侦听客户端的连接请求*/
		if (-1==listen(sockfd,5)){
		perror("listen error\n");
		return -1;
    		}	
		
		memset(recv_buf,0,MAXSIZE);
		memset(send_buf,0,MAXSIZE);
	    
        	/*循环接受连接accept*/		
		// 保证client使用ctrl+c退出后server不挂,accept需要移入while
		if(0>(fd=accept(sockfd,(struct sockaddr*)&client,&len))){
			perror("create connect socket error\n");
			continue;
		}
		
	
		if(client_count<MAX_CLIENTS){
			//传递给新线程的参数是*fd
			if (pthread_create(&client_threads[client_count], NULL, threadFunc, &fd) != 0)
      		{
        		perror("pthread_create error\n");
      		}
      		client_count++;
		}else{
			printf("Max clients reached. Connection rejected.\n");
      		close(fd);
		}
    	
		
	}
	close(sockfd);
  	for (i; i < client_count; i++)
  	{	
    	pthread_join(client_threads[i], NULL);
  	}
  	return 0;
}

client.c

/*client*/
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>

#define PORT 8888
#define MAXSIZE 4096

/*输入提示*/
void print_usage(char* str){
	fprintf(stderr," %s usage:\n",str);
	fprintf(stderr,"%s IP_Addr",str);
}


int main(int argc, char** argv){
	/*定义变量*/
    int sockfd;
	struct sockaddr_in client;
	struct sockaddr_in server;
	char send_buf[MAXSIZE];
	char recv_buf[MAXSIZE];
	int sendnum;
	int recvnum;
	int len;
	int input_len;

    /*参数检查*/
	if(2!=argc){
		print_usage(argv[0]);
		return -1;	

	}

	/*网络字节序转换*/
    memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	/*设置IP地址族结构体*/
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = inet_addr(argv[1]);//键入第二个参数为IP地址
	server.sin_port = htons(PORT);//端口号	
	
 	len = sizeof(struct sockaddr);	
 	
	while(1){
		memset(send_buf,0,MAXSIZE);
		memset(recv_buf,0,MAXSIZE);
		
		/*创建基于TCP通信的socket套接字,每次都新建socket,保证server不挂*/
		if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){
			perror("create socket error\n");
			return -1;
		}	
		
        /*建立连接*/
		if (-1==(connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr)))){
			perror("connect error");
			close(sockfd);
			break;
		}
				
		printf("TCP>");//打印远程连接符号
        /*用户输入命令*/
		fgets(send_buf,MAXSIZE,stdin);
		input_len=strlen(send_buf);
		send_buf[input_len-1]='\0';
		
        /*发送命令至服务端,接收执行结果*/
		if (-1==send(sockfd,send_buf,sizeof(send_buf),0)){
			perror("send error!\n");
			continue;
		}
		
		if (0==strcmp(send_buf,"quit")){
			perror("quit\n");
			break;
		}
		
		if (-1==recv(sockfd,recv_buf,sizeof(recv_buf),0)){
			perror("recv error!");
			continue;
		}

        /*打印接收到的服务端执行结果*/
		fprintf(stdout,"%s\n",recv_buf);
		close(sockfd);

	}
	close(sockfd);
	return 0;
}

编译运行

  • 环境:ubuntu20.04

  • 服务端运行,连接一个客户端,客户端发送多次命令,服务端执行结果回显
    在这里插入图片描述

  • 连接上第二个客户端,第二个客户端发送命令,服务端执行结果回显
    在这里插入图片描述

  • 第一个客户端quit,第二个客户端发送命令,服务端继续执行且结果在第二个客户端回显
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值