您的位置:首页 > 理论基础 > 计算机网络

网络版shell之网络编程练习篇--telnet服务端

2013-06-25 17:25 288 查看
以前写过一个shell命令解释器,对与shell命令解释器的执行流程有了清晰的认识,这段时间学习网络编程,至于网络编程的细节以及知识点,已经在上一遍博客中,转载了从网上摘的文章,基本概括了网络编程的主要api,而对于程序员,更重要的是解决实际问题的能力,所以练习是非常重要的,现在,我们在一起shell命令解释器的基础上,写一个基于socket网络编程的网络版shell命令解释器,也可以称之为telnet服务端。
telnet服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "my_type.h" //外部头文件 申明了一个函数体 exec_cmd()
int main(void)
{
char buf[2048];//为了保持程序简短,便于阅读,省去了错误判断。
int socket_fd;
struct sockaddr_in netaddr;
unsigned short int port=8000;
socket_fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
bzero(buf,2048);
bzero(&netaddr,sizeof(struct sockaddr_in));//为了保持程序简短,便于阅读,省去了错误判断。
netaddr.sin_family=AF_INET;
netaddr.sin_port=htons(port);
netaddr.sin_addr.s_addr=htonl(INADDR_ANY);
bind(socket_fd,(struct sockaddr *)&netaddr,sizeof(struct sockaddr));//在实际代码中,已加入了错误判断
listen(socket_fd,3);
while(1)
{
int connfd;
int i=0;
char ch=0;
char os_chose[4];
int len=sizeof(struct sockaddr);
struct sockaddr_in cliaddr;
char buf_in[2048];
bzero(os_chose,4);
bzero(&cliaddr,sizeof(struct sockaddr_in));
printf("port = %d \n",ntohs(netaddr.sin_port));
start:
connfd=accept(socket_fd,(struct sockaddr *)&cliaddr,&len);//监听端口 等待客户端连接
printf(" accept is over, remote ip = \n ");
while(1)
{
strcpy(buf," chose your system : \r\n");
write(connfd,buf,30);
strcpy(buf,"\r [1] windons \n");
write(connfd,buf,60);
strcpy(buf,"\r [2] linux \n");
write(connfd,buf,60);
read(connfd,os_chose,4);

/*window 与linux 有一些小区别,linux中,换行会自动回车,

而window只会换行而不会自动回车,所以如果对于从linux中获取的命令输出,

不做处理的话,在window终端下,会呈现梯形显示 */
if( strpbrk(os_chose,"1")!=NULL )
{
ch='1';
printf("window \n");
break;
}
else if( strpbrk(os_chose,"2")!=NULL )
{
ch='2';
printf(" linux \n");
break;
}
// window的telnet程序,按每个字符发送,而首字符往往不是输入的字符,可能是\0,没有去深究,

直接捕获输入的前4个字符,

然后检查是否1 或者2 来判断登陆者选择的系统类型。
}
strcpy(buf,"\r\ntelnet_shell>>");
write(connfd,buf,20);   //输入提示符
bzero(&buf,2048);
while(1)
{
char tmp_ch=0;
if(connfd>=0)
{
if(ch=='2')//linux的telnet客户端,以回车符作为命令的结束,与window不同,

要想去别对待,这样服务端获取的就是一个字符串
{
bzero(&buf,2048);
read(connfd,buf,2048);
if( exec_cmd(buf,connfd)==10 )//my_type.h  申明的函数,主要解释输入的字符串命令,执行命令后,将标准输出和错误输出都重定向到网络中。
goto start;
bzero(&buf,2048);
strcpy(buf,"\rtelnet_shell>>");
write(connfd,buf,20);
bzero(&buf,2048);
}
if(ch=='1')
{ // window的telnet客户端,按单个字符发送命令,所以要对输入的单个字符重新组合。
read(connfd,&tmp_ch,1);
if(tmp_ch=='\0'&&i==0)
{}
else if(tmp_ch!='\n')
buf_in[i++]=tmp_ch;
else if(tmp_ch=='\n'&&i!=0)
{
printf("%s \n",buf_in);
buf_in[i]=' ';
buf_in[i+1]=' ';
buf_in[i+2]='\n';
if( exec_cmd(buf_in,connfd)==10 )
goto start;
bzero(&buf_in,2048);
strcpy(buf_in,"\rtelnet_shell>>");
write(connfd,buf_in,20);
bzero(&buf_in,2048);
i=0;
}
}
}
}
}
return 0;
}
my_type.h 与exec_cmd()函数

#include "my_type.h"
int exec_cmd(char *p,int fd)
{
char *argv[5];
char cmd_data[5][10];
char tmp_p[200];
char *token;
char *p_tmp;
pid_t pid;
int id=0,i=0,j=0;
int pipe_fd[2];
char buf_cmd[2048];
memset(buf_cmd,'\0',2048);
if( (pipe(pipe_fd))==-1 )
{
perror("pipe init error");
return -1;
}
memset(tmp_p,'\0',200);
memset(cmd_data,'\0',10*5);
memset(argv,'\0',5);
strcpy(tmp_p,p);
p_tmp=tmp_p;
while( (*p_tmp)!='\n' )
{
if( ((*p_tmp)=='\r') )
{
*p_tmp=' ';
*(p_tmp+1)='\r';
*(p_tmp+2)='\n';
break;
}
++p_tmp;
}    //以空格符为字符串分割符有一个缺点,就是会把字符串的末尾符号\r\n也认为是字符串,而在exec时,无法正确执行命令,所以不管字符串末尾是否有空格,都统一在\r\n前加入空格符。
token=strtok(tmp_p," ");
while(token!=NULL)
{
if( (*token!=' ')&&(*token!='\n')&&(*token!='\0') )
strcpy(cmd_data[id++],token);
token=strtok(NULL," ");
}
cmd_data[id-1][strlen(cmd_data[id-1])-1]='\0';
for(j=0;j<id-1;j++)
{
argv[j]=cmd_data[j];
}
argv[j]=(char *)NULL;
if( strcmp(argv[0],"cd")==0)
{
puts("cmd is cd ");
chdir(argv[1]);
return 0 ;
}
else if( strcmp(argv[0],"exit")==0 )
{
puts("cmd is exit ");
puts("bye bye ! \n");
close(fd);
return 10;
}
if( (pid=fork())==-1)
{
perror("fork error");
exit(1);
}
if( pid==0 )
{
close(pipe_fd[0]);//通过无名管道对stdou、stderr输出重定向
dup2(pipe_fd[1],fileno(stderr));
dup2(pipe_fd[1],fileno(stdout));
execvp(argv[0],argv);
close(pipe_fd[1]);
exit(0);
}
else
{
char *buf_p=0,buf_ch=0;
int buf_i=0;
char tmp[2048];
close(pipe_fd[1]);
memset(buf_cmd,'\0',2048);
memset(tmp,'\0',2048);//读取子进程写入到管道中的信息
read(pipe_fd[0],buf_cmd,2048);
buf_p=buf_cmd;
puts(buf_cmd);//linux和window都统一起来,虽然linux对于换行符的处理是换行和自动回车,这里为了方便起见,都统一加入回车符。这样就可以在window下完美显示了。
while( buf_cmd[buf_i]!='\0')
{
tmp[buf_i]=buf_cmd[buf_i++];
if( buf_cmd[buf_i]=='\n' )
{
tmp[buf_i-1]='\r';
tmp[buf_i++]='\n';
}
}
write(fd,tmp,2048);
waitpid(pid,NULL,NULL);
return 0;
}

}
my_type.h文件:

#ifndef __my_type__
#define __my_type__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
extern int exec_cmd(char *p,int fd);
#endif
阅读(1538) | 评论(0) | 转发(0) |

0
上一篇:Linux下Socket编程【作者不详】

下一篇:规范链表操作

相关热门文章

自己用的ubuntu环境搭建(一)...

Twisted用户验证之服务端详解...

一次tuxedo重启服务失败的处理...

打印struct in_addr / sockadd...

Redis源码简要分析

test123

编写安全代码——小心有符号数...

使用openssl api进行加密解密...

一段自己打印自己的c程序...

sql relay的c++接口

GCC编译命令

推荐系统常用算法

C++Primer笔记 第八章 标准IO...

c语言中的#号和##号的作用...

gstreamer插件开发-------sink...

给主人留下些什么吧!~~

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: