Home
Categories
WIKI
Topic
User
LANGUAGE:
中文
English
自己动手写ftp服务器5(到此为止)
社区开发
2057
views ·
2
replies ·
To
floor
Go
zhqh100
deepin
2014-05-04 05:45
Author
实现了上传下载,我计划的功能也就实现了,本节稍微实现一下FTP无法缺少的删除和切换路径的功能,再进一步的功能如重命名、复制粘贴等功能各位可以直接看tinyftp的源代码。
开发语言:C语言
操作系统:LinuxDeepin 2013
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define bool unsigned short
typedef struct cmd_opts {
bool daemonize;
bool listen_any;
char *listen_addr;
int port;
int max_conn;
int userid;
char *chrootdir;
}cmd_opts;
//格式化字符串line
static int write_file(char *line,const char *mode,int num,const char *user,const char * group,int size,const char *date,const char*fl_name) {
sprintf(line,"%s %3d %-4s %-4s %8d %12s %s\r\n",mode,num,user,group,size,date,fl_name);
return 0;
}
//判断字符是否是以某指令开始
int strBeginWith(char *str,char *substr){
while(*str && *substr && *str==*substr){
str++;
substr++;
}
if(*substr=='\0')
return 1;
else
return 0;
}
//字符串转int
int toint(char *str) {
if (strlen(str) < 1)
{
return -1;
}
int retVal = str[0] - '0';
int i = 1;
while(str[i] >= '0' && str[i] <= '9')
{
retVal = retVal * 10 + str[i] - '0';
i++;
}
return retVal;
}
//向客户端发送消息
int send_repl(int send_sock,char *msg) {
printf("Send Message:%s\n", msg);
if (send(send_sock, msg, strlen(msg),0) < 0) {
perror("send");
close(send_sock);
return -1;
}
return 0;
}
//创建数据传输的连接,用于处理"LIST","STOR","RETR"等请求
int make_client_connection(int client_port,const char* client_addr) {
int sock=-1;
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(client_addr);
servaddr.sin_port = htons (client_port);
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
send_repl(sock,"425 Can't open data connection.\r\n");
perror("socket");
return -1;
}
int status = connect (sock, (struct sockaddr *)&servaddr, sizeof (servaddr));
if(status!=0) {
send_repl(sock,"425 Can't open data connection.\r\n");
perror("connect");
return -1;
}
return sock;
}
//zhqh100@gmail.com
enum {
TRUE=1,
FALSE=0
};
int main(int argc,char *argv[])
{
struct cmd_opts *opts= malloc(sizeof(struct cmd_opts));
//设置一些初始配置
opts->daemonize = FALSE;
opts->listen_any = TRUE;
opts->port = 21;
opts->userid = 0;
opts->chrootdir = "./";
opts->max_conn = 5;
opts->listen_addr = NULL;
struct sockaddr_in servaddr;
memset((char *)&servaddr, 0, sizeof(servaddr));
//TCP协议
servaddr.sin_family = PF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons (opts->port);
int servaddr_len = sizeof(servaddr);
int sock;
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
return -1;
}
int flag = 1;
setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,(char *) &flag, sizeof(int));
setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(char *) &flag, sizeof(int));
//套接字绑定端口和地址
if(bind (sock, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
perror("bind");
return -1;
}
if(listen(sock,opts->max_conn) <0) {
perror("listen");
return -1;
}
int connection;
struct stat *s_buff = malloc(sizeof(struct stat));
char mode[11] = "----------";
char date[16];
struct passwd * pass_info = NULL;
struct group * group_info;
struct dirent *d_next;
char line[300];
DIR *dir;
//传输类型
int type;
//文件指针
int fpr;
//长度
int len;
//死循环等待客户端连接
while(1){
printf("Begin to accept\n");
if ((connection = accept(sock, (struct sockaddr *) &servaddr, &servaddr_len)) < 0) {
perror("accept");
return -1;
}
int pid = fork();
char msg[]= "220 Service ready for new user.\r\n";
char send_buff[1000];
char read_buff[1000];
char current_dir[1000];
//客户端IP地址和端口号
char client_addr[16];
int client_port;
//临时指针
char *temp;
int i;
int client_fd;
strcpy(current_dir,opts->chrootdir);
//获取当前绝对路径
getcwd(current_dir,sizeof(current_dir));
len = strlen(msg);
if(pid==0) {
//客户端连接后,服务器先主动发送200 Service ready
if (send(connection, msg, len,0) < 0) {
perror("send");
close(connection);
return -1;
}
while(1){
//初始化读取buffer和发送buffer
memset(send_buff,0,sizeof(send_buff));
memset(read_buff,0,sizeof(read_buff));
//等待接收客户端指令
int recvbuff = recv(connection,read_buff,sizeof(read_buff),0);
if(recvbuff<1) {
return -1;
}
if(recvbuff==sizeof(read_buff)) {
return -2;
}
printf("Received:%s",read_buff);
if (strBeginWith(read_buff,"USER")){
//收到客户端发送的用户名,这里不处理用户名,均答复登陆成功
//注释代码也可用,只是这里修改后,发送的消息更精简了
//后面的人性化提示可以省略,但\r\n不可省略,经Filezilla和 CuteFTP客户端验证,必须把\r\n带上才可正常识别
// if (send_repl(connection, "331 Anonymous login okay, send your complete email as your password.\r\n") < 0)
if(send_repl(connection,"331 Anonymous\r\n") < 0)
return -1;
}else if(strBeginWith(read_buff,"PASS")){
//可以看到ftp协议发送的密码均为明文发送
//这里不做密码校验,均答复已登录
// if (send_repl(connection, "230 User logged in, proceed.\r\n") < 0)
if (send_repl(connection, "230 User\r\n") < 0)
return -1;
}else if (strBeginWith(read_buff,"SYST")){
//问操作系统类型
//下面UNIX的可替代选项参考 http://www.iana.org/assignments/operating-system-names/operating-system-names.xhtml
if (send_repl(connection, "215 UNIX\r\n") < 0)
return -1;
}else if (strBeginWith(read_buff,"PWD")){
//当前路径
sprintf(send_buff,"257 %s\r\n",current_dir);
if (send_repl(connection, send_buff) < 0)
return -1;
}else if (strBeginWith(read_buff,"PORT")){
//客户端发来的IP和端口号
char *temp = strtok(read_buff + 5,",");
strcpy(client_addr, temp);
for (i = 0; i < 3; ++i)
{
strcat(client_addr, ".");
temp = strtok(NULL, ",");
strcat(client_addr, temp);
}
printf("client_addr = %s\n", client_addr);
temp = strtok(NULL, ",");
client_port = toint(temp);
temp = strtok(NULL, ",");
client_port = client_port * 256 + toint(temp);
printf("client_port = %d\n", client_port);
//回复传说中的200 OK
if (send_repl(connection, "200 \r\n") < 0)
return -1;
}else if (strBeginWith(read_buff,"LIST")){
//列出当前路径的所有文件
client_fd = make_client_connection(client_port, client_addr);
if (client_fd != -1){
dir = opendir(current_dir);
if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
return -1;
while(1) {
d_next = readdir(dir);
if(d_next==NULL)
break;
line[0]='\0';
stat(d_next->d_name,s_buff);
int b_mask = s_buff->st_mode & S_IFMT;
//判断是文件还是文件夹
if(b_mask == S_IFDIR) {
mode[0]='d';
} else if(b_mask == S_IFREG){
mode[0]='-';
} else {
return FALSE;
}
mode[1] = (s_buff->st_mode & S_IRUSR)?'r':'-';
mode[2] = (s_buff->st_mode & S_IWUSR)?'w':'-';
mode[3] = (s_buff->st_mode & S_IXUSR)?'x':'-';
mode[4] = (s_buff->st_mode & S_IRGRP)?'r':'-';
mode[5] = (s_buff->st_mode & S_IWGRP)?'w':'-';
mode[6] = (s_buff->st_mode & S_IXGRP)?'x':'-';
mode[7] = (s_buff->st_mode & S_IROTH)?'r':'-';
mode[8] = (s_buff->st_mode & S_IWOTH)?'w':'-';
mode[9] = (s_buff->st_mode & S_IXOTH)?'x':'-';
strftime(date,13,"%b %d %H:%M",localtime(&(s_buff->st_mtime)));
pass_info = getpwuid(s_buff->st_uid);
group_info = getgrgid(s_buff->st_gid);
write_file(line,mode,s_buff->st_nlink,pass_info->pw_name,group_info->gr_name,s_buff->st_size,date,d_next->d_name);
printf("line = %s\n", line);
send(client_fd,line,strlen(line),0);
}
close(client_fd);
if(send_repl(connection,"226 Closing data connection.\r\n"))
return -1;
closedir(dir);
client_fd = -1;
}
}else if(strBeginWith(read_buff,"TYPE")){
switch(read_buff[5]){
//type A Ascii文件流,type I,二进制文件流
case 'I':
if (send_repl(connection, "200 \r\n") < 0)
return -1;
type = 1;break;
case 'A':
if (send_repl(connection, "200 \r\n") < 0)
return -1;
type = 2;break;
case 'L':printf("get TYPE L,need to handle the request");break;
default: break;
}
}else if(strBeginWith(read_buff,"RETR")){
client_fd = make_client_connection(client_port,client_addr);
if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
return -1;
temp = read_buff+5;//filename
//我原以为temp就是文件名,实际却是客户端发过来的数据会在可见字符后面跟一堆不可见字符,所以需要进行处理
//这里进行简单粗暴的处理,会造成无法接收中文文件的传输
i = 0;
while(1){
if(temp[i] < ' ' | temp[i] > '~'){
temp[i] = '\0';
break;
}
i++;
}
fpr = open(temp,O_RDONLY);
if (fpr < 0)
{
perror("open");
return -1;
}
while(1){
len = read(fpr,send_buff,sizeof(send_buff));
if(len>0) {
send(client_fd,send_buff,len,0);
}
else {
break;
}
}
close(fpr);
send_repl(connection,"226 Closing data connection.\r\n");
close(client_fd);
}else if(strBeginWith(read_buff,"STOR")){
client_fd = make_client_connection(client_port,client_addr);
if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
return -1;
temp = read_buff+5;//filename
i = 0;
while(1){
if(temp[i] < ' ' | temp[i] > '~'){
temp[i] = '\0';
break;
}
i++;
}
fpr = open(temp,O_WRONLY|O_CREAT,0644);
if (fpr < 0)
{
perror(temp);
return -1;
}
while(1){
len = recv(client_fd,read_buff,sizeof(read_buff),0);
if(len>0) {
write(fpr,read_buff,len);
}
else {
break;
}
}
close(fpr);
send_repl(connection,"226 Closing data connection.\r\n");
close(client_fd);
}else if(strBeginWith(read_buff,"CWD")){
temp = read_buff+4;//dir name
i = 0;
while(1){
if(temp[i] < ' ' | temp[i] > '~'){
temp[i] = '\0';
break;
}
i++;
}
if(chdir(temp) == 0) {
if(getcwd(current_dir,sizeof(current_dir))!=NULL) {
send_repl(connection,"250 Requested file action okay, completed.\r\n");
}
}
}else if(strBeginWith(read_buff,"DELE")){
temp = read_buff+5;//dir name
i = 0;
while(1){
if(temp[i] < ' ' | temp[i] > '~'){
temp[i] = '\0';
break;
}
i++;
}
printf("temp = %s\n", temp);
if(unlink(temp)==0){
send_repl(connection,"250 Requested file action okay, completed.\r\n");
}
}else{
//其他一律回复500,I don't know
if (send_repl(connection, "500 \r\n") < 0)
return -1;
}
}
} else if(pid>0) {
close(connection);
}
}
return 0;
}
Copy the Code
ftp就先研究到这里,接下来开始学习http的源码,可能的话后面再发一些心得
Reply
Like 0
Favorite
View the author
All Replies
yeser
deepin
2014-05-05 06:33
#1
虽一点不懂,但看看行数不多,觉得不难,呵呵!
Reply
Like 0
View the author
键盘机
deepin
2014-07-29 22:52
#2
马克一下
Reply
Like 0
View the author
Please
sign
in first
Featured Collection
Change
UOS AI 2.8 Released! Three New Intelligent Agents & Major Evolution
Solid Q&A | deepin 25 Common Questions – The Immutable System Edition
New Thread
Popular Events
More
开发语言:C语言
操作系统:LinuxDeepin 2013
ftp就先研究到这里,接下来开始学习http的源码,可能的话后面再发一些心得