新聞中心
隨著互聯(lián)網技術的發(fā)展,F(xiàn)TP(File Transfer Protocol,文件傳輸協(xié)議)已經成為了互聯(lián)網上常用的文件傳輸方式,而Linux操作系統(tǒng)本身就支持FTP服務的實現(xiàn)。本篇文章將介紹如何使用C語言在Linux平臺下實現(xiàn)FTP服務。

一、FTP客戶端與服務端的交互流程
在使用C語言實現(xiàn)FTP服務之前,我們需要了解FTP客戶端與服務端之間的交互流程,如下所示:
1. FTP客戶端連接FTP服務端;
2. FTP服務端返回連接成功的消息;
3. FTP客戶端發(fā)送用戶名和密碼到FTP服務端;
4. FTP服務端驗證用戶名和密碼,返回響應碼;
5. FTP客戶端發(fā)送需要下載/上傳的文件名到FTP服務端;
6. FTP服務端返回文件或目錄信息;
7. FTP客戶端斷開與FTP服務端的連接。
二、FTP服務端的實現(xiàn)
FTP服務端的實現(xiàn)需要借助于Linux系統(tǒng)提供的套接字API進行編寫。首先我們需要創(chuàng)建一個監(jiān)聽套接字,等待客戶端的連接請求。
1. 創(chuàng)建監(jiān)聽套接字
在Linux中創(chuàng)建監(jiān)聽套接字的函數為socket(),通過該函數可以創(chuàng)建一個網際套接字(IPv4或IPv6),該套接字用于監(jiān)聽客戶端的連接請求。
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 21
int mn()
{
int fd_listen;
struct sockaddr_in addr_listen;
int ret;
int on = 1;
fd_listen = socket(AF_INET, SOCK_STREAM, 0);
if(fd_listen == -1)
{
perror(“socket error”);
exit(-1);
}
memset(&addr_listen, 0, sizeof(addr_listen));
addr_listen.sin_family = AF_INET;
addr_listen.sin_addr.s_addr = INADDR_ANY;
addr_listen.sin_port = htons(SERVER_PORT);
//設置地址復用
if(setsockopt(fd_listen, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
{
perror(“setsockopt error”);
exit(-1);
}
ret = bind(fd_listen, (struct sockaddr *)&addr_listen, sizeof(struct sockaddr_in));
if(ret == -1)
{
perror(“bind error”);
exit(-1);
}
ret = listen(fd_listen, 5);
if(ret == -1)
{
perror(“l(fā)isten error”);
exit(-1);
}
while(1)
{
//等待客戶端連接請求
int fd_client = accept(fd_listen, NULL, NULL);
if(fd_client == -1)
{
perror(“accept error”);
continue;
}
//創(chuàng)建子進程處理客戶端請求
pid_t pid = fork();
if(pid == -1)
{
perror(“fork error”);
continue;
}
if(pid == 0) //子進程
{
close(fd_listen);
ftp_server(fd_client);
close(fd_client);
exit(0);
}
else //父進程
{
close(fd_client);
}
}
}
“`
2. 處理客戶端請求
FTP服務端的處理流程比較復雜,需要考慮多種情況。下面是一個基本的FTP服務端的處理函數。
“`c
void ftp_server(int fd_client)
{
char cmd[1024];
char filename[1024];
char buf[4096];
int ret;
FILE *fp;
int fd_data = -1; //數據連接套接字
char ip_data[32]; //數據連接IP地址
int port_data = 0; //數據連接端口號
struct sockaddr_in addr_data;
socklen_t addr_data_len = sizeof(addr_data);
char username[1024]; //用戶名
char password[1024]; //密碼
int is_login = 0; //是否登錄
int is_pasv = 0; //是否被動模式
send_msg(fd_client, “220 FTP Server ready\r\n”);
while(1)
{
memset(cmd, 0, sizeof(cmd));
ret = recv(fd_client, cmd, sizeof(cmd)-1, 0);
if(ret == -1)
{
perror(“recv error”);
return;
}
else if(ret == 0)
{
printf(“client quit\n”);
return;
}
cmd[ret] = ‘\0’;
printf(“%s”, cmd);
if(strncmp(cmd, “USER “, 5) == 0) //用戶名
{
sscanf(cmd, “USER %s”, username);
printf(“user: %s\n”, username);
send_msg(fd_client, “331 Password required for %s\r\n”, username);
}
else if(strncmp(cmd, “PASS “, 5) == 0) //密碼
{
sscanf(cmd, “PASS %s”, password);
printf(“pass: %s\n”, password);
send_msg(fd_client, “230 User logged in\r\n”);
is_login = 1;
}
else if(strncmp(cmd, “SYST”, 4) == 0) //系統(tǒng)信息
{
send_msg(fd_client, “215 UNIX Type: L8\r\n”);
}
else if(strncmp(cmd, “TYPE “, 5) == 0) //文件類型
{
send_msg(fd_client, “200 Switching to %s mode\r\n”, cmd+5);
}
else if(strncmp(cmd, “PWD”, 3) == 0) //當前工作目錄
{
char pwd[1024];
getcwd(pwd, sizeof(pwd));
send_msg(fd_client, “257 %s\r\n”, pwd);
}
else if(strncmp(cmd, “CWD “, 4) == 0) //更改工作目錄
{
char path[1024];
sscanf(cmd, “CWD %s”, path);
if(chdir(path) == 0)
{
send_msg(fd_client, “250 Directory successfully changed\r\n”);
}
else
{
send_msg(fd_client, “550 Fled to change directory\r\n”);
}
}
else if(strncmp(cmd, “PORT “, 5) == 0) //主動模式
{
//提取IP地址和端口號
sscanf(cmd+5, “%[^,],%d,%d,%d,%d,%d”, ip_data, &port_data, &port_data, &port_data, &port_data, &port_data);
//創(chuàng)建數據連接套接字
fd_data = socket(AF_INET, SOCK_STREAM, 0);
if(fd_data == -1)
{
perror(“socket error”);
continue;
}
memset(&addr_data, 0, sizeof(addr_data));
addr_data.sin_family = AF_INET;
addr_data.sin_addr.s_addr = inet_addr(ip_data);
addr_data.sin_port = htons(port_data);
//連接客戶端
ret = connect(fd_data, (struct sockaddr *)&addr_data, addr_data_len);
if(ret == -1)
{
perror(“connect error”);
close(fd_data);
fd_data = -1;
continue;
}
is_pasv = 0;
send_msg(fd_client, “200 Port command successful\r\n”);
}
else if(strncmp(cmd, “PASV”, 4) == 0) //被動模式
{
//創(chuàng)建數據連接套接字
fd_data = socket(AF_INET, SOCK_STREAM, 0);
if(fd_data == -1)
{
perror(“socket error”);
continue;
}
//設置套接字地址復用
int on = 1;
ret = setsockopt(fd_data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if(ret == -1)
{
perror(“setsockopt error”);
close(fd_data);
fd_data = -1;
continue;
}
memset(&addr_data, 0, sizeof(addr_data));
addr_data.sin_family = AF_INET;
addr_data.sin_addr.s_addr = INADDR_ANY;
addr_data.sin_port = htons(0);
//綁定套接字
ret = bind(fd_data, (struct sockaddr *)&addr_data, addr_data_len);
if(ret == -1)
{
perror(“bind error”);
close(fd_data);
fd_data = -1;
continue;
}
//獲取套接字綁定的端口號
ret = getsockname(fd_data, (struct sockaddr *)&addr_data, &addr_data_len);
if(ret == -1)
{
perror(“getsockname error”);
close(fd_data);
fd_data = -1;
continue;
}
is_pasv = 1;
//返回響應碼
unsigned char *p = (unsigned char *)&addr_data.sin_addr.s_addr;
send_msg(fd_client, “227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n”, p[0], p[1], p[2], p[3], addr_data.sin_port>>8, addr_data.sin_port&0xff);
}
else if(strncmp(cmd, “LIST”, 4) == 0) //列出目錄內容
{
if(!is_login)
{
send_msg(fd_client, “530 Please login with USER and PASS\r\n”);
continue;
}
if(fd_data == -1)
{
send_msg(fd_client, “425 Use PORT or PASV first\r\n”);
continue;
}
char path[1024];
getcwd(path, sizeof(path));
ret = read_dir(path, buf, sizeof(buf));
if(ret == -1)
{
send_msg(fd_client, “550 Fled to list directory\r\n”);
continue;
}
if(is_pasv)
{
//等待客戶端連接
int fd = accept(fd_data, NULL, NULL);
if(fd == -1)
{
perror(“accept error”);
continue;
}
//向客戶端發(fā)送目錄內容
send_msg(fd_client, “150 Opening ASCII mode data connection for file list\r\n”);
send_msg(fd, “%s\r\n”, buf);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd);
}
else
{
//向客戶端發(fā)送目錄內容
send_msg(fd_client, “150 Opening ASCII mode data connection for file list\r\n”);
send_msg(fd_data, “%s\r\n”, buf);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd_data);
fd_data = -1;
}
}
else if(strncmp(cmd, “RETR “, 5) == 0) //下載文件
{
if(!is_login)
{
send_msg(fd_client, “530 Please login with USER and PASS\r\n”);
continue;
}
if(fd_data == -1)
{
send_msg(fd_client, “425 Use PORT or PASV first\r\n”);
continue;
}
sscanf(cmd, “RETR %s”, filename);
fp = fopen(filename, “rb”);
if(fp == NULL)
{
send_msg(fd_client, “550 Fled to open file\r\n”);
continue;
}
if(is_pasv)
{
//等待客戶端連接
int fd = accept(fd_data, NULL, NULL);
if(fd == -1)
{
perror(“accept error”);
continue;
}
//發(fā)送文件內容
send_msg(fd_client, “150 Opening BINARY mode data connection for %s (%ld bytes)\r\n”, filename, get_file_size(fp));
send_file(fd, fp);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd);
}
else
{
//發(fā)送文件內容
send_msg(fd_client, “150 Opening BINARY mode data connection for %s (%ld bytes)\r\n”, filename, get_file_size(fp));
send_file(fd_data, fp);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd_data);
fd_data = -1;
}
fclose(fp);
}
else if(strncmp(cmd, “STOR “, 5) == 0) //上傳文件
{
if(!is_login)
{
send_msg(fd_client, “530 Please login with USER and PASS\r\n”);
continue;
}
if(fd_data == -1)
{
send_msg(fd_client, “425 Use PORT or PASV first\r\n”);
continue;
}
sscanf(cmd, “STOR %s”, filename);
fp = fopen(filename, “wb”);
if(fp == NULL)
{
send_msg(fd_client, “550 Fled to create file\r\n”);
continue;
}
if(is_pasv)
{
//等待客戶端連接
int fd = accept(fd_data, NULL, NULL);
if(fd == -1)
{
perror(“accept error”);
continue;
}
//接收文件內容
send_msg(fd_client, “150 Opening BINARY mode data connection for %s\r\n”, filename);
recv_file(fd, fp);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd);
}
else
{
//接收文件內容
send_msg(fd_client, “150 Opening BINARY mode data connection for %s\r\n”, filename);
recv_file(fd_data, fp);
send_msg(fd_client, “226 Transfer complete\r\n”);
close(fd_data);
fd_data = -1;
}
fclose(fp);
}
else if(strncmp(cmd, “QUIT”, 4) == 0) //斷開連接
{
send_msg(fd_client, “221 Goodbye\r\n”);
break;
}
else
{
send_msg(fd_client, “502 Command not implemented\r\n”);
}
}
}
“`
其中,send_msg()函數用于向客戶端發(fā)送數據,recv_file()和send_file()函數用于接收和發(fā)送文件,read_dir()函數用于讀取目錄信息,get_file_size()函數用于獲取文件大小。
三、FTP客戶端的使用
Linux系統(tǒng)本身提供了FTP客戶端工具,使用語法如下:
“`
ftp [options] [hostname]
“`
其中,hostname指定FTP服務端的地址,options包括:
– -n:禁止自動登錄;
– -v:顯示服務器響應信息;
– -d:啟用調試輸出;
– -i:禁止交互式提示;
– -g:關閉全局展開;
– -G:開啟全局展開;
– -r:啟用被動模式;
– -p:指定數據端口。
FTP客戶端的使用比較簡單,基本的命令如下:
– ls:列出服務器當前目錄的內容;
– cd:更改服務器的當前目錄;
– put:上傳文件;
– get:下載文件;
– quit:斷開FTP連接。
例如,使用FTP客戶端下載文件的命令為:
“`
ftp> get filename
“`
其中,filename是需要下載的文件名。
四、
相關問題拓展閱讀:
- linux怎么搭建ftp服務器
linux怎么搭建ftp服務器
在Linux中ftp服務器的全名叫 vsftpd,我們需要利用相關命令來開啟安彎派裝ftp服務器,然后再在vsftpd.conf中進行相關配置,下面我來介紹在Ubuntu中vsftpd安裝與配置增加用戶的方法。
(1)、首先用命令檢查是否安裝了vsftpd
vsftpd -version
如果未安裝用一下命令安裝
sudo apt-get install vsftpd
安裝完成后,再次輸入vsftpd -version命令查看是否安裝成功
(2)、新建一個文件夾用于FTP的工作目錄
mkdir /home/ftp
(3)、新建FTP用戶并設置密碼以及工作目錄
ftpname為你為該ftp創(chuàng)建的用戶名
sudo useradd -d /home/ftp -s /bin/bash ftpname
為新建的用戶設置密碼
passwd ftpname
【注釋:用cat etc/passwd可以查看搏滾當前系統(tǒng)用戶】
(4)、修改vsftpd配置文件
用命令打開vsftpd.conf
vi vsftpd.conf
設置屬性值
anonymous_enable=NO #禁止匿名訪問
local_enable=YES
write_enable =YES
保存返回
(5)、啟動vsftpd服務
service vsftpd start
(6)、在資源管理器,或者瀏覽器中ftp服務器
輸入賬號,密碼登基鬧余錄即可
linuxC語言實現(xiàn)ftp服務的介紹就聊到這里吧,感謝你花時間閱讀本站內容,更多關于linuxC語言實現(xiàn)ftp服務,Linux平臺下使用C語言實現(xiàn)FTP服務,linux怎么搭建ftp服務器的信息別忘了在本站進行查找喔。
創(chuàng)新互聯(lián)服務器托管擁有成都T3+級標準機房資源,具備完善的安防設施、三線及BGP網絡接入帶寬達10T,機柜接入千兆交換機,能夠有效保證服務器托管業(yè)務安全、可靠、穩(wěn)定、高效運行;創(chuàng)新互聯(lián)專注于成都服務器托管租用十余年,得到成都等地區(qū)行業(yè)客戶的一致認可。
標題名稱:Linux平臺下使用C語言實現(xiàn)FTP服務(linuxC語言實現(xiàn)ftp服務)
轉載來于:http://m.5511xx.com/article/dhdddoi.html


咨詢
建站咨詢
