前一段時間看了看socket編程,可也就是看,真正在這方面寫過的程序卻沒有多少,因為以前總是在Linux下用C++寫,在家裡雖然也裝了個Linux,可那天配置了一晚上也沒把ADSL給驅動起來,於是這一個假期還一直用著windows,win下的開發工具,用過的也只有vs了,還是找了個vc6.0裝上了,網絡編程這一塊,VC把一些socket給封裝了,使用起來簡單得多了,不過,還是用最底層的API比較好,因為winsock跟linux環境下的socket大同小異.
第一次練手應該選一個簡單的主題,決定寫一個操作FTP的小程序,為此我先查了一些相關的資料,了解了一下有關FTP協議,寫一下個人的想法,就不是那麼專業了.看了一下win下的ftp.exe,跟cuteftp,flashfxp之類的軟件其實原理上都是一樣的,用底層的socket來實現,說簡單了就兩個步驟,用send或write函數向服務器發送命令,服務器接收到命令後向客戶發送信息,客戶端通過read函數或recv函數讀取到服務器發送來的信息,當然過程中還包挺創建套接字,綁定端口,連接服務器之類的操作.大體的步驟就是:socket()->connect()->send()->read().
客戶端與服務器端進行數據交換必須建立兩個套接字,一個作為命令通道,一個作為數據通道.前者用於客戶端向服務器發送命令,如登錄,刪除某個文件,更換當前目錄等,後者用於接收數據,例如在查看目錄文件列表,下載或上傳文件等.
FTP有兩種模式,PORT和PASV模式,一種叫主動模式,一種叫被動模式,PORT模式是用戶像服務器發送PORT 124,128,249,76,2,25\r\n這種形式的命令,前四個參數是客戶端IP地址,後兩位是端口,端口的計算方法為port=2*256+25=537,通過這個命令來告訴服務器,客戶端的IP,並且在客戶端開放了537端口,服務器主動向客戶端的537端口發送數據,客戶端新建一個套接字作為數據通道,通過監聽537端口來獲得服務器發送來的數據,這種模式下要注意,客戶端計算機上的防火牆是否禁止了537端口.PASV模式是用戶向服務器發出"PASV\r\n"命令,然後服務器返回數據:227 Entering Passive Mode (124,128,249,76,4,186),與PORT模式發送的數據形式相似,參數的前四位為服務器的IP地址,後兩位為端口號,計算方法也與PORT模式相同,服務器通過這條數據告訴客氣端,服務器打開了4*256+186端口,客戶端通過連接到該端口來獲得服務器發送來的數據.
在寫這個程序的時候遇到了很多白癡問題,弄得自己都很郁悶,不過問題解決了心裡也很痛快.剛開始的時候創建一個套接字作為命令通道,可一發送數據就處於阻塞狀態,查了很多資料也沒能搞定,後來一句話一句話地排查,終於找到症結所在,原來在windows下的send函數的入口參數跟linux是不一樣的,雖然大體形式差不多,但數據類型不一致,win的send函數的第二個入口參數是LPSTR數據,而我傳入的是char*類型,這種情況下編譯器是不會報錯的,後來找了很長時間才找到問題所在,send()函數的第三個參數應該使用strlen()來獲得命令字符串的長度,而使用sizeof()的時候就會出現阻塞,這樣便可以讀取到服務器發送來的數據了.另一個問題更讓人郁悶,花了我一下午的時間才搞定,最後我都懷疑我自己的智商了,在建立數據通道的時候,connect函數在執行的時候總會返回一個10060錯誤,用Error Lookup查了一個錯誤的描述為:由於連接方在一段時間後沒有正確答復或連接的主機沒有反應,連接嘗試失敗。服務器是沒有問題的,因為是自己的服務器,可以隨時查看服務器的運行狀態,再一個可能的原因是端口沒有開放,如果是防火牆的原因,那FlashFxp用PASV模式也能訪問,這個問題排除了,後來又看了下端口,計算的也沒有錯啊,這一個問題郁悶了一下午,查了N多資料也沒能搞定,最後吃飯的時候一想,知道問題出在哪裡了,建立數據通道時綁定的端口沒有轉換成網絡數據格式,也就是說忘了調用htons()函數將端口轉換了,總是犯這種超沒有技術含量的錯誤,連自己都鄙視自己了.
最後通過數據通道獲得的目錄列表是一大堆字符串,下一步的工作就是對這些字符串進行解析,我也就做到這一步了,正在考慮要不要繼續玩下去,C++的字符串處理功能沒有C#那麼牛,字符串解析是一個很讓人頭痛的問題,有必要的話就先寫一個字符串函數庫,也方便以後移植到Linux下繼續使用.