网络版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. android Handler及消息处理机制的简单介绍

    学习android线程时,直接在UI线程中使用子线程来更新TextView显示的内容,会有如下错误:android.view.ViewRoot$CalledFromWrongThreadExcepti ...

  2. php+jquery+ajax+json的一个最简单实例

    html页面: <html> <head> <meta http-equiv="content-type" content="text/ht ...

  3. asp.net 中的错误跳转 customerrors 对html文件不起作用

    在配置web.config时发现customerrors对aspx文件是起作用的,我想通过customerrors来判断是否有html文件时,却不起作用? 这是为什么,如果要起作用.net里该如何操作 ...

  4. CodeForces 19D Points

    Pete and Bob invented a new interesting game. Bob takes a sheet of paper and locates a Cartesian coo ...

  5. Linux中部署JAVA程序

    JAVA程序在开发完成后,需要部署到服务器,如果是WEB项目,需要部署到WEB服务器,否则部署到应用服务器. JAVA是跨平台的编程语言,服务器的操作系统可以是Windows.Linux或者其它,下面 ...

  6. html p标签换行问题

    /*p标签自动换行*/ p{ word-wrap:break-word; word-break:normal; } /*p强制不换行*/ p{ white-space:nowrap; } /*块级元素 ...

  7. Django架设blog步骤

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  8. EL表达式 functions String处理函数

    01.uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>  02.上面的 uri 根据 ...

  9. Eclipse+Java+OpenCV246人脸识别

    1.环境搭建:见上一篇博客 整个项目的结构图: 2.编写DetectFaceDemo.java,代码如下: package com.njupt.zhb.test; import org.opencv. ...

  10. LRU Cache的简单c++实现

    什么是 LRU LRU Cache是一个Cache的置换算法,含义是“最近最少使用”,把满足“最近最少使用”的数据从Cache中剔除出去,并且保证Cache中第一个数据是最近刚刚访问的,因为这样的数据 ...