最近在学习unix网络编程,现在正在学习tcp的通信。其实,只要建立起了tcp通信,操作远端的计算机就不是什么问题了。正向telnet一样,也是基于tcp/IP协议的。所以这个实验,也算是对telnet功能的一种简单的模拟。
但是,值得注意的问题是关机涉及到系统权限,所以要给运行在Server端的程序以足够的权限,这样才可以在接收到Client端的关机请求时,执行关机。
将会模拟如下执行过程:
1. 执行 mytelnet 跟上参数telnet服务器 IP地址 127.0.0.1
2. 输入login 向服务器请求登录,随之服务器会要求输入密码
3. 输入一个错误的登录密码 123
4. 服务器验证不通过,所以返回Fail to login ,please check your password
5. 再次输入密码,这次输入正确的密码123456
6. 服务器验证通过,此时客户端可以执行基于telnet的远程操控
7. 输入操控命令,例如关机命令shutdown
8. 服务器将会执行shutdown 操作。
在编译时使用如下的命令:
Server:
sudo gcc server.c -o server //足够的权限
sudo chmod u+s server
./server
Client:
gcc client.c -o mytelnet
./mytelnet 10.3.1.210
Client端:
#include
<
stdio.h
>
#include < string .h >
#include < arpa / inet.h >
#include < netinet / in .h >
#include < sys / socket.h >
#define MAXLINE 4096
void clientWork(FILE * fp , int sockfd)
{
char sendline[MAXLINE],receiveline[MAXLINE];
int n;
while (fgets(sendline,MAXLINE,fp) != NULL)
{
write(sockfd,sendline,strlen(sendline));
n = read(sockfd,receiveline,MAXLINE);
receiveline[n] = ' \0 ' ;
fputs(receiveline,stdout);
if (strcmp(receiveline, " bye\n " ) == 0 ) break ;
}
}
int main( int argc , char * * argv )
{
int sockfd , n ;
char recvline[ MAXLINE + 1 ];
struct sockaddr_in servaddr;
if ( ( sockfd = socket( AF_INET , SOCK_STREAM , 0 ) ) < 0 ) {
printf( " socket error\n " );
exit( 1 );
}
memset( & servaddr , 0 , sizeof ( servaddr ) );
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons( 2000 ); // 指定Server端的端口号
char * serverAddress = argv[ 1 ];
// 判断指定的ip地址是否有错误
if ( inet_pton( AF_INET ,serverAddress , & servaddr.sin_addr ) <= 0 ) {
printf( " inet_pton error for %s\n " , serverAddress );
exit( 1 );
}
if ( connect( sockfd , ( struct sockaddr * ) & servaddr , sizeof ( servaddr ) ) < 0 ) {
printf( " connect error\n " );
exit( 1 );
}
// 从Terminal中读取用户输入的指令
clientWork(stdin,sockfd);
close(sockfd);
return 0 ;
}
#include < string .h >
#include < arpa / inet.h >
#include < netinet / in .h >
#include < sys / socket.h >
#define MAXLINE 4096
void clientWork(FILE * fp , int sockfd)
{
char sendline[MAXLINE],receiveline[MAXLINE];
int n;
while (fgets(sendline,MAXLINE,fp) != NULL)
{
write(sockfd,sendline,strlen(sendline));
n = read(sockfd,receiveline,MAXLINE);
receiveline[n] = ' \0 ' ;
fputs(receiveline,stdout);
if (strcmp(receiveline, " bye\n " ) == 0 ) break ;
}
}
int main( int argc , char * * argv )
{
int sockfd , n ;
char recvline[ MAXLINE + 1 ];
struct sockaddr_in servaddr;
if ( ( sockfd = socket( AF_INET , SOCK_STREAM , 0 ) ) < 0 ) {
printf( " socket error\n " );
exit( 1 );
}
memset( & servaddr , 0 , sizeof ( servaddr ) );
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons( 2000 ); // 指定Server端的端口号
char * serverAddress = argv[ 1 ];
// 判断指定的ip地址是否有错误
if ( inet_pton( AF_INET ,serverAddress , & servaddr.sin_addr ) <= 0 ) {
printf( " inet_pton error for %s\n " , serverAddress );
exit( 1 );
}
if ( connect( sockfd , ( struct sockaddr * ) & servaddr , sizeof ( servaddr ) ) < 0 ) {
printf( " connect error\n " );
exit( 1 );
}
// 从Terminal中读取用户输入的指令
clientWork(stdin,sockfd);
close(sockfd);
return 0 ;
}
Server端:
#include
<
stdio.h
>
#include < string .h >
#include < arpa / inet.h >
#include < netinet / in .h >
#include < sys / socket.h >
#define MAXSIZE 1024
#define LOGIN 1 // 登录Server
#define BYE 2 // 离开Server
#define PASS 3 // 合法登录
#define DENY 4 // 非法登录
#define SHUTDOWN 5 // 执行关机
#define ERROR -1 // 非法信息
// 记录状态信息
static int STATE = 0 ;
int getClientChoice( char * clientmsg)
{
if (strcmp(clientmsg, " login\n " ) == 0 ) return LOGIN;
if (STATE > 0 )
{
if (strcmp(clientmsg, " bye\n " ) == 0 ) return BYE;
}
if (STATE == LOGIN)
{
// Default password is 123456
if (strcmp(clientmsg, " 123456\n " ) == 0 ) return PASS;
else return DENY;
}
if (STATE == PASS)
{
if (strcmp(clientmsg, " shutdown\n " ) == 0 ) return SHUTDOWN;
else ERROR;
}
return ERROR;
};
// 发送消息
void sendMsg( int sockfd, char * buffer, char * msg)
{
char buffer2[MAXSIZE];
snprintf(buffer2, sizeof (buffer2), " %s " ,msg);
write(sockfd,buffer2, sizeof (buffer2));
}
// 定义关机函数
void myshutdown()
{
// 系统在一分钟后关机
system( " shutdown -t 1 " );
}
int main( int argc , char * * argv )
{
char buffer[MAXSIZE];
int listenfd = socket(AF_INET,SOCK_STREAM, 0 ); // 定义socket,指向ipv4地址的字节流套接口
struct sockaddr_in serverAddr;
memset( & serverAddr, 0 , sizeof (serverAddr)); // sockAddr_in 进行初始化
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons( 2000 );
if (bind(listenfd,( struct sockaddr * ) & serverAddr, sizeof (serverAddr)) ==- 1 )
{
printf( " There is an error during binding\n " );
return - 1 ;
}
else
{
printf( " Bind successfully!!!\n " );
}
// 对listenfd进行监听,从最初建立时的主动套接口(用于进行connect的套接口)转化为被动套接口(接受连接)
listen(listenfd, 100 ); // 第二个参数为套接口排队的最大连接个数
int connectfd;
socklen_t addrlen;
struct sockaddr_in connectAddr;
memset( & connectAddr, 0 , sizeof (connectAddr));
printf( " Be ready to accept a connection!\n " );
while ( 1 )
{
connectfd = accept(listenfd,( struct sockAddr * ) & connectAddr, & addrlen); // 接受client端一个请求的socket
char receivebuffer[MAXSIZE];
int revbuflen;
while ( 1 )
{
revbuflen = read(connectfd,receivebuffer,MAXSIZE);
receivebuffer[revbuflen] = ' \0 ' ;
// printf("%s",receivebuffer);
int clientChoice = getClientChoice(receivebuffer);
// 模拟操作过程
if (clientChoice == LOGIN)
{
STATE = LOGIN;
sendMsg(connectfd,buffer, " Please input your password :\n " );
}
else if (clientChoice == PASS && STATE == LOGIN)
{
STATE = PASS;
sendMsg(connectfd,buffer, " Welcome to my Telnet Server...\n " );
}
else if (clientChoice == SHUTDOWN && STATE == PASS)
{
myshutdown();
sendMsg(connectfd,buffer, " Remote computer is going to shutdown...\n " );
}
else if (clientChoice == BYE)
{
sendMsg(connectfd,buffer, " bye\n " );
break ;
}
else if (clientChoice == DENY)
{
sendMsg(connectfd,buffer, " Fail to login ,please check your password\n " );
}
else if (clientChoice == ERROR)
{
sendMsg(connectfd,buffer, " wrong,Check your input...\n " );
}
}
close(connectfd);
}
close(listenfd); // 虽然因为上面有while(true),这行永远都执行不了,但是时刻注意关闭socket连接应该是个好习惯。
return 0 ;
}
#include < string .h >
#include < arpa / inet.h >
#include < netinet / in .h >
#include < sys / socket.h >
#define MAXSIZE 1024
#define LOGIN 1 // 登录Server
#define BYE 2 // 离开Server
#define PASS 3 // 合法登录
#define DENY 4 // 非法登录
#define SHUTDOWN 5 // 执行关机
#define ERROR -1 // 非法信息
// 记录状态信息
static int STATE = 0 ;
int getClientChoice( char * clientmsg)
{
if (strcmp(clientmsg, " login\n " ) == 0 ) return LOGIN;
if (STATE > 0 )
{
if (strcmp(clientmsg, " bye\n " ) == 0 ) return BYE;
}
if (STATE == LOGIN)
{
// Default password is 123456
if (strcmp(clientmsg, " 123456\n " ) == 0 ) return PASS;
else return DENY;
}
if (STATE == PASS)
{
if (strcmp(clientmsg, " shutdown\n " ) == 0 ) return SHUTDOWN;
else ERROR;
}
return ERROR;
};
// 发送消息
void sendMsg( int sockfd, char * buffer, char * msg)
{
char buffer2[MAXSIZE];
snprintf(buffer2, sizeof (buffer2), " %s " ,msg);
write(sockfd,buffer2, sizeof (buffer2));
}
// 定义关机函数
void myshutdown()
{
// 系统在一分钟后关机
system( " shutdown -t 1 " );
}
int main( int argc , char * * argv )
{
char buffer[MAXSIZE];
int listenfd = socket(AF_INET,SOCK_STREAM, 0 ); // 定义socket,指向ipv4地址的字节流套接口
struct sockaddr_in serverAddr;
memset( & serverAddr, 0 , sizeof (serverAddr)); // sockAddr_in 进行初始化
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons( 2000 );
if (bind(listenfd,( struct sockaddr * ) & serverAddr, sizeof (serverAddr)) ==- 1 )
{
printf( " There is an error during binding\n " );
return - 1 ;
}
else
{
printf( " Bind successfully!!!\n " );
}
// 对listenfd进行监听,从最初建立时的主动套接口(用于进行connect的套接口)转化为被动套接口(接受连接)
listen(listenfd, 100 ); // 第二个参数为套接口排队的最大连接个数
int connectfd;
socklen_t addrlen;
struct sockaddr_in connectAddr;
memset( & connectAddr, 0 , sizeof (connectAddr));
printf( " Be ready to accept a connection!\n " );
while ( 1 )
{
connectfd = accept(listenfd,( struct sockAddr * ) & connectAddr, & addrlen); // 接受client端一个请求的socket
char receivebuffer[MAXSIZE];
int revbuflen;
while ( 1 )
{
revbuflen = read(connectfd,receivebuffer,MAXSIZE);
receivebuffer[revbuflen] = ' \0 ' ;
// printf("%s",receivebuffer);
int clientChoice = getClientChoice(receivebuffer);
// 模拟操作过程
if (clientChoice == LOGIN)
{
STATE = LOGIN;
sendMsg(connectfd,buffer, " Please input your password :\n " );
}
else if (clientChoice == PASS && STATE == LOGIN)
{
STATE = PASS;
sendMsg(connectfd,buffer, " Welcome to my Telnet Server...\n " );
}
else if (clientChoice == SHUTDOWN && STATE == PASS)
{
myshutdown();
sendMsg(connectfd,buffer, " Remote computer is going to shutdown...\n " );
}
else if (clientChoice == BYE)
{
sendMsg(connectfd,buffer, " bye\n " );
break ;
}
else if (clientChoice == DENY)
{
sendMsg(connectfd,buffer, " Fail to login ,please check your password\n " );
}
else if (clientChoice == ERROR)
{
sendMsg(connectfd,buffer, " wrong,Check your input...\n " );
}
}
close(connectfd);
}
close(listenfd); // 虽然因为上面有while(true),这行永远都执行不了,但是时刻注意关闭socket连接应该是个好习惯。
return 0 ;
}