前面我們已經(jīng)實現(xiàn)了基于RAW API的TCP服務器和客戶端,也在此基礎上實現(xiàn)了HTTP應用。接下來我們實現(xiàn)一個基于TCP的Telnet服務器應用。
1 、 Telnet****協(xié)議簡介
Telnet協(xié)議是TCP/IP協(xié)議族中的一員,是Internet遠程登陸服務的標準協(xié)議和主要方式。它為用戶提供了在本地計算機上完成遠程主機工作的能力。在終端使用者的電腦上使用telnet程序,用它連接到服務器。終端使用者可以在telnet程序中輸入命令,這些命令會在服務器上運行,就像直接在服務器的控制臺上輸入一樣??梢栽诒镜鼐湍芸刂品掌?。要開始一個telnet會話,必須輸入用戶名和密碼來登錄服務器。Telnet是常用的遠程控制Web服務器的方法。
Telnet是位于OSI模型的第7層---應用層上的一種協(xié)議,是一個通過創(chuàng)建虛擬終端提供連接到遠程主機終端仿真的TCP/IP協(xié)議。這一協(xié)議需要通過用戶名和口令進行認證,是Internet遠程登陸服務的標準協(xié)議。應用Telnet協(xié)議能夠把本地用戶所使用的計算機變成遠程主機系統(tǒng)的一個終端。它提供了三種基本服務:
- Telnet定義一個網(wǎng)絡虛擬終端為遠程系統(tǒng)提供一個標準接口??蛻魴C程序不必詳細了解遠程系統(tǒng),他們只需構(gòu)造使用標準接口的程序;
- Telnet包括一個允許客戶機和服務器協(xié)商選項的機制,而且它還提供一組標準選項;.
- Telnet對稱處理連接的兩端,即Telnet不強迫客戶機從鍵盤輸入,也不強迫客戶機在屏幕上顯示輸出。
2 、 TELNET****服務器的設計
Telnet是一種基于TCP實現(xiàn)的遠程登錄方式,Telnet協(xié)議也分配有固定端口23,在這里我們就是用這一端口來實現(xiàn)一個Telnet服務器。這個服務器可以提供給多個客戶端訪問。
我們要實現(xiàn)的這個Telnet服務器是比較簡單的一個設計。當客戶端成功鏈接到服務器后,服務器就會提示用戶登錄,成功登陸后就可以向服務器發(fā)送命令,當發(fā)送不同的命令時,服務器給出不同的響應。具體的操作流程設計如下:
從上面的流程圖看其實我們設計的Telnet服務器功能已經(jīng)非常明確了。但有兩點需要描述一下。首先是關(guān)于連接狀態(tài)的設定,在這里我們只是簡單的將狀態(tài)定義為兩種:已登錄和未登錄。如果已登錄則按命令交互來解析。如果未登錄則按登錄密碼來解析。
另一方面,為了實現(xiàn)命令交互,我們需要為Telnet服務器設定命令。我們簡單的設定6種命令:"hello"、"date"、"time"、"version"、"quit"與"help"等命令。事實上我們實現(xiàn)Telnet服務器主要就是處理:如何接收和響應這些命令。
3 、 TELNET****服務器的實現(xiàn)
我們已經(jīng)設計了Telnet服務器的基本功能。接下來就是如何實現(xiàn)它了。我們已經(jīng)有前面實現(xiàn)TCP服務器的基礎。所以實現(xiàn)他的重點就是我們設計的Telnet服務器了。
我們依然采用實現(xiàn)普通TCP服務器結(jié)構(gòu)來實現(xiàn)Telnet服務器,只是在信息處理回調(diào)函數(shù)上更復雜一點。還有就是端口方面我們采用Telnet的慣用端口。首先必然是Telnet服務器的初始化。
1 /* TELNET服務器初始化配置*/
2 void Telnet_Server_Initialization(void)
3 {
4 struct tcp_pcb *pcb;
5
6 /* 生成一個新的TCP控制塊 */
7 pcb = tcp_new();
8
9 /* 控制塊邦定到本地IP和對應端口 */
10 tcp_bind(pcb, IP_ADDR_ANY, TCP_TELNET_SERVER_PORT);
11
12 /* 服務器進入偵聽狀態(tài) */
13 pcb = tcp_listen(pcb);
14
15 /* 注冊服務器accept回調(diào)函數(shù) */
16 tcp_accept(pcb, TelnetServerAccept);
17 }
其實初始化部分就是我們已經(jīng)熟悉的TCP服務器的初始化,只是使用了Telnet的慣用端口。接下來就是實現(xiàn)在初始化中注冊的Telnet服務器接收回調(diào)函數(shù)。該函數(shù)為tcp_accept_fn類型,注冊到了監(jiān)聽控制塊的accept字段。在服務器上有新連接建立時就會被內(nèi)核調(diào)用。
1 /* TELNET接收回調(diào)函數(shù),客戶端建立連接后,本函數(shù)被調(diào)用 */
2 static err_t TelnetServerAccept(void *arg, struct tcp_pcb *pcb, err_t err)
3 {
4 u32_t remote_ip;
5 char linkInfo [100];
6 u8_t iptab[4];
7 telnet_conn_arg *conn_arg = NULL;
8 remote_ip = pcb->remote_ip.addr;
9
10 iptab[0] = (u8_t)(remote_ip >> 24);
11 iptab[1] = (u8_t)(remote_ip >> 16);
12 iptab[2] = (u8_t)(remote_ip >> 8);
13 iptab[3] = (u8_t)(remote_ip);
14
15 //生成登錄提示信息
16 sprintf(linkInfo, "Welcome to Telnet! your IP:Port --> [%d.%d.%d.%d:%d]\\r\\n", \\
17 iptab[3], iptab[2], iptab[1], iptab[0], pcb->remote_port);
18
19 conn_arg = mem_calloc(sizeof(telnet_conn_arg), 1);
20 if(!conn_arg)
21 {
22 return ERR_MEM;
23 }
24
25 conn_arg->state = TELNET_SETUP;
26 conn_arg->client_port = pcb->remote_port;
27 conn_arg->bytes_len = 0;
28 memset(conn_arg->bytes, 0, MAX_MSG_SIZE);
29
30 tcp_arg(pcb, conn_arg);
31
32 /* 注冊Telnet服務器連接錯誤回調(diào)函數(shù) */
33 tcp_err(pcb, TelnetServeConnectError);
34 /* 注冊Telnet服務器消息處理回調(diào)函數(shù)*/
35 tcp_recv(pcb, TelnetServerCallback);
36
37 /* 連接成功,發(fā)送登錄提示信息 */
38 tcp_write(pcb, linkInfo, strlen(linkInfo), 1);
39 tcp_write(pcb, LOGIN_INFO, strlen(LOGIN_INFO), 1);
40
41 return ERR_OK;
42 }
在這個函數(shù)中,我們實現(xiàn)的功能主要是三方面:注冊Telnet服務器消息處理回調(diào)函數(shù);注冊Telnet服務器連接錯誤回調(diào)函數(shù);初始化Telnet服務器的狀態(tài)。這個初始化是在連接建立后,Telnet服務器與客戶端的交互初始化,比如登錄狀態(tài),用戶提示等。
在上面的函數(shù)中,我們注冊了兩個回調(diào)函數(shù),接下來必然就是實現(xiàn)這兩個函數(shù)。我們先來實現(xiàn)Telnet服務器信息處理回調(diào)函數(shù)。這個函數(shù)其實就是我們前面注冊過的TCP服務器數(shù)據(jù)接收處理函數(shù)。這個函數(shù)是tcp_recv_fn類型。這是使用RAW API實現(xiàn)TCP服務器最重要的函數(shù),因為我們實現(xiàn)的TCP服務器究竟有什么功能,完全依賴于這個函數(shù)及其所調(diào)用的函數(shù)。
1 /* TELNET服務器信息處理回調(diào)函數(shù),在有消息需要處理時,調(diào)用此函數(shù) */
2 static err_t TelnetServerCallback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
3 {
4 telnet_conn_arg *conn_args = (telnet_conn_arg *)arg;
5 char sndbuf[50];
6 int strlen = 0;
7 int ret = 0;
8
9 if(NULL == conn_args || pcb->remote_port != conn_args->client_port)
10 {
11 if(p!= NULL)
12 {
13 pbuf_free(p);
14 }
15 return ERR_ARG;
16 }
17
18 if (p != NULL)
19 {
20 /* 更新接收窗口 */
21 tcp_recved(pcb, p->tot_len);
22
23 ret = TelnetCommandInput(pcb, conn_args, p);
24
25 if(ret == 1)//是完整命令
26 {
27 switch(conn_args->state)
28 {
29 case TELNET_SETUP:
30 {
31 if(strcmp(conn_args->bytes,PASSWORD) == 0)//密碼正確
32 {
33 strlen = sprintf(sndbuf,"##Hello! This is an LwIP-based Telnet Server##\\r\\n");
34 tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);
35 strlen = sprintf(sndbuf,"##Created by Moonan... ##\\r\\n");
36 tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);
37 strlen = sprintf(sndbuf,"##Enter help for help. Enter quit for quit.##\\r\\n");
38 tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);
39 strlen = sprintf(sndbuf,"LwIP Telnet>");
40 tcp_write(pcb,sndbuf,strlen, 1);
41
42 conn_args->state = TELNET_CONNECTED;//轉(zhuǎn)換狀態(tài)
43 }
44 else//密碼錯誤,提示重新登錄
45 {
46 strlen = sprintf(sndbuf,"##PASSWORD ERROR! Try again:##\\r\\n");
47 tcp_write(pcb, sndbuf, strlen,TCP_WRITE_FLAG_COPY);
48 }
49 memset(conn_args->bytes, 0, MAX_MSG_SIZE);
50 conn_args->bytes_len = 0;
51 break;
52 }
53 case TELNET_CONNECTED:
54 {
55 if(TelnetCommandParse(pcb, conn_args->bytes) == 0)
56 {
57 memset(conn_args->bytes, 0, MAX_MSG_SIZE);
58 conn_args->bytes_len = 0;
59 }
60 else
61 {
62 /* 服務器關(guān)閉連接 */
63 ServerCloseTelnetConnection(pcb);
64 }
65 break;
66 }
67 default:
68 {
69 break;
70 }
71 }
72 }
73 pbuf_free(p);
74 }
75 else if (err == ERR_OK)
76 {
77 /* 服務器關(guān)閉連接 */
78 ServerCloseTelnetConnection(pcb);
79 }
80
81 return ERR_OK;
82
83 }
在這個函數(shù)中,我們實現(xiàn)了Telnet服務器的各種功能,如登錄驗證,命令檢查,命令響應等。已經(jīng)具備一個Telnet服務器的基本框架。接下來還要實現(xiàn)Telnet連接錯誤回調(diào)函數(shù)。這個函數(shù)是tcp_err_fn類型,在這個程序中主要完成連接異常結(jié)束時的一些處理,可以釋放一些必要的資源。在這個函數(shù)被內(nèi)核調(diào)用時,連接實際上已經(jīng)斷開,相關(guān)控制塊也已經(jīng)被刪除。所以在這個函數(shù)中我們可以重新初始化連接及其資源。
1 /* TELNET連接錯誤回調(diào)函數(shù),連接故障時調(diào)用本函數(shù) */
2 static void TelnetServeConnectError(void *arg, err_t err)
3 {
4 Telnet_Server_Initialization();
5 }
至此,我們就實現(xiàn)了一個簡單的Telnet服務器,當然它只是一個雛形,需要開發(fā)更復雜的功能則需要修改這幾個回調(diào)函數(shù)。
4 、 TELNET****服務器總結(jié)
我們已經(jīng)實現(xiàn)了一個簡單的Telnet服務器。當然,我們的目的主要是以此來學習基于LwIP的復雜的TCP應用。事實上理解了TCP服務器的實現(xiàn)機制,諸如此類基于TCP的高級應用協(xié)議并不是特別復雜的事情。
-
服務器
+關(guān)注
關(guān)注
12文章
8897瀏覽量
85003 -
HTTP
+關(guān)注
關(guān)注
0文章
497瀏覽量
30922 -
TCP
+關(guān)注
關(guān)注
8文章
1338瀏覽量
78884 -
TELNET
+關(guān)注
關(guān)注
0文章
16瀏覽量
10771
發(fā)布評論請先 登錄
相關(guān)推薦
評論