网络版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. CH Round #53-数据备份

    描述 已知有N座办公楼位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备 份.然而,网络电缆的费用很高.当地电信公司仅能为你提供K ...

  2. BZOJ1211: [HNOI2004]树的计数

    1211: [HNOI2004]树的计数 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1245  Solved: 383[Submit][Statu ...

  3. 解决android4.0系统中菜单(Menu)添加Icon无效问题

    本文转载自: http://blog.csdn.net/stevenhu_223/article/details/9705173 在Android4.0系统中,创建菜单Menu,通过setIcon方法 ...

  4. 【No system images installed for this target】的解决方式

    打开eclipse,新建安卓SDK模拟器时,选择完Target之后,再选择CPU/ABI时,默认为No system images installed for this target. 且无法编辑: ...

  5. C++链接器工具错误:LNK2001, LNK2019(转载)

    这是归属于链接器工具错误 这一类. 无法解析的外部符号“symbol” 代码引用了链接器无法在库和对象文件中找到的内容(如函数.变量或标签). 可能的原因 代码请求的内容不存在(例如,符号拼写错误或使 ...

  6. c指针点滴-指针与类型

    #include <stdio.h> #include <stdlib.h> //数据通信 void main() { ; int *p1 = &num; int *p ...

  7. JavaScript 数组中查找符合条件的值

    数组实例的find方法,用于找出第一个符合条件的数组成员.它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员.如果没有符合条件的成员,则返回u ...

  8. stat(),lstat(),fstat() 获取文件/目录的相关信息

    stat 的使用 Linux有个命令,ls -l,效果如下: 这个命令能显示文件的类型.操作权限.硬链接数量.属主.所属组.大小.修改时间.文件名.它是怎么获得这些信息的呢,请看下面的讲解. stat ...

  9. jquery之提示信息

    //生成优惠券并分发 function saveCouponAssign(){ //发行券种 var couponTypeId = $("#couponTypeId").combo ...

  10. tomcat配置数据源

    1.修改conf下的context.xml,在<context>标签中添加: <Resource name="jdbc/soa" auth="Conta ...