第3章详细设计和实现
3.1相关技术
1)TCP编程,主要包括socket()函数、bind()函数、listen()函数、recv()函数、send()函数以及客户端的connect()函数。
2)C语言中对结构体的操作,和对字符串的比较
3)对文件的读写操作
4)popen调用shell
3.2开发工具和运行环境
本系统在ubantu 16.4 LTS环境下开发,所用的工具为vi编辑器和gedit编辑器,以及gcc编译工具。
3.3主要功能的源码
3.3.1服务器端socket的建立
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) //创建socket
printf( "socket failed" );
/* 设置端口快速重用*/
optval = 1;
if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) < 0 )
printf( "setsockopt failed" );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( PORT );
server_addr.sin_addr.s_addr = htonl( INADDR_ANY );
if ( bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0 ) //绑定ip和端口号
printf( "bind failed" );
if ( listen(sockfd, MAX_LISTEN) < 0 ) //建立监听
printf( "listen failed" );
3.3.2对客户端的发送信息进行处理
while ( 1 ) {
if ( (connfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_size)) < 0 ) //响应客户端请求
printf( "accept failed" );
printf( "Server accept a client: ip = %s\n",inet_ntoa(client_addr.sin_addr) ); //打印客户端ip
/* 创建子进程处理客户端请求 */
if ( (pid = fork()) == 0 ) {
while ( 1 ) {
/* 接受客户端信息 */
if ( (recv_bits = recv(connfd, buffer, BUF_SIZE, 0)) < 0 )
printf( "recv failed" );
/* 去除最后一个字符 */
buffer[recv_bits - 1] = '\0';
/* 用户名鉴定 */
if ( recv_type == USERNAME ) {
if ( strcmp(name, buffer) != 0 ) {
if ( send(connfd, error_user,strlen(error_user), 0) < 0 )
printf( "send failed" );
break;
}else{
if ( send(connfd, correct_user,strlen(correct_user), 0) < 0 )
printf( "send failed" );
}
printf("%s正在登录:",buffer);
recv_type = PASSWORD; //设置标志为密码鉴定模式
}
/* 密码鉴定 */
else if ( recv_type == PASSWORD ) {
if ( strcmp(passwd, buffer) != 0 ) { //验证密码
if ( send(connfd, error_password,strlen(error_password), 0) < 0 )
printf( "send failed" );
} else {
if ( send(connfd, success_login, strlen(success_login), 0) < 0 )
printf( "send failed" );
printf("用户登录成功!\n");
recv_type = COMMAND; //设置标志为命令模式
}
}
/* 命令模式 */
else if( recv_type == COMMAND ) {
command(connfd,buffer);
puts("客户端输入的命令为:");
puts(buffer);
}
}
close( sockfd );
close( connfd );
exit ( 0 );
}
else
close( connfd );
}
3.3.3 执行命令函数
int command(int connfd,char*command)
{
/*
* 作用:通过popen调用shell执行command命令
* connfd: 客户端标识
* command: 命令字符串
*/
FILE *fstream = NULL;
char buff[1024];
memset(buff, 0, sizeof(buff));
if ( strcmp(exit_command, command) == 0 ) { //退出
if ( send(connfd, exit_command,strlen(exit_command), 0) < 0 )
printf( "send failed" );
printf("客户端已断开连接!\n");
return 0;
}
if(NULL == (fstream = popen(command,"r")))
{
fprintf(stderr,"execute command failed: %s",strerror(errno));
return -1;
}
while(NULL != fgets(buff, sizeof(buff), fstream))
{
//printf("%s",buff);
if ( send(connfd, buff,strlen(buff), 0) < 0 ) //将命令执行结果发送至客户端
printf( "send failed" );
}
pclose(fstream);
return 0;
}
3.3.4客户端发送与接受信息
int send_recv( int connfd,char flag )
{
/*
* 作用:客户端与服务器端信息的处理
* connfd: 服务器端标识
* flag: 输入类比标识
*/
char input_buf[ BUF_SIZE ]; //定义字符串存放输入信息
char recv_buf[ BUF_SIZE ]; //定义字符串存放服务器端返回信息
memset(input_buf, 0, sizeof(input_buf));
memset(recv_buf, 0, sizeof(recv_buf));
fgets( input_buf, BUF_SIZE, stdin ); //获取用户输入
input_buf[strlen(input_buf) - 1] = '\0'; //去除最后一个输入的确认
/* 向服务器发送消息 */
if ( send(connfd, input_buf, BUF_SIZE, 0) < 0 )
printf( "send failed" );
/* 从服务器接受消息 */
if ( recv(connfd, recv_buf, BUF_SIZE, 0) < 0 )
printf( "recv failed" );
/* 用户名处理 */
if ( flag == 'u' ) {
if ( strcmp(recv_buf, error_user) == 0 ) { //用户名错误
puts( error_user );
return FALSE;
}
}
/* 密码处理 */
else if ( flag == 'p' ) {
if ( strcmp(recv_buf, error_password) == 0 )
{ puts( error_password ); //密码错误
return FALSE;
}
}
/* 命令处理 */
else if( flag == 'c' )
{
if ( strcmp(recv_buf, exit_command) == 0 ) { //退出
printf("连接已断开\n");
return FALSE;
}
puts(recv_buf); //命令结果输出
}
return TRUE;
}
第7章 附录
7.1服务器端
#include#include #include #include <string.h> #include #include #include #include #include #include in.h> #define PORT 3333 //用户名和密码 char *name="twain"; char *passwd="123"; #define MAX_LISTEN 10 #define BUF_SIZE 1024 #define USERNAME 0 #define PASSWORD 1 #define COMMAND 2 //登录过程状态标识 char *error_user = "error user name"; char *error_password = "error password"; char *correct_user = "correct user"; char *success_login = "correct password"; char *exit_command = "exit"; char *exit_tip="退出连接!"; /* * 介绍:服务器端 * 功能: * 1.接受客户端的登录请求并验证 * 2.接受客户端的命令在本地执行并返回执行结果 */ int command(int connfd,char*command) { /* * 作用:执行command命令 * connfd: 客户端标识 * command: 命令字符串 */ FILE *fstream = NULL; char buff[1024]; memset(buff, 0, sizeof(buff)); if ( strcmp(exit_command, command) == 0 ) { //退出 if ( send(connfd, exit_command,strlen(exit_command), 0) < 0 ) printf( "send failed" ); printf("客户端已断开连接!\n"); return 0; } if(NULL == (fstream = popen(command,"r"))) { fprintf(stderr,"execute command failed: %s",strerror(errno)); return -1; } while(NULL != fgets(buff, sizeof(buff), fstream)) { //printf("%s",buff); if ( send(connfd, buff,strlen(buff), 0) < 0 ) //将命令执行结果发送至客户端 printf( "send failed" ); }/* if(NULL != fgets(buff, sizeof(buff), fstream)) { //printf("%s",buff); if ( send(connfd, buff,strlen(buff), 0) < 0 ) printf( "send failed" ); }else{ if ( send(connfd, "..",sizeof("buff"), 0) < 0 ) printf( "send failed" ); }*/ pclose(fstream); return 0; } int main( int argc, char *argv[] ) { int sockfd, connfd; int optval; int recv_type = USERNAME; //默认为USERNAME,即用户名输入模式 int client_size; int recv_bits; char buffer[ BUF_SIZE ]; pid_t pid; struct sockaddr_in client_addr, server_addr; client_size = sizeof( struct sockaddr_in ); /* 初始化各变量为零 */ memset( buffer, 0, BUF_SIZE ); memset( &server_addr, 0, sizeof(server_addr) ); memset( &client_addr, 0, sizeof(server_addr) ); if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) //创建socket printf( "socket failed" ); /* 设置端口快速重用*/ optval = 1; if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) < 0 ) printf( "setsockopt failed" ); server_addr.sin_family = AF_INET; server_addr.sin_port = htons( PORT ); server_addr.sin_addr.s_addr = htonl( INADDR_ANY ); if ( bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0 ) //绑定ip和端口号 printf( "bind failed" ); if ( listen(sockfd, MAX_LISTEN) < 0 ) //建立监听 printf( "listen failed" ); printf("telnet服务器已开启……\n"); while ( 1 ) { if ( (connfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_size)) < 0 ) //响应客户端请求 printf( "accept failed" ); printf( "Server accept a client: ip = %s\n",inet_ntoa(client_addr.sin_addr) ); //打印客户端ip /* 创建子进程处理客户端请求 */ if ( (pid = fork()) == 0 ) { while ( 1 ) { /* 接受客户端信息 */ if ( (recv_bits = recv(connfd, buffer, BUF_SIZE, 0)) < 0 ) printf( "recv failed" ); /* 去除最后一个字符 */ buffer[recv_bits - 1] = '\0'; /* 用户名鉴定 */ if ( recv_type == USERNAME ) { if ( strcmp(name, buffer) != 0 ) { if ( send(connfd, error_user,strlen(error_user), 0) < 0 ) printf( "send failed" ); break; }else{ if ( send(connfd, correct_user,strlen(correct_user), 0) < 0 ) printf( "send failed" ); } printf("%s正在登录:",buffer); recv_type = PASSWORD; //设置标志为密码鉴定模式 } /* 密码鉴定 */ else if ( recv_type == PASSWORD ) { if ( strcmp(passwd, buffer) != 0 ) { //验证密码 if ( send(connfd, error_password,strlen(error_password), 0) < 0 ) printf( "send failed" ); } else { if ( send(connfd, success_login, strlen(success_login), 0) < 0 ) printf( "send failed" ); printf("用户登录成功!\n"); recv_type = COMMAND; //设置标志为命令模式 } } /* 命令模式 */ else if( recv_type == COMMAND ) { command(connfd,buffer); puts("客户端输入的命令为:"); puts(buffer); } } close( sockfd ); close( connfd ); exit ( 0 ); } else close( connfd ); } return 0; }
7.2客户端
#include#include #include #include <string.h> #include #include #include #include #include #include in.h> #define TRUE 1 #define FALSE -1 #define BUF_SIZE 1024 //登录过程状态标识 char *error_user = "error user name"; char *error_password = "error password"; char *correct_user = "correct user"; char *success_login = "correct password"; char *exit_command = "exit"; char *exit_tip="退出连接!"; /* * 介绍:客户端 * 功能: * 1.登录服务器端 * 2.登录后在服务器端对文件进行操作 */ int send_recv( int connfd,char flag ) { /* * 作用:客户端与服务器端信息的处理 * connfd: 服务器端标识 * flag: 输入类比标识 */ char input_buf[ BUF_SIZE ]; //定义字符串存放输入信息 char recv_buf[ BUF_SIZE ]; //定义字符串存放服务器端返回信息 memset(input_buf, 0, sizeof(input_buf)); memset(recv_buf, 0, sizeof(recv_buf)); fgets( input_buf, BUF_SIZE, stdin ); //获取用户输入 input_buf[strlen(input_buf) - 1] = '\0'; //去除最后一个输入的确认 /* 向服务器发送消息 */ if ( send(connfd, input_buf, BUF_SIZE, 0) < 0 ) printf( "send failed" ); /* 从服务器接受消息 */ if ( recv(connfd, recv_buf, BUF_SIZE, 0) < 0 ) printf( "recv failed" ); /* 用户名处理 */ if ( flag == 'u' ) { if ( strcmp(recv_buf, error_user) == 0 ) { //用户名错误 puts( error_user ); return FALSE; } } /* 密码处理 */ else if ( flag == 'p' ) { if ( strcmp(recv_buf, error_password) == 0 ) { puts( error_password ); //密码错误 return FALSE; } } /* 命令处理 */ else if( flag == 'c' ) { if ( strcmp(recv_buf, exit_command) == 0 ) { //退出 printf("连接已断开\n"); return FALSE; } puts(recv_buf); //命令结果输出 } return TRUE; } int main( int argc, char *argv[] ) { int connfd; int serv_port; char recv_buf[ BUF_SIZE ]; struct sockaddr_in serv_addr; if ( argc != 3 ) { printf( "输入格式为: ./c [ip] [port]\n" ); exit ( 1 ); } memset( &serv_addr, 0, sizeof(struct sockaddr_in) );//置零 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons( atoi(argv[2]) ); inet_aton( argv[1], &serv_addr.sin_addr ); if ( serv_addr.sin_port == 0 ||serv_addr.sin_addr.s_addr == 0 ) { fprintf( stderr, "输入格式为: ./c [ip] [port]\n" ); exit ( 1 ); } connfd = socket( AF_INET, SOCK_STREAM, 0 ); //创建socket if ( connfd < 0 ) printf( "socket failed" ); if ( connect(connfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) < 0 ) //连接服务器 { printf( "connect failed" ); exit(0); } printf("请输入用户名:"); /*输入操作*/ if ( send_recv( connfd, 'u' ) == TRUE ) { printf("请输入密码:"); if ( send_recv( connfd,'p' )== TRUE ) { while(1) { printf("guest@ubantu:"); if ( send_recv( connfd, 'c' )== FALSE ) { close( connfd ); exit(0); } } } } close( connfd ); return 0; }