基于visual c++之windows核心编程代码分析(30)Telnet协议编程
Telnet協(xié)議是TCP/IP協(xié)議族中的一員,是Internet遠程登陸服務(wù)的標準協(xié)議和主要方式。它為用戶提供了在本地計算機上完成遠程主機工作的能力。在終端使用者的電腦上使用telnet程序,用它連接到服務(wù)器。終端使用者可以在telnet程序中輸入命令,這些命令會在服務(wù)器上運行,就像直接在服務(wù)器的控制臺上輸入一樣。可以在本地就能控制服務(wù)器。要開始一個telnet會話,必須輸入用戶名和密碼來登錄服務(wù)器。Telnet是常用的遠程控制Web服務(wù)器的方法。
Telnet協(xié)議?它都具備哪些特點呢?
1、 基本內(nèi)容
Telnet是位于OSI模型的第7層---應(yīng)用層上的一種協(xié)議,是一個通過創(chuàng)建虛擬終端提供連接到遠程主機終端仿真的TCP/IP協(xié)議。這一協(xié)議需要通過用戶名和口令進行認證,是Internet遠程登陸服務(wù)的標準協(xié)議。應(yīng)用Telnet協(xié)議能夠把本地用戶所使用的計算機變成遠程主機系統(tǒng)的一個終端。它提供了三種基本服務(wù):
? 1)Telnet定義一個網(wǎng)絡(luò)虛擬終端為遠程系統(tǒng)提供一個標準接口。客戶機程序不必詳細了解遠程系統(tǒng),他們只需構(gòu)造使用標準接口的程序;
? 2)Telnet包括一個允許客戶機和服務(wù)器協(xié)商選項的機制,而且它還提供一組標準選項; .
3)Telnet對稱處理連接的兩端,即Telnet不強迫客戶機從鍵盤輸入,也不強迫客戶機在屏幕上顯示輸出。
2 、適應(yīng)異構(gòu)
為了使多個操作系統(tǒng)間的Telnet交互操作成為可能,就必須詳細了解異構(gòu)計算機和操作系統(tǒng)。比如,一些操作系統(tǒng)需要每行文本用ASCII回車控制符(CR)結(jié)束,另一些系統(tǒng)則需要使用ASCII換行符(LF),還有一些系統(tǒng)需要用兩個字符的序列回車-換行(CR-LF);再比如,大多數(shù)操作系統(tǒng)為用戶提供了一個中斷程序運行的快捷鍵,但這個快捷鍵在各個系統(tǒng)中有可能不同(一些系統(tǒng)使用CTRL+C,而另一些系統(tǒng)使用ESCAPE)。如果不考慮系統(tǒng)間的異構(gòu)性,那么在本地發(fā)出的字符或命令,傳送到遠地并被遠程系統(tǒng)解釋后很可能會不準確或者出現(xiàn)錯誤。因此,Telnet協(xié)議必須解決這個問題。 為了適應(yīng)異構(gòu)環(huán)境,Telnet協(xié)議定義了數(shù)據(jù)和命令在Internet上的傳輸方式,此定義被稱作網(wǎng)絡(luò)虛擬終端NVT(Net Virtual Terminal)。它的應(yīng)用過程如下: 對于發(fā)送的數(shù)據(jù):客戶機軟件把來自用戶終端的按鍵和命令序列轉(zhuǎn)換為NVT格式,并發(fā)送到服務(wù)器,服務(wù)器軟件將收到的數(shù)據(jù)和命令,從NVT格式轉(zhuǎn)換為遠地系統(tǒng)需要的格式; 對于返回的數(shù)據(jù):遠地服務(wù)器將數(shù)據(jù)從遠地機器的格式轉(zhuǎn)換為NVT格式,而本地客戶機將將接收到的NVT格式數(shù)據(jù)再轉(zhuǎn)換為本地的格式。 對于NVT格式的詳細定義,有興趣的朋友可以去查找相關(guān)資料。
3 、傳送遠程命令
我們知道絕大多數(shù)操作系統(tǒng)都提供各種快捷鍵來實現(xiàn)相應(yīng)的控制命令,當(dāng)用戶在本地終端鍵入這些快捷鍵的時候,本地系統(tǒng)將執(zhí)行相應(yīng)的控制命令,而不把這些快捷鍵作為輸入。那么對于Telnet來說,它是用什么來實現(xiàn)控制命令的遠程傳送呢? Telnet同樣使用NVT來定義如何從客戶機將控制功能傳送到服務(wù)器。我們知道USASCII字符集包括95個可打印字符和33個控制碼。當(dāng)用戶從本地鍵入普通字符時,NVT將按照其原始含義傳送;當(dāng)用戶鍵入快捷鍵(組合鍵)時,NVT將把它轉(zhuǎn)化為特殊的ASCII字符在網(wǎng)絡(luò)上傳送,并在其到達遠地機器后轉(zhuǎn)化為相應(yīng)的控制命令。 將正常ASCII字符集與控制命令區(qū)分的原因:
? 1)這種區(qū)分意味著Telnet具有更大的靈活性:它可在客戶機與服務(wù)器間傳送所有可能的ASCII字符以及所有控制功能;
2)這種區(qū)分使得客戶機可以無二義性的指定信令,而不會產(chǎn)生控制功能與普通字符的混亂。 .
4 、數(shù)據(jù)流向
上面我們提到過將Telnet設(shè)計為應(yīng)用級軟件有一個缺點,那就是:效率不高。這是為什么呢?下面給出Telnet中的數(shù)據(jù)流向: 數(shù)據(jù)信息被用戶從本地鍵盤鍵入并通過操作系統(tǒng)傳到客戶機程序,客戶機程序?qū)⑵涮幚砗蠓祷夭僮飨到y(tǒng),并由操作系統(tǒng)經(jīng)過網(wǎng)絡(luò)傳送到遠程機器,遠程操作系統(tǒng)將所接收數(shù)據(jù)傳給服務(wù)器程序,并經(jīng)服務(wù)器程序再次處理后返回到操作系統(tǒng)上的偽終端入口點,最后,遠程操作系統(tǒng)將數(shù)據(jù)傳送到用戶正在運行的應(yīng)用程序,這便是一次完整的輸入過程;輸出將按照同一通路從服務(wù)器傳送到客戶機。 因為每一次的輸入和輸出,計算機將切換進程環(huán)境好幾次,這個開銷是很昂貴的。還好用戶的鍵入速率并不算高,這個缺點我們?nèi)匀荒軌蚪邮堋!?
5、 強制命令
我們應(yīng)該考慮到這樣一種情況:假設(shè)本地用戶運行了遠地機器的一個無休止循環(huán)的錯誤命令或程序,且此命令或程序已經(jīng)停止讀取輸入,那么操作系統(tǒng)的緩沖區(qū)可能因此而被占滿,如果這樣,遠程服務(wù)器也無法再將數(shù)據(jù)寫入偽終端,并且最終導(dǎo)致停止從TCP連接讀取數(shù)據(jù),TCP連接的緩沖區(qū)最終也會被占滿,從而導(dǎo)致阻止數(shù)據(jù)流流入此連接。如果以上事情真的發(fā)生了,那么本地用戶將失去對遠程機器的控制。 為了解決此問題,Telnet協(xié)議必須使用外帶信令以便強制服務(wù)器讀取一個控制命令。我們知道TCP用緊急數(shù)據(jù)機制實現(xiàn)外帶數(shù)據(jù)信令,那么Telnet只要再附加一個被稱為數(shù)據(jù)標記(date mark)的保留八位組,并通過讓TCP發(fā)送已設(shè)置緊急數(shù)據(jù)比特的報文段通知服務(wù)器便可以了,攜帶緊急數(shù)據(jù)的報文段將繞過流量控制直接到達服務(wù)器。作為對緊急信令的相應(yīng),服務(wù)器將讀取并拋棄所有數(shù)據(jù),直到找到了一個數(shù)據(jù)標記。服務(wù)器在遇到了數(shù)據(jù)標記后將返回正常的處理過程。
6 、選項協(xié)商
由于Telnet兩端的機器和操作系統(tǒng)的異構(gòu)性,使得Telnet不可能也不應(yīng)該嚴格規(guī)定每一個telnet連接的詳細配置,否則將大大影響Telnet的適應(yīng)異構(gòu)性。因此,Telnet采用選項協(xié)商機制來解決這一問題。 Telnet選項的范圍很廣:一些選項擴充了大方向的功能,而一些選項制涉及一些微小細節(jié)。例如:有一個選項可以控制Telnet是在半雙工還是全雙工模式下工作(大方向);還有一個選項允許遠地機器上的服務(wù)器決定用戶終端類型(小細節(jié))。
下面我們在代碼中實踐Telnet協(xié)議編程
?
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "kernel32.lib")
#define PORT 90
SOCKET 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);
}
}
總結(jié)
以上是生活随笔為你收集整理的基于visual c++之windows核心编程代码分析(30)Telnet协议编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux系统编程:mmap介绍和使用m
- 下一篇: Effective C++ 读后感