Home
Categories
WIKI
Topic
User
LANGUAGE:
中文
English
自己动手写ftp服务器2(更进一步)
社区开发
1816
views ·
0
replies ·
To
floor
Go
zhqh100
deepin
2014-04-30 08:29
Author
前面基本把框架搭建好,后面的逻辑处理就比较简单了,一步一步对功能进行完善。
为了简化代码,对于一些合法性校验都是尽量简化,只处理核心功能。
封装是编程的最基本思想,不过我这里还是尽量减少封装,因为较多的封装的话在看代码时会不得不停顿下来找调用到的地方,增加看代码的难度。
开发语言:C语言
操作系统:LinuxDeepin 2013
增加了后面的一点交互,代码如下:
#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;
//判断字符是否是以某指令开始
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;
}
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;
//死循环等待客户端连接
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));
int 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){
}
}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
用CuteFTP客户端登陆,打印信息如下:
*** CuteFTP 9.0 - build Jun 25 2013 ***
STATUS:> [2014/4/30 0:28:13] Getting listing ""...
STATUS:> [2014/4/30 0:28:13] Connecting to FTP server... 192.168.11.29:21 (ip = 192.168.11.29)...
STATUS:> [2014/4/30 0:28:13] Socket connected. Waiting for welcome message...
[2014/4/30 0:28:13] 220 Service ready for new user.
STATUS:> [2014/4/30 0:28:13] Connected. Authenticating...
COMMAND:> [2014/4/30 0:28:13] USER anonymous
[2014/4/30 0:28:13] 331 Anonymous
COMMAND:> [2014/4/30 0:28:13] PASS *****
[2014/4/30 0:28:13] 230 User
STATUS:> [2014/4/30 0:28:13] Login successful.
COMMAND:> [2014/4/30 0:28:13] SYST
[2014/4/30 0:28:13] 215 UNIX
STATUS:> [2014/4/30 0:28:13] Host type detected: Unix.
COMMAND:> [2014/4/30 0:28:13] PWD
[2014/4/30 0:28:13] 257 /home/taiji/桌面
STATUS:> [2014/4/30 0:28:13] Home directory: /home/taiji/桌面
COMMAND:> [2014/4/30 0:28:13] FEAT
[2014/4/30 0:28:13] 500
STATUS:> [2014/4/30 0:28:13] This site doesn't support the 'features' command.
STATUS:> [2014/4/30 0:28:13] Setting up character encoding.
COMMAND:> [2014/4/30 0:28:13] OPTS UTF8 on
[2014/4/30 0:28:13] 500
STATUS:> [2014/4/30 0:28:13] Using local encoding.
COMMAND:> [2014/4/30 0:28:13] REST 100
[2014/4/30 0:28:13] 500
STATUS:> [2014/4/30 0:28:13] This site cannot resume broken downloads.
COMMAND:> [2014/4/30 0:28:13] PASV
[2014/4/30 0:28:13] 500
STATUS:> [2014/4/30 0:28:13] PASV failed, trying PORT.
COMMAND:> [2014/4/30 0:28:13] PORT 192,168,11,16,209,127
[2014/4/30 0:28:13] 200
COMMAND:> [2014/4/30 0:28:13] LIST
Copy the Code
Reply
Like 0
Favorite
View the author
All Replies
No replies yet
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
增加了后面的一点交互,代码如下:
用CuteFTP客户端登陆,打印信息如下: