經過一天多的研究,簡易實現了TFTP用戶端,在項目的進程中遇到了一些問題,大部分的問題是由於對C++類型轉換等基礎概念還是有點模糊,收到根深蒂固C語言的影響,使得我對C++的繼承,類等概念的理解還是有些紙上談兵,在實際運用中有些捉衿見肘。
現在簡要分析一下實現TFTP用戶端的過程。
通過TFTP協議我們可以得知TFTP用戶端主要有5種類型的資料報:讀/寫請求報文,響應報文,資料報文,錯誤判文。
根據協議我們可以定義5種報文的結構體分別為:
//TFTP包頭<br />struct TFTP_Head<br />{<br /> byte opcode[2];<br /> byte block[2];<br /> byte other[DATASIZE];<br />};</p><p>//TFTP請求包<br />struct TFTP_RRWQ<br />{<br /> byte opcode[2];<br /> string Filename;<br /> byte flag1;<br /> string Mode;<br /> byte flag2;<br />};</p><p>//DATA報文</p><p>struct TFTP_DATA<br />{<br /> byte opcode[2];<br /> byte blocknum[2];<br /> byte Data[DATASIZE];<br />};</p><p>//應答報文<br />struct TFTP_ACK<br />{<br /> byte opcode[2];<br /> byte blocknum[2];<br />};</p><p>//差錯資料包<br />struct TFTP_ERROR<br />{<br /> byte opcode[2];<br /> byte errornum[2];<br /> byte Data[DATASIZE];<br /> byte flag;<br />};<br />
注意:TFTP_Head的建立主要是為了在抓包時判斷報文的種類。
在具體實現資料包傳輸的過程中,我將各種操作分為通訊和資料包操作兩個介面類,來分別進行操作。
通訊類:
#define TFTPPORT 69</p><p>class TFTPCONNECTCONTROL<br />{<br /> WORD versionRequired;<br /> WSADATA wsaData;<br /> SOCKET clientsocket;<br /> SOCKADDR_IN clientsock_in;</p><p>public:<br /> TFTPCONNECTCONTROL();<br /> ~TFTPCONNECTCONTROL();<br /> bool SendtoTFTPserver(char* senddata);<br /> bool RecvTFTPpackage(char receiveBuf[DATASIZE+4]);<br />};</p><p>//版本驗證<br />TFTPCONNECTCONTROL::TFTPCONNECTCONTROL()<br />{<br /> versionRequired=MAKEWORD(2,2);<br /> int err=WSAStartup(versionRequired,&wsaData); //版本驗證<br /> if(!err)<br /> {<br /> cout<<"版本驗證成功!"<<endl;<br /> }<br /> else<br /> {<br /> cout<<"版本驗證失敗!"<<endl;<br /> return ;<br /> }</p><p> clientsocket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); //建立UDP通訊端<br /> if(clientsocket==INVALID_SOCKET) //通訊端建立失敗<br /> {<br /> cout<<"Invalid socket"<<endl;<br /> return ;<br /> }<br /> memset(&clientsock_in,0,sizeof(clientsock_in));<br /> clientsock_in.sin_family=AF_INET;<br /> clientsock_in.sin_addr.S_un.S_addr=inet_addr("***.***.***.***");<br /> clientsock_in.sin_port=htons(TFTPPORT);<br />}</p><p>TFTPCONNECTCONTROL::~TFTPCONNECTCONTROL()<br />{<br /> closesocket(clientsocket);<br /> WSACleanup();<br /> return ;<br />}</p><p>//發送資料包</p><p>bool TFTPCONNECTCONTROL::SendtoTFTPserver(char* senddata)<br />{<br /> cout<<"ip:"<<inet_ntoa(clientsock_in.sin_addr)<<endl;<br /> cout<<"port:"<<ntohs(clientsock_in.sin_port)<<endl;<br /> int err=sendto(clientsocket,senddata,sizeof(senddata),0,(SOCKADDR*)&clientsock_in,sizeof(SOCKADDR_IN)); //資料包傳輸<br /> if(err==SOCKET_ERROR) //資料轉送錯誤<br /> {<br /> DWORD res=WSAGetLastError();<br /> cout<<"傳輸發生錯誤:"<<res<<endl;<br /> //closesocket(clientsocket);<br /> return false;<br /> }<br /> cout<<"資料包發送成功!"<<endl;<br /> return true;<br />}</p><p>//接收資料包<br />bool TFTPCONNECTCONTROL::RecvTFTPpackage(char receiveBuf[DATASIZE+4])<br />{<br /> int len=sizeof(sockaddr);<br /> int err=recvfrom(clientsocket,receiveBuf,sizeof(receiveBuf),0,(sockaddr*)&clientsock_in,&len); //接收資料包<br /> if(err==SOCKET_ERROR)<br /> {<br /> DWORD res=WSAGetLastError();<br /> cout<<"接收發生錯誤:"<<res<<endl;<br /> return false;<br /> }</p><p> return true;<br />}<br />
資料包處理類:
//判斷檔案是否傳完<br />bool FileEOF(byte datapack[])<br />{<br /> return (strlen((const char*)datapack)<512&&strlen((const char*)datapack)>=0)?true:false;<br />}</p><p>class TFTPPACKAGECONTROL<br />{<br /> char TFTPdata[DATASIZE+4]; //預留緩衝區<br /> int recvblock; //收到塊號<br /> string filename; //檔案名稱</p><p> public:<br /> TFTPPACKAGECONTROL(string name); //建構函式<br /> TFTPPACKAGECONTROL(); //建構函式2<br /> ~TFTPPACKAGECONTROL(); //解構函式<br /> void CreateTFTPRRQ(); //建立檔案請求包<br /> void CreateACK(); //建立ACK資料包<br /> bool Recv_TFTP_File(char* packagedata); //判斷是否傳輸的是檔案<br /> bool DealWithDataPackage(char *datapackage); //處理data資料包<br /> void Setfilename(string name);<br /> char* GetTFTPdata();<br />};</p><p>//建構函式<br />TFTPPACKAGECONTROL::TFTPPACKAGECONTROL(string name)<br />{<br /> memset(TFTPdata,0,sizeof(TFTPdata));<br /> recvblock=0;<br /> filename=name;<br />}</p><p>//建構函式2<br />TFTPPACKAGECONTROL::TFTPPACKAGECONTROL()<br />{<br /> memset(TFTPdata,0,sizeof(TFTPdata));<br /> recvblock=0;<br /> filename="";</p><p>}</p><p>//析構<br />TFTPPACKAGECONTROL::~TFTPPACKAGECONTROL()<br />{<br /> memset(TFTPdata,0,sizeof(TFTPdata));<br /> recvblock=0;<br /> filename="";<br />}</p><p>//設定接收檔案名<br />void TFTPPACKAGECONTROL::Setfilename(string name)<br />{<br /> filename=name;<br />}</p><p>//建立RRQ資料包<br />void TFTPPACKAGECONTROL::CreateTFTPRRQ()<br />{<br /> struct TFTP_RRWQ package;<br /> memset(package.opcode,0x01,sizeof(package.opcode));<br /> package.Filename.assign(filename);<br /> memset(&package.flag1,0,sizeof(package.flag1));<br /> package.Mode="netascii"; //netascii對於ascii檔案,octet對於二進位檔案<br /> memset(&package.flag2,0,sizeof(package.flag2));<br /> memcpy(TFTPdata,(char*)&package,sizeof(package));<br />}</p><p>//建立ACK資料包<br />void TFTPPACKAGECONTROL::CreateACK()<br />{<br /> struct TFTP_ACK *ackpackage=(struct TFTP_ACK*) malloc(sizeof(TFTP_ACK));<br /> memset(ackpackage->opcode,0x04,sizeof(ackpackage->opcode));<br /> memset(ackpackage->blocknum,recvblock,sizeof(ackpackage->blocknum));<br /> memcpy(TFTPdata,(char*)&ackpackage,sizeof(ackpackage));<br />}</p><p>//判斷是否接收到data資料包<br />bool TFTPPACKAGECONTROL::Recv_TFTP_File(char* packagedata)<br />{<br /> struct TFTP_Head *data=(struct TFTP_Head*)packagedata;<br /> recvblock=(int)*data->block;<br /> if(memcmp(data->opcode,"0x03",sizeof(data->opcode)))<br /> return true;<br /> else<br /> return false;<br />}</p><p>char* TFTPPACKAGECONTROL::GetTFTPdata()<br />{<br /> return TFTPdata;<br />}</p><p>//一個檔案是否處理完畢<br />bool TFTPPACKAGECONTROL::DealWithDataPackage(char*datapackage)<br />{<br /> char filedata[DATASIZE];<br /> struct TFTP_DATA *data=(struct TFTP_DATA*) datapackage;<br /> memcpy(filedata,data->Data,sizeof(data->Data));</p><p> ofstream fout;<br /> fout.open(filename.c_str());<br /> fout<<filedata;<br /> fout.close();<br /> return FileEOF(data->Data);</p><p>}
當然,這隻是TFTP用戶端的最初設計,隨著項目的進行還會接著改進,暫時記到這裡。(*^__^*)