计算机网络课程设计之TELNET 终端设计与实现

前言

Telnet设计是一个比较麻烦的东东,因为Telnet服务器需要部署,而且网络上的资料比较少,最后通过在云服务器CentOS上安装Telnet服务器然后自己的程序作为一个Telnet客户端测试成功,记录结果,重点测试过程放在总体设计中 附录附上Telnet码

参考博客:
https://www.cnblogs.com/suni/p/11528480.html
https://www.cnblogs.com/image-eye/archive/2012/03/28/2421726.html

白嫖容易,创作不易,本文原创,转载请注明!!!
源码和可运行程序:
链接:https://pan.baidu.com/s/1A9KctmpP2JJgyW2wLrehIg
提取码:Lin2

计算机网络课程设计:
计算机网络课程设计之网络聊天程序的设计与实现
计算机网络课程设计之Tracert与Ping程序设计与实现
计算机网络课程设计之基于 IP 多播的网络会议程序
计算机网络课程设计之网络嗅探器的设计与实现
计算机网络课程设计之电子邮件客户端程序设计与实现
计算机网络课程设计之TELNET 终端设计与实现
计算机网络课程设计之网络代理服务器的设计与实现
计算机网络课程设计之简单 Web Server 程序的设计与实现

Qt入门系列:
Qt学习之C++基础
Qt学习之Qt安装
Qt学习之Qt基础入门(上)
Qt学习之Qt基础入门(中)
Qt学习之Qt基础入门(下)

创作不易,整个课程设计程序3000多行代码,所有实验都写在了一个程序中,时间有限,能力不足,转载望注明!!!
本文链接
个人博客:https://ronglin.fun/archives/274
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/122510563

实验题目

TELNET 终端设计与实现

实验目的

参照 RFC854、RFC855 文档,设计一个 TELNET 终端程序。

总体设计

(含背景知识或基本原理与算法、或模块介绍、设计步骤等)

安装Telnet

首先在自己的云服务器上安装Telnet,没有云服务器怎么办呢
安装Linux虚拟机,然后把虚拟机的ip穿透到主机上,虚拟机和主机能实现相互通信即可,然后要记得防火墙开放Telnet默认的23端口
为了方便,我这里直接用自己的云服务器,就免去了ip穿透的过程,注意要在安全组中开发23端口
在CentOS终端中输入

yum install  xinetd
yum install telnet
yum install telnet-server

telnet默认不开启,修改文件/etc/xinetd.d/telnet来开启服务 修改 disable = yes 为 disable = no
如果 telnet文件不存在可以手动创建 添加以下内容

service telnet
{
  flags = REUSE
  socket_type = stream
  wait = no
  user = root
  server =/usr/sbin/in.telnetd
  log_on_failure += USERID
  disable = no
}

启动Telnet 查看运行

service  xinetd restart
ps -ef | grep telnet

Telnet客户端设计

准备工作做好,然后就是根据协议设计Telnet
要更好地理解Telnet数据类型,我们用一个网络调试助手,直接用TCP连接Telnet服务端,看看返回什么格式,如下图:
在这里插入图片描述
可以看到服务器返回了一段十六进制代码

FF FD 18 FF FD 20 FF FD 23 FF FD 27

然后根据博客
https://www.cnblogs.com/image-eye/archive/2012/03/28/2421726.html
博客中有对应的命令解释
我们做出如下理解
Telnet连接之后,服务器会与客户端"协商",例如:

FF FD 18

一个Telnet命令由三部分构成,第一个是IAC 固定值为0xFF,表示是一个Telnet,第二部分是命令码,这里是0xFD,表达的是DO,意为认可选项请求,第三部分为选项码,这里是18,表示18号功能(查文档可以知道18号功能对应什么功能)
这句话的意思是,服务器:我想要求18号功能
然后客户端就要回应,我这里回应的是FF FC 18,意为客户端拒绝你的请求,拒绝18号功能
其他命令同理。
我们看一段我人为联系认证的过程

[2022-01-05 21:15:35.681]# RECV HEX>
FF FD 18 FF FD 20 FF FD 23 FF FD 27

[2022-01-05 21:15:47.899]# SEND HEX>
FF FC 18 FF FC 20 FF FC 23 FF FC 27

[2022-01-05 21:15:47.970]# RECV HEX>
FF FB 03 FF FD 01 FF FD 1F FF FB 05 FF FD 21

[2022-01-05 21:16:14.162]# SEND HEX>
FF FE 03 FF FC 01 FF FC 1F FF FE 05 FF FC 21

[2022-01-05 21:16:14.220]# RECV HEX>
FF FB 03 FF FB 01 0D 0A 4B 65 72 6E 65 6C 20 33 2E 31 30 2E 30 2D 31 31 36 30 2E 34 35 2E 31 2E 65 6C 37 2E 78 38 36 5F 36 34 20 6F 6E 20 61 6E 20 78 38 36 5F 36 34 0D 0A

[2022-01-05 21:16:14.327]# RECV HEX>
4C 69 6E 4C 69 6E 20 6C 6F 67 69 6E 3A 20

可以看到,在最后的接收数据中,[2022-01-05 21:16:14.220]# RECV HEX>这条数据FF FB 01结束之后,就是其他数据了,就是ASCII码了,我们用工具翻译一下

Kernel 3.10.0-1160.45.1.el7.x86_64 on an x86_64
LinLin login:

可以看到"协商"完成

详细设计

(含主要的数据结构、程序流程图、关键代码等)
这样就好理解了,主要就是TCP连接,然后用代码和Telnet服务器"协商",协商完成之后,直接用TCP做数据交互即可
连接TCP服务器

//ip为远程服务器的地址 default_port = 23
socket->connectToHost(this->ip,this->default_port);

然后就是和服务器协商代码,我这里为了方便,将服务器的所有请求命令全部拒绝

QByteArray MyTelnet::response(QByteArray data)
{
//FF FD 18 -> FF FC 18
    QByteArray result;
    result.resize(data.size());
    for(int i=0;i<data.size();++i)  //所有请求都不允许
    {
        if(data[i] == 0xFE)    //DONT -- WONT 发送者希望对方使某选项无效,接收者必须接受该请求
        {
            result[i] = 0xFC;
        }
        else if(data[i] == 0xFC)    //WONT -- DONT 发送者希望某选项无效,接收者必须接受该请求
        {
            result[i] = 0xFE;
        }
        else if(data[i] == 0xFD)    //DO -- DONT 发送者希望接收者激活某选项 接收者拒绝请求
        {
            result[i] = 0xFC;
        }
        else if(data[i] == 0xFB)    //WILL -- DONT 发送者想激活某功能 接收者拒绝该选项
        {
            result[i] = 0xFE;
        }
        else
        {
            result[i] = data[i];
        }
    }
    qDebug()<<"result: "<<result;
    return result;
}

然后需要注意的是,有些TCP传回来的数据,是既有Telnet命令也有ASCII数据的,例如上边[2022-01-05 21:16:14.220]# RECV HEX>这条数据,就既有命令,也有数据,因此我写了两个函数用来分离命令和数据,大概代码如下

QByteArray data = socket->readAll();
    qDebug()<<data;
    int result_write = -1;
    if(data.contains((char)(0xFF))) //命令
    {
        //endIACIndex()函数用来寻找最后一个0xFF的位置
        int IAC_index = endIACIndex(data);
        if(data.size() - IAC_index-1 >2)  //有命令也有数据
        {
            //截取命令部分,发送数据给服务器进行互交
            result_write = socket->write(response(QByteArray(data).mid(0,IAC_index+3)));
            //\xFF\xFB\x03\xFF\xFB\x01\r\nKernel
            //删除前边的命令和\r\n
            //截取数据部分,将数据发送给前端界面显示
            emit this->sendData(QByteArray(data).right(data.size()-(IAC_index+4+1)));
        }
        else
        {
            //发送数据给服务器进行互交
            result_write = socket->write(response(data));
        }

    }
    else
    {
        //将数据发送给前端界面显示
        emit this->sendData(data);
    }

然后的数据就都是ASCII码了

实验结果与分析

运行程序
在这里插入图片描述
输入自己云服务器的域名,如果是虚拟机的话,输入虚拟机的穿透成功之后的ip
点击连接,然后登录验证,然后输入ls /ls命令查看效果
在这里插入图片描述
可以看到命令可用,成功

小结与心得体会

对Telnet有了更深入的理解,同时对于基于TCP的协议也有了更深入的理解,当遇到一个基于TCP的协议的时候,可以先用最普通的TCP连接查看数据格式,然后根据开放的协议文档研究,获益匪浅
=w=

文本数据

//命令码: 一系列定义:(最常用的250-254)
const unsigned char IAC = 0xFF;    //命令解释符.每条指令的前缀 255
const unsigned char DONT = 0xFE;   //拒绝选项请求 254
const unsigned char DO = 0xFD;     //认可选项请求 253
const unsigned char WONT = 0xFC;   //拒绝启动选项 252
const unsigned char WILL = 0xFB;   //同意启动(enable)选项 251
const unsigned char SB = 0xFA;     //子选项开始 250
const unsigned char GA = 0xF9;     //继续进行
const unsigned char EL = 0xF8;     //删除行
const unsigned char EC = 0xF7;     //转义字符
const unsigned char AYT = 0xF6;    //对方是否还在运行?
const unsigned char AO = 0xF5;     //异常中止输出
const unsigned char IP = 0xF4;     //中断进程
const unsigned char BRK = 0xF3;    //中断
const unsigned char DM = 0xF2;     //数据标记
const unsigned char NOP = 0xF1;    //无操作
const unsigned char SE = 0xF0;     //自选项结束
const unsigned char EOR = 0xEF;    //记录结束符
const unsigned char ABORT = 0xEE;  //异常中止进程
const unsigned char SUSP = 0xED;   //挂起当前进程(作业控制)
const unsigned char eof = 0xEC;    //文件结束符

//1)WILL:发送方本身将激活选项
//2)DO:发送方想叫接受端激活选项
//3)WONT:发送方本身想禁止选项
//4)DONT:发送方想让接受端去禁止选项
//WILL -- DO 发送者想激活某功能 接收者接受该选项
//WILL -- DONT 发送者想激活某功能 接收者拒绝该选项
//DO -- WILL 发送者希望接收者激活某选项 接收者接受请求
//DO -- DONT 发送者希望接收者激活某选项 接收者拒绝请求
//WONT -- DONT 发送者希望某选项无效,接收者必须接受该请求
//DONT -- WONT 发送者希望对方使某选项无效,接收者必须接受该请求

//选项码
//1-回显
//3-抑制继续进行
//5-状态
//6-定时标记
//24-终端类型
//31-窗口大小
//32-终端速度
//33-远程流量控制
//34-行方式
//36-环境变量

//FF FD 18 FF FD 20 FF FD 23 FF FD 27
//255 253 24 :DO 终端类型
//255 253 32 :DO 终端速度
//255 253 35 :DO 行方式
//255 253 39

//[2022-01-05 21:15:35.681]# RECV HEX>
//FF FD 18 FF FD 20 FF FD 23 FF FD 27

//[2022-01-05 21:15:47.899]# SEND HEX>
//FF FC 18 FF FC 20 FF FC 23 FF FC 27

//[2022-01-05 21:15:47.970]# RECV HEX>
//FF FB 03 FF FD 01 FF FD 1F FF FB 05 FF FD 21

//[2022-01-05 21:16:14.162]# SEND HEX>
//FF FE 03 FF FC 01 FF FC 1F FF FE 05 FF FC 21

//[2022-01-05 21:16:14.220]# RECV HEX>
//FF FB 03 FF FB 01 0D 0A 4B 65 72 6E 65 6C 20 33 2E 31 30 2E 30 2D 31 31 36 30 2E 34 35 2E 31 2E 65 6C 37 2E 78 38 36 5F 36 34 20 6F 6E 20 61 6E 20 78 38 36 5F 36 34 0D 0A

//[2022-01-05 21:16:14.327]# RECV HEX>
//4C 69 6E 4C 69 6E 20 6C 6F 67 69 6E 3A 20

猜你喜欢

转载自blog.csdn.net/RongLin02/article/details/122510563