LwIP应用开发笔记之九:LwIP无操作系统TELNET服务器
前面我們已經(jīng)實(shí)現(xiàn)了基于RAW API的TCP服務(wù)器和客戶端,也在此基礎(chǔ)上實(shí)現(xiàn)了HTTP應(yīng)用。接下來(lái)我們實(shí)現(xiàn)一個(gè)基于TCP的Telnet服務(wù)器應(yīng)用。
1、Telnet協(xié)議簡(jiǎn)介
Telnet協(xié)議是TCP/IP協(xié)議族中的一員,是Internet遠(yuǎn)程登陸服務(wù)的標(biāo)準(zhǔn)協(xié)議和主要方式。它為用戶提供了在本地計(jì)算機(jī)上完成遠(yuǎn)程主機(jī)工作的能力。在終端使用者的電腦上使用telnet程序,用它連接到服務(wù)器。終端使用者可以在telnet程序中輸入命令,這些命令會(huì)在服務(wù)器上運(yùn)行,就像直接在服務(wù)器的控制臺(tái)上輸入一樣。可以在本地就能控制服務(wù)器。要開始一個(gè)telnet會(huì)話,必須輸入用戶名和密碼來(lái)登錄服務(wù)器。Telnet是常用的遠(yuǎn)程控制Web服務(wù)器的方法。
Telnet是位于OSI模型的第7層---應(yīng)用層上的一種協(xié)議,是一個(gè)通過(guò)創(chuàng)建虛擬終端提供連接到遠(yuǎn)程主機(jī)終端仿真的TCP/IP協(xié)議。這一協(xié)議需要通過(guò)用戶名和口令進(jìn)行認(rèn)證,是Internet遠(yuǎn)程登陸服務(wù)的標(biāo)準(zhǔn)協(xié)議。應(yīng)用Telnet協(xié)議能夠把本地用戶所使用的計(jì)算機(jī)變成遠(yuǎn)程主機(jī)系統(tǒng)的一個(gè)終端。它提供了三種基本服務(wù):
- Telnet定義一個(gè)網(wǎng)絡(luò)虛擬終端為遠(yuǎn)程系統(tǒng)提供一個(gè)標(biāo)準(zhǔn)接口。客戶機(jī)程序不必詳細(xì)了解遠(yuǎn)程系統(tǒng),他們只需構(gòu)造使用標(biāo)準(zhǔn)接口的程序;
- Telnet包括一個(gè)允許客戶機(jī)和服務(wù)器協(xié)商選項(xiàng)的機(jī)制,而且它還提供一組標(biāo)準(zhǔn)選項(xiàng); .
- Telnet對(duì)稱處理連接的兩端,即Telnet不強(qiáng)迫客戶機(jī)從鍵盤輸入,也不強(qiáng)迫客戶機(jī)在屏幕上顯示輸出。
2、TELNET服務(wù)器的設(shè)計(jì)
Telnet是一種基于TCP實(shí)現(xiàn)的遠(yuǎn)程登錄方式,Telnet協(xié)議也分配有固定端口23,在這里我們就是用這一端口來(lái)實(shí)現(xiàn)一個(gè)Telnet服務(wù)器。這個(gè)服務(wù)器可以提供給多個(gè)客戶端訪問(wèn)。
我們要實(shí)現(xiàn)的這個(gè)Telnet服務(wù)器是比較簡(jiǎn)單的一個(gè)設(shè)計(jì)。當(dāng)客戶端成功鏈接到服務(wù)器后,服務(wù)器就會(huì)提示用戶登錄,成功登陸后就可以向服務(wù)器發(fā)送命令,當(dāng)發(fā)送不同的命令時(shí),服務(wù)器給出不同的響應(yīng)。具體的操作流程設(shè)計(jì)如下:
從上面的流程圖看其實(shí)我們?cè)O(shè)計(jì)的Telnet服務(wù)器功能已經(jīng)非常明確了。但有兩點(diǎn)需要描述一下。首先是關(guān)于連接狀態(tài)的設(shè)定,在這里我們只是簡(jiǎn)單的將狀態(tài)定義為兩種:已登錄和未登錄。如果已登錄則按命令交互來(lái)解析。如果未登錄則按登錄密碼來(lái)解析。
另一方面,為了實(shí)現(xiàn)命令交互,我們需要為Telnet服務(wù)器設(shè)定命令。我們簡(jiǎn)單的設(shè)定6種命令:"hello"、"date"、"time"、"version"、"quit"與"help"等命令。事實(shí)上我們實(shí)現(xiàn)Telnet服務(wù)器主要就是處理:如何接收和響應(yīng)這些命令。
3、TELNET服務(wù)器的實(shí)現(xiàn)
我們已經(jīng)設(shè)計(jì)了Telnet服務(wù)器的基本功能。接下來(lái)就是如何實(shí)現(xiàn)它了。我們已經(jīng)有前面實(shí)現(xiàn)TCP服務(wù)器的基礎(chǔ)。所以實(shí)現(xiàn)他的重點(diǎn)就是我們?cè)O(shè)計(jì)的Telnet服務(wù)器了。
我們依然采用實(shí)現(xiàn)普通TCP服務(wù)器結(jié)構(gòu)來(lái)實(shí)現(xiàn)Telnet服務(wù)器,只是在信息處理回調(diào)函數(shù)上更復(fù)雜一點(diǎn)。還有就是端口方面我們采用Telnet的慣用端口。首先必然是Telnet服務(wù)器的初始化。
/* TELNET服務(wù)器初始化配置*/ void Telnet_Server_Initialization(void) {struct tcp_pcb *pcb;????? ??????????? ??????????/* 生成一個(gè)新的TCP控制塊 */pcb = tcp_new();??? ??????????????? ????????? ?????/* 控制塊邦定到本地IP和對(duì)應(yīng)端口 */tcp_bind(pcb, IP_ADDR_ANY, TCP_TELNET_SERVER_PORT);??????/* 服務(wù)器進(jìn)入偵聽狀態(tài) */pcb = tcp_listen(pcb);???????????????????????/* 注冊(cè)服務(wù)器accept回調(diào)函數(shù) */tcp_accept(pcb, TelnetServerAccept); ?????????????????????????????????????? }其實(shí)初始化部分就是我們已經(jīng)熟悉的TCP服務(wù)器的初始化,只是使用了Telnet的慣用端口。接下來(lái)就是實(shí)現(xiàn)在初始化中注冊(cè)的Telnet服務(wù)器接收回調(diào)函數(shù)。該函數(shù)為tcp_accept_fn類型,注冊(cè)到了監(jiān)聽控制塊的accept字段。在服務(wù)器上有新連接建立時(shí)就會(huì)被內(nèi)核調(diào)用。
/* TELNET接收回調(diào)函數(shù),客戶端建立連接后,本函數(shù)被調(diào)用 */ static err_t TelnetServerAccept(void *arg, struct tcp_pcb *pcb, err_t err) {????u32_t remote_ip;char linkInfo [100];u8_t iptab[4];telnet_conn_arg *conn_arg = NULL;remote_ip = pcb->remote_ip.addr;iptab[0] = (u8_t)(remote_ip >> 24);iptab[1] = (u8_t)(remote_ip >> 16);iptab[2] = (u8_t)(remote_ip >> 8);iptab[3] = (u8_t)(remote_ip);//生成登錄提示信息sprintf(linkInfo, "Welcome to Telnet! your IP:Port --> [%d.%d.%d.%d:%d]\r\n", \iptab[3], iptab[2], iptab[1], iptab[0], pcb->remote_port);??conn_arg = mem_calloc(sizeof(telnet_conn_arg), 1);if(!conn_arg){return ERR_MEM;}conn_arg->state = TELNET_SETUP;conn_arg->client_port = pcb->remote_port;conn_arg->bytes_len = 0;memset(conn_arg->bytes, 0, MAX_MSG_SIZE);tcp_arg(pcb, conn_arg);/* 注冊(cè)Telnet服務(wù)器連接錯(cuò)誤回調(diào)函數(shù) */tcp_err(pcb, TelnetServeConnectError);/* 注冊(cè)Telnet服務(wù)器消息處理回調(diào)函數(shù)*/tcp_recv(pcb, TelnetServerCallback);/* 連接成功,發(fā)送登錄提示信息 */?tcp_write(pcb, linkInfo, strlen(linkInfo), 1);tcp_write(pcb, LOGIN_INFO, strlen(LOGIN_INFO), 1);return ERR_OK; }在這個(gè)函數(shù)中,我們實(shí)現(xiàn)的功能主要是三方面:注冊(cè)Telnet服務(wù)器消息處理回調(diào)函數(shù);注冊(cè)Telnet服務(wù)器連接錯(cuò)誤回調(diào)函數(shù);初始化Telnet服務(wù)器的狀態(tài)。這個(gè)初始化是在連接建立后,Telnet服務(wù)器與客戶端的交互初始化,比如登錄狀態(tài),用戶提示等。
在上面的函數(shù)中,我們注冊(cè)了兩個(gè)回調(diào)函數(shù),接下來(lái)必然就是實(shí)現(xiàn)這兩個(gè)函數(shù)。我們先來(lái)實(shí)現(xiàn)Telnet服務(wù)器信息處理回調(diào)函數(shù)。這個(gè)函數(shù)其實(shí)就是我們前面注冊(cè)過(guò)的TCP服務(wù)器數(shù)據(jù)接收處理函數(shù)。這個(gè)函數(shù)是tcp_recv_fn類型。這是使用RAW API實(shí)現(xiàn)TCP服務(wù)器最重要的函數(shù),因?yàn)槲覀儗?shí)現(xiàn)的TCP服務(wù)器究竟有什么功能,完全依賴于這個(gè)函數(shù)及其所調(diào)用的函數(shù)。
/* TELNET服務(wù)器信息處理回調(diào)函數(shù),在有消息需要處理時(shí),調(diào)用此函數(shù) */ static err_t TelnetServerCallback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {telnet_conn_arg *conn_args = (telnet_conn_arg *)arg;char sndbuf[50];int strlen = 0;int ret = 0;if(NULL == conn_args || pcb->remote_port != conn_args->client_port){if(p!= NULL){pbuf_free(p);}return ERR_ARG;}if (p != NULL){???????/* 更新接收窗口 */tcp_recved(pcb, p->tot_len);ret = TelnetCommandInput(pcb, conn_args, p);if(ret == 1)//是完整命令{switch(conn_args->state){case TELNET_SETUP:{if(strcmp(conn_args->bytes,PASSWORD) == 0)//密碼正確{strlen = sprintf(sndbuf,"##Hello! This is an LwIP-based Telnet Server##\r\n");tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);strlen = sprintf(sndbuf,"##Created by Moonan...??????? ??????????????##\r\n");tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);strlen = sprintf(sndbuf,"##Enter help for help.? Enter quit for quit.##\r\n");tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);strlen = sprintf(sndbuf,"LwIP Telnet>");tcp_write(pcb,sndbuf,strlen, 1);conn_args->state = TELNET_CONNECTED;//轉(zhuǎn)換狀態(tài)}else//密碼錯(cuò)誤,提示重新登錄{strlen = sprintf(sndbuf,"##PASSWORD ERROR! Try again:##\r\n");tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);}memset(conn_args->bytes, 0, MAX_MSG_SIZE);conn_args->bytes_len = 0;break;}case TELNET_CONNECTED:{if(TelnetCommandParse(pcb, conn_args->bytes) == 0){memset(conn_args->bytes, 0, MAX_MSG_SIZE);conn_args->bytes_len = 0;}else{/* 服務(wù)器關(guān)閉連接 */ServerCloseTelnetConnection(pcb);}break;}default:{break;}}}pbuf_free(p);}?else if (err == ERR_OK){/* 服務(wù)器關(guān)閉連接 */ServerCloseTelnetConnection(pcb);}return ERR_OK;}在這個(gè)函數(shù)中,我們實(shí)現(xiàn)了Telnet服務(wù)器的各種功能,如登錄驗(yàn)證,命令檢查,命令響應(yīng)等。已經(jīng)具備一個(gè)Telnet服務(wù)器的基本框架。接下來(lái)還要實(shí)現(xiàn)Telnet連接錯(cuò)誤回調(diào)函數(shù)。這個(gè)函數(shù)是tcp_err_fn類型,在這個(gè)程序中主要完成連接異常結(jié)束時(shí)的一些處理,可以釋放一些必要的資源。在這個(gè)函數(shù)被內(nèi)核調(diào)用時(shí),連接實(shí)際上已經(jīng)斷開,相關(guān)控制塊也已經(jīng)被刪除。所以在這個(gè)函數(shù)中我們可以重新初始化連接及其資源。
/* TELNET連接錯(cuò)誤回調(diào)函數(shù),連接故障時(shí)調(diào)用本函數(shù) */ static void TelnetServeConnectError(void *arg, err_t err) {Telnet_Server_Initialization(); }至此,我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Telnet服務(wù)器,當(dāng)然它只是一個(gè)雛形,需要開發(fā)更復(fù)雜的功能則需要修改這幾個(gè)回調(diào)函數(shù)。
4、TELNET服務(wù)器總結(jié)
我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Telnet服務(wù)器。當(dāng)然,我們的目的主要是以此來(lái)學(xué)習(xí)基于LwIP的復(fù)雜的TCP應(yīng)用。事實(shí)上理解了TCP服務(wù)器的實(shí)現(xiàn)機(jī)制,諸如此類基于TCP的高級(jí)應(yīng)用協(xié)議并不是特別復(fù)雜的事情。
歡迎關(guān)注:
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的LwIP应用开发笔记之九:LwIP无操作系统TELNET服务器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿里云服务器安装JDK指南
- 下一篇: LwIP应用开发笔记之十一:LwIP带操