ftp客户端代码
被動模式
1.被動模式通訊介紹
首先,服務(wù)器準(zhǔn)備就緒后返回 220。客戶端接收到服務(wù)器端返回的響應(yīng)碼后,相繼發(fā)送“USER username” 和 “PASS password” 命令登錄。隨后,服務(wù)器返回的響應(yīng)碼為 230 開頭,說明客戶端已經(jīng)登入了。這時,客戶端發(fā)送 PASV 命令讓服務(wù)器進(jìn)入被動模式。服務(wù)器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客戶端從中得到端口號,然后連接到服務(wù)器的數(shù)據(jù)端口。接下來,客戶端發(fā)送下載命令,服務(wù)器會返回響應(yīng)碼 150,并從數(shù)據(jù)端口發(fā)送數(shù)據(jù)。最后,服務(wù)器返回 “226 transfer complete”,表明數(shù)據(jù)傳輸完成。
需要注意的是,客戶端不要一次發(fā)送多條命令,例如我們要打開一個目錄并且顯示這個目錄,我們得發(fā)送 CWD dirname,PASV,LIST。在發(fā)送完 CWD dirname 之后等待響應(yīng)代碼,然后再發(fā)送后面一條。當(dāng) PASV 返回之后,我們打開另一個 Socket 連接到相關(guān)端口上。然后發(fā)送 LIST,返回 125 之后在開始接收數(shù)據(jù),最后返回 226 表明完成。
在傳輸多個文件的過程中,需要注意的是每次新的傳輸都必須重新使用 PASV 獲取新的端口號,接收完數(shù)據(jù)后應(yīng)該關(guān)閉該數(shù)據(jù)連接,這樣服務(wù)器才會返回一個 2XX 成功的響應(yīng)。然后客戶端可以繼續(xù)下一個文件的傳輸。
上傳文件與下載文件相比,登入驗證和切換被動模式都如出一轍,只需要改變發(fā)送到服務(wù)器端的命令,并通過數(shù)據(jù)連接發(fā)送文件內(nèi)容。
2. 客戶端建立連接,設(shè)置服務(wù)器為被動模式
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);/* 連接到服務(wù)器端 */ connect(control_sock,(struct sockaddr *)&server, sizeof(server)); /* 客戶端接收服務(wù)器端的一些歡迎信息 */ read(control_sock, read_buf, read_len); //返回220 * 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客戶端發(fā)送用戶名到服務(wù)器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”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); /* 客戶端發(fā)送密碼到服務(wù)器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); /* 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客戶端發(fā)送用戶名到服務(wù)器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”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); /* 客戶端發(fā)送密碼到服務(wù)器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); /* 命令 ”PASV\r\n” */ sprintf(send_buf,"PASV\r\n"); /* 客戶端告訴服務(wù)器用被動模式 */ write(control_sock, send_buf, strlen(send_buf)); /*客戶端接收服務(wù)器的響應(yīng)碼和新開的端口號, * 正常為 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */ read(control_sock, read_buf, read_len);3.數(shù)據(jù)通道代碼
/* 連接服務(wù)器新開的數(shù)據(jù)端口 */ connect(data_sock,(struct sockaddr *)&server, sizeof(server)); /* 命令 ”CWD dirname\r\n” */ sprintf(send_buf,"CWD %s\r\n", dirname); /* 客戶端發(fā)送命令改變工作目錄 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”250 Command okay.” */ read(control_sock, read_buf, read_len);/* 命令 ”SIZE filename\r\n” */ sprintf(send_buf,"SIZE %s\r\n",filename); /* 客戶端發(fā)送命令從服務(wù)器端得到下載文件的大小 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”213 <size>” */ read(control_sock, read_buf, read_len);/* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客戶端發(fā)送命令從服務(wù)器端下載文件 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”150 Opening data connection.” */ read(control_sock, read_buf, read_len);/* 客戶端創(chuàng)建文件 */ file_handle = open(disk_name, CRFLAGS, RWXALL); for( ; ; ) { ... ... /* 客戶端通過數(shù)據(jù)連接 從服務(wù)器接收文件內(nèi)容 */ read(data_sock, read_buf, read_len); /* 客戶端寫文件 */ write(file_handle, read_buf, read_len); ... ... } /* 客戶端關(guān)閉文件 */ rc = close(file_handle);4.關(guān)閉客戶端通道
/* 客戶端關(guān)閉數(shù)據(jù)連接 */ close(data_sock); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”226 Transfer complete.” */ read(control_sock, read_buf, read_len);/* 命令 ”QUIT\r\n” */ sprintf(send_buf,"QUIT\r\n"); /* 客戶端將斷開與服務(wù)器端的連接 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼,正常為 ”200 Closes connection.” */ read(control_sock, read_buf, read_len); /* 客戶端關(guān)閉控制連接 */ close(control_sock);5.服務(wù)器相應(yīng)輸出
(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);/* 客戶端開始監(jiān)聽端口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)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”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)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,正常為 ”150 Opening data channel for file transfer.” */ read(control_sock, read_buf, read_len);/* ftp客戶端接受服務(wù)器端的連接請求 */ 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.斷點續(xù)傳 ... ... /* 命令 ”REST offset\r\n” */ sprintf(send_buf,"REST %ld\r\n", offset); /* 客戶端發(fā)送命令指定下載文件的偏移量 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息, *正常為 ”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); /* 客戶端發(fā)送命令從服務(wù)器端下載文件, 并且跳過該文件的前offset字節(jié)*/ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務(wù)器的響應(yīng)碼和信息,* *正常為 ”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/
總結(jié)
- 上一篇: 字符设备驱动高级篇4——设备类(自动创建
- 下一篇: elf文件结构