Home
Categories
WIKI
Topic
User
LANGUAGE:
中文
English
自己动手写ftp服务器5(到此为止)
社区开发
2090
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 Ranking
Change
Bright is not working on deepin25, it is stuck
Nvidia Drivers in Fresh installed V25.0.10 with Secure Boot on.
Popular Events
More
开发语言:C语言
操作系统:LinuxDeepin 2013
ftp就先研究到这里,接下来开始学习http的源码,可能的话后面再发一些心得