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

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

网络版shell之网络编程练习篇--telnet服务端的更多相关文章

  1. Socket网络编程(2)--服务端实现

    中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...

  2. 网络编程基础_4.1TCP_服务端

    TCP_服务端 #include <stdio.h> // 1. 包含必要的头文件和库, 必须位于 windows之前 #include <WinSock2.h> #pragm ...

  3. Python网络编程--Echo服务

    Python网络编程--Echo服务 学习网络编程必须要练习的三个小项目就是Echo服务,Chat服务和Proxy服务.在接下来的几篇文章会详细介绍. 今天就来介绍Echo服务,Echo服务是最基本的 ...

  4. RedHat下安装Telnet服务端及客户端远程连接配置

    Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式.它为用户提供了在本地计算机上完成远程主机工作的能力. 配置之前请确保网络连通,如防火墙影响连接,请先关 ...

  5. socket编程,简单多线程服务端测试程序

    socket编程,简单多线程服务端测试程序 前些天重温了MSDN关于socket编程的WSAStartup.WSACleanup.socket.closesocket.bind.listen.acce ...

  6. winsock 编程(简单客户&服务端通信实现)

    winsock 编程(简单客户&服务端通信实现) 双向通信:Client send message to Server, and if  Server receive the message, ...

  7. CentOS 7.4安装telnet服务端

    CentOS 7.4安装telnet服务端 安装xinetd服务 # yum -y install xinetd 安装telnet-server # yum -y install telnet-ser ...

  8. Socket网络编程-基础篇

    Socket网络编程 网络通讯三要素: IP地址[主机名] 网络中设备的标识 本地回环地址:127.0.0.1 主机名:localhost 端口号 用于标识进程的逻辑地址 有效端口:0~65535 其 ...

  9. [转]12篇学通C#网络编程——第二篇 HTTP应用编程(上)

    本文转自:http://www.cnblogs.com/huangxincheng/archive/2012/01/09/2316745.html 我们学习网络编程最熟悉的莫过于Http,好,我们就从 ...

随机推荐

  1. 深入浅出Node.js (8) - 构建Web应用

    8.1 基础功能 8.1.1 请求方法 8.1.2 路径解析 8.1.3 查询字符串 8.1.4 Cookie 8.1.5 Session 8.1.6 缓存 8.1.7 Basic认证 8.2 数据上 ...

  2. BOT、BT、PPP形式介绍(1)

    BOT.BT.PPP形式介绍 BOT1.什么是BOT     BOT是英文Build-Operate-Transfer的缩写,即“建设-经营-转让”.实质上是基础设施投资.建设和经营的一种方式,以政府 ...

  3. bzoj1619[Usaco2008 Nov]Guarding the Farm 保卫牧场

    Description The farm has many hills upon which Farmer John would like to place guards to ensure the ...

  4. Enum Types

    参考Java的官方tutorial和Doc整理如下. What is Enum An enum type is a special data type. It enables for a variab ...

  5. Decorator学习笔记

    初学者,自己的理解,请各位前辈不吝指正! Decorator,装饰模式,设计模式之一,谈谈我的理解,装饰这个词在我概念中就是给某个事物加上一些美丽的外表,把它变得更加完美.但是装饰是可以随时改变的,可 ...

  6. MySql 查询表字段数

    MySql 查询表字段数 SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='test_cases' AND tab ...

  7. 【转】WCF、WebAPI、WCFREST、WebService之间的区别

    在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我分享一下我对 ...

  8. js计算日期天数差-2013-9-26

    function daymath(sdate, edate) {             var startdate = sdate;             var enddate = edate; ...

  9. LoadRunner监控windows资源报错Monitor name :Windows Resources. Cannot connect to machine

    目标机:被监控的机器,windows server 2008 R2. 测试机:执行control的机器,windows7 操作:在测试机上执行Control,添加windows的监控 问题现象:Mon ...

  10. 解决 Google 重定向,体验 Google 本味

    想要体验原汁原味的 Google(google.com),下面的方案是我用过的较方便的方案. 欢迎更正及补充 Chrome 扩展 Chrone 商店有一款禁止重定向的扩展 NoCountryRedir ...