Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1651929
  • 博文数量: 245
  • 博客积分: 10378
  • 博客等级: 上将
  • 技术积分: 2571
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-27 08:19
文章分类

全部博文(245)

文章存档

2013年(4)

2012年(8)

2011年(13)

2010年(68)

2009年(152)

分类: C/C++

2009-04-28 15:19:21

  以前写过一个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

阅读(5660) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~