ftp客户端代码
被動模式
1.被動模式通訊介紹
首先,服務器準備就緒后返回 220。客戶端接收到服務器端返回的響應碼后,相繼發送“USER username” 和 “PASS password” 命令登錄。隨后,服務器返回的響應碼為 230 開頭,說明客戶端已經登入了。這時,客戶端發送 PASV 命令讓服務器進入被動模式。服務器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客戶端從中得到端口號,然后連接到服務器的數據端口。接下來,客戶端發送下載命令,服務器會返回響應碼 150,并從數據端口發送數據。最后,服務器返回 “226 transfer complete”,表明數據傳輸完成。
需要注意的是,客戶端不要一次發送多條命令,例如我們要打開一個目錄并且顯示這個目錄,我們得發送 CWD dirname,PASV,LIST。在發送完 CWD dirname 之后等待響應代碼,然后再發送后面一條。當 PASV 返回之后,我們打開另一個 Socket 連接到相關端口上。然后發送 LIST,返回 125 之后在開始接收數據,最后返回 226 表明完成。
在傳輸多個文件的過程中,需要注意的是每次新的傳輸都必須重新使用 PASV 獲取新的端口號,接收完數據后應該關閉該數據連接,這樣服務器才會返回一個 2XX 成功的響應。然后客戶端可以繼續下一個文件的傳輸。
上傳文件與下載文件相比,登入驗證和切換被動模式都如出一轍,只需要改變發送到服務器端的命令,并通過數據連接發送文件內容。
2. 客戶端建立連接,設置服務器為被動模式
SOCKET control_sock; struct hostent *hp; struct sockaddr_in server; memset(&server, 0, sizeof(struct sockaddr_in));/* 初始化socket */ control_sock = socket(AF_INET, SOCK_STREAM, 0); hp = gethostbyname(server_name); memcpy(&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = AF_INET; server.sin_port = htons(port);/* 連接到服務器端 */ connect(control_sock,(struct sockaddr *)&server, sizeof(server)); /* 客戶端接收服務器端的一些歡迎信息 */ read(control_sock, read_buf, read_len); //返回220 * 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客戶端發送用戶名到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”331 User name okay, need password.” */ read(control_sock, read_buf, read_len);/* 命令 ”PASS password\r\n” */ sprintf(send_buf,"PASS %s\r\n",password); /* 客戶端發送密碼到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); /* 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客戶端發送用戶名到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”331 User name okay, need password.” */ read(control_sock, read_buf, read_len);/* 命令 ”PASS password\r\n” */ sprintf(send_buf,"PASS %s\r\n",password); /* 客戶端發送密碼到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); /* 命令 ”PASV\r\n” */ sprintf(send_buf,"PASV\r\n"); /* 客戶端告訴服務器用被動模式 */ write(control_sock, send_buf, strlen(send_buf)); /*客戶端接收服務器的響應碼和新開的端口號, * 正常為 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */ read(control_sock, read_buf, read_len);3.數據通道代碼
/* 連接服務器新開的數據端口 */ connect(data_sock,(struct sockaddr *)&server, sizeof(server)); /* 命令 ”CWD dirname\r\n” */ sprintf(send_buf,"CWD %s\r\n", dirname); /* 客戶端發送命令改變工作目錄 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”250 Command okay.” */ read(control_sock, read_buf, read_len);/* 命令 ”SIZE filename\r\n” */ sprintf(send_buf,"SIZE %s\r\n",filename); /* 客戶端發送命令從服務器端得到下載文件的大小 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”213 <size>” */ read(control_sock, read_buf, read_len);/* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客戶端發送命令從服務器端下載文件 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”150 Opening data connection.” */ read(control_sock, read_buf, read_len);/* 客戶端創建文件 */ file_handle = open(disk_name, CRFLAGS, RWXALL); for( ; ; ) { ... ... /* 客戶端通過數據連接 從服務器接收文件內容 */ read(data_sock, read_buf, read_len); /* 客戶端寫文件 */ write(file_handle, read_buf, read_len); ... ... } /* 客戶端關閉文件 */ rc = close(file_handle);4.關閉客戶端通道
/* 客戶端關閉數據連接 */ close(data_sock); /* 客戶端接收服務器的響應碼和信息,正常為 ”226 Transfer complete.” */ read(control_sock, read_buf, read_len);/* 命令 ”QUIT\r\n” */ sprintf(send_buf,"QUIT\r\n"); /* 客戶端將斷開與服務器端的連接 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼,正常為 ”200 Closes connection.” */ read(control_sock, read_buf, read_len); /* 客戶端關閉控制連接 */ close(control_sock);5.服務器相應輸出
(not logged in) (127.0.0.1)> Connected, sending welcome message... (not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.36 beta (not logged in) (127.0.0.1)> 220 hello gaoleyi (not logged in) (127.0.0.1)> USER gaoleyi (not logged in) (127.0.0.1)> 331 Password required for gaoleyi (not logged in) (127.0.0.1)> PASS ********* gaoleyi (127.0.0.1)> 230 Logged on gaoleyi (127.0.0.1)> PWD gaoleyi (127.0.0.1)> 257 "/" is current directory. gaoleyi (127.0.0.1)> SIZE file.txt gaoleyi (127.0.0.1)> 213 4096 gaoleyi (127.0.0.1)> PASV gaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67) gaoleyi (127.0.0.1)> RETR file.txt gaoleyi (127.0.0.1)> 150 Connection accepted gaoleyi (127.0.0.1)> 226 Transfer OK gaoleyi (127.0.0.1)> QUIT gaoleyi (127.0.0.1)> 221 Goodbye主動模式
1.連接和傳輸 SOCKET data_sock; data_sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in name; name.sin_family = AF_INET; name.sin_addr.s_addr = htons(INADDR_ANY); server_port = p1*256+p2; length = sizeof(name); name.sin_port = htons(server_port); bind(server_sock, (struct sockaddr *)&name, length); struct sockaddr_in client_name; length = sizeof(client_name);/* 客戶端開始監聽端口p1*256+p2 */ listen(server_sock, 64); /* 命令 ”PORT \r\n” */ sprintf(send_buf,"PORT 1287,0,0,1,%d,%d\r\n", p1, p2); write(control_sock, send_buf,strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”200 Port command successful” */ read(control_sock, read_buf, read_len);sprintf(send_buf,"RETR filename.txt\r\n"); write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”150 Opening data channel for file transfer.” */ read(control_sock, read_buf, read_len);/* ftp客戶端接受服務器端的連接請求 */ data_sock = accept(server_sock,(struct sockaddr *)&client_name, &length); ... ...file_handle = open(disk_name, ROFLAGS, RWXALL); for( ; ; ) { ... ... read(data_sock, read_buf, read_len); write(file_handle, read_buf, read_len); ... ... } close(file_handle);
2.斷點續傳 ... ... /* 命令 ”REST offset\r\n” */ sprintf(send_buf,"REST %ld\r\n", offset); /* 客戶端發送命令指定下載文件的偏移量 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息, *正常為 ”350 Restarting at <position>. Send STORE or RETRIEVE to initiate transfer.” */ read(control_sock, read_buf, read_len); ... .../* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客戶端發送命令從服務器端下載文件, 并且跳過該文件的前offset字節*/ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,* *正常為 ”150 Connection accepted, restarting at offset <position>” */ read(control_sock, read_buf, read_len); ... ...file_handle = open(disk_name, CRFLAGS, RWXALL); /* 指向文件寫入的初始位置 */ lseek(file_handle, offset, SEEK_SET); ... ...
http://www.ibm.com/developerworks/cn/linux/l-cn-socketftp/
總結
- 上一篇: 字符设备驱动高级篇4——设备类(自动创建
- 下一篇: elf文件结构