本文已参与「新人创作礼」活动,一起开启掘金创作之路。
工作原理
- 客户端接收用户输入的命令,客户端通过套接字将命令传送给服务器端
- 服务器在收到用户的命令,对命令进行解析
- 在服务器端调用对应的命令
- 将命令执行的结果发送给客户端,从而实现远距离控制的功能
附加要求
- 客户端输入“quit”,客户端程序与服务器端程序打印退出信息,终止程序的执行
- 客户输入命令,客户端将命令通过流套接字发送给客户端,服务器执行收到的命令,并将结果发送到客户端显示
- 如果没有客户输入的命令,服务器发送命令非法信息,并在客户端显示该条信息
- 服务器端可以接收客户端多次连接,能够处理客户端非正常退出
源码
gcc编译后使用
gcc -o cmdtcpserver cmdtcpserver.c
gcc -o cmdtcpclient cmdtcpclient.c
./cmdtcpserver
./cmdtcpclient IP
server
// cmdtcpserver.c
#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 8900
#define MAXSIZE 4096
// 定义命令执行的函数
int execute(char* cmd,char* buf){
FILE *fp;
int count;
count = 0;
// 管道
if (NULL==(fp = popen(cmd,"r"))){
perror("create pipe error\n");
return -1;
}
// 统计长度
while(((buf[count] = fgetc(fp))!=EOF)&&count<4095)
count++;
// 添加终止符
buf[count]='\0';
pclose(fp);
return count;
}
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;
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);
server.sin_port = htons(PORT);
if (-1==bind(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))){
perror("bind error\n");
close(sockfd);
return -1;
}
if (-1==listen(sockfd,5)){
perror("listen error\n");
return -1;
}
while(1){
memset(recv_buf,0,MAXSIZE);
memset(send_buf,0,MAXSIZE);
// 为了实现client使用ctrl+c退出后server不挂,accept需要移入while
if(0>(fd=accept(sockfd,(struct sockaddr*)&client,&len))){
perror("create connect socket error\n");
continue;
}
if(-1==(recvnum = recv(fd,recv_buf,sizeof(recv_buf),0))){
perror("recv error\n");
continue;
}
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");
printf("reply:%s\n",send_buf);
if (-1==send(fd,send_buf,sizeof(send_buf),0)){
perror("send error\n");
break;
}
// 每次都需要关闭fd
close(fd);
}
close(sockfd);
return 0;
}
client
// cmdtcpclient.c
#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 8900
#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));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_port = htons(PORT);
len = sizeof(struct sockaddr);
while(1){
memset(send_buf,0,MAXSIZE);
memset(recv_buf,0,MAXSIZE);
// 每次新建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");
continue;
}
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;
}
运行样例
可以执行长段指令、对无效指令报错、ctrl+c断开后server端不挂,以及quit结束两端会话
完
欢迎在评论区留言