FTP_SERVER 소스코드 추천좀 부탁드립니다.
매우간단한 FTP CLIENT / SERVER 프로그램을 리눅스에서 C 로 짜려고 하는데요
제가 보고있는 책(네트워크프로그래밍,ITC)에는 클라이언트 코드와 설명만 나오고
서버에 대한 언급이 전혀 없어서.... 혹시 이책에서 제시하는 클라이언트 코드에 대응해서
공부할만한 서버 소스 코드를 알고 계신분은 추천 부탁드립니다.
상용으로 잘짜여진 FTP 서버 소스는 제 지식으로는 이해가 잘 안가더락요 쉬운걸로 좀 부탁드려요~
아래는 교제에 있던 클라이언 소스 코드입니다.
/*
Passive Mode를 이용한 간단한 FTP_Client
Command
1. PWD : 현재의 디렉토리 표시=>PWD
2. CWD : 디렉토리 변경 => CWD 이동할디록토리명
3. ls : 현재의 디렉토리 파일리스트 표시 =>ls
4. get : 파일 다운로드 => get 다운로드파일이름
5. put : 파일 업로드 => put 업로드파일이름
6. bye : 종료 => bye
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*접속 FTP Sever 포트 설정*/
#define FTP_PORT 21
void* send_data_thread(void* sockfd);
void* recv_ctrl_thread(void* sockfd);
void* recv_data_thread(void* sockfd);
void help();
void get_remote_port(char * message);
void parse_filename(char * temp_buff, char * buff);
struct sockaddr_in ftp_send_addr;
struct sockaddr_in ftp_recv_data_addr;
int ftp_send_sockfd;
/*현재 진행명령에 대한 인덱스 : ls(1), put(2), get(3)*/
int command_index;
/*다운로드나 업로드 할 파일 이름*/
char file_name[1024];
int main(int argc, char* argv[])
{
pthread_t recv_thread;
char cmd_ctrl_buff[1024] = {0};
char temp_buff[1024] = {0};
/*FTP Server에 대한 주소 및 포트 설정*/
ftp_send_addr.sin_family = AF_INET;
ftp_send_addr.sin_port = htons(FTP_PORT);
ftp_send_addr.sin_addr.s_addr = inet_addr(argv[1]);
memset(&(ftp_send_addr.sin_zero), 0, 8);
/*서버 접속 소켓 생성*/
ftp_send_sockfd = socket(AF_INET, SOCK_STREAM, 0);
/*서버 접속 및 초기 서버 응답 데이타 수신*/
connect(ftp_send_sockfd, (struct sockaddr*)&ftp_send_addr, sizeof(struct sockaddr_in));
memset(temp_buff, '\0', sizeof(temp_buff));
read(ftp_send_sockfd, temp_buff, sizeof(temp_buff));
printf("**>%s\n", temp_buff);
/*FTP서버 로그인 : 로그인 실패시 계속 로그인 시도 3번까지 가능함. 이후는 서버에서 파이프를 끊음*/
while(1)
{
printf("User Name:");
memset(temp_buff, '\0', sizeof(temp_buff));
fgets(temp_buff, sizeof(temp_buff), stdin);
sprintf(cmd_ctrl_buff,"USER %s", temp_buff);
/*FTP Command : USER 유저명*/
write(ftp_send_sockfd, cmd_ctrl_buff, strlen(cmd_ctrl_buff));
memset(temp_buff, '\0', sizeof(temp_buff));
read(ftp_send_sockfd, temp_buff, sizeof(temp_buff));
printf("**>%s\n", temp_buff);
printf("Password:");
memset(temp_buff, '\0', sizeof(temp_buff));
fgets(temp_buff, sizeof(temp_buff), stdin);
memset(cmd_ctrl_buff, '\0', sizeof(cmd_ctrl_buff));
/*FTP Command : PASS 패스워드*/
sprintf(cmd_ctrl_buff, "PASS %s", temp_buff);
write(ftp_send_sockfd, cmd_ctrl_buff, strlen(cmd_ctrl_buff));
memset(temp_buff, '\0', sizeof(temp_buff));
read(ftp_send_sockfd, temp_buff, sizeof(temp_buff));
printf("**>%s\n", temp_buff);
if (strncmp(temp_buff, "530", 3) == 0)
continue;
else
break;
}
pthread_create(&recv_thread, NULL, recv_ctrl_thread, (void*)ftp_send_sockfd);
help();
while(1)
{
memset(temp_buff, '\0', sizeof(temp_buff));
memset(cmd_ctrl_buff, '\0',sizeof(cmd_ctrl_buff));
printf("Command:");
/*사용자로부터의 명령 입력*/
fgets(temp_buff, sizeof(temp_buff), stdin);
/*ls명령 처리 : 데이타 커넥션 생성*/
if (strncmp(temp_buff, "ls", 2) == 0 || strncmp(temp_buff, "dir", 3) == 0)
{
memset(temp_buff, '\0', sizeof(temp_buff));
/*passive 모드설정*/
sprintf(temp_buff, "pasv\n");
write(ftp_send_sockfd, temp_buff, strlen(temp_buff));
command_index = 1;
memset(temp_buff, '\0', sizeof(temp_buff));
/*접속된 현재의 디렉토리의 파일 리스트에 대한 명령*/
sprintf(temp_buff, "list -a\n");
write(ftp_send_sockfd, temp_buff, strlen(temp_buff));
sleep(1);
}
/*put 명령 처리 : 데이타 커넥션 생성*/
else if(strncmp(temp_buff, "put", 3) == 0)
{
/*업로드할 파일명 파싱*/
temp_buff[strlen(temp_buff)-1] = '\0';
memset(cmd_ctrl_buff, '\0', sizeof(cmd_ctrl_buff));
parse_filename(temp_buff, cmd_ctrl_buff);
memset(file_name, '\0', sizeof(file_name));
sprintf(file_name, "%s", cmd_ctrl_buff);
memset(cmd_ctrl_buff, '\0', sizeof(cmd_ctrl_buff));
command_index = 2;
/*passive 모드 설정*/
sprintf(cmd_ctrl_buff, "pasv\n");
write(ftp_send_sockfd, cmd_ctrl_buff, strlen(cmd_ctrl_buff));
sleep(1);
}
/*get 명령 처리 : 데이타 커넥션 생성*/
else if(strncmp(temp_buff, "get", 3) == 0)
{
temp_buff[strlen(temp_buff)-1] = '\0';
memset(cmd_ctrl_buff, '\0', sizeof(cmd_ctrl_buff));
/*passive 모드 설정*/
sprintf(cmd_ctrl_buff, "pasv\n");
write(ftp_send_sockfd, cmd_ctrl_buff, strlen(cmd_ctrl_buff));
command_index = 3;
/*다운로드할 파일명 파싱*/
memset(cmd_ctrl_buff, '\0', sizeof(cmd_ctrl_buff));
parse_filename(temp_buff, cmd_ctrl_buff);
memset(file_name, '\0', sizeof(file_name));
sprintf(file_name, "%s", cmd_ctrl_buff);
memset(temp_buff, '\0', sizeof(temp_buff));
/*서버로의 명령 전달 retr 다운로드 파일명*/
sprintf(temp_buff, "retr %s\n", cmd_ctrl_buff);
write(ftp_send_sockfd, temp_buff, strlen(temp_buff));
sleep(1);
}
/*help메뉴*/
else if (strncmp(temp_buff, "help", 4) == 0)
{
help();
}
/*FTP 종료*/
else if (strncmp(temp_buff, "bye", 3) == 0)
{
memset(temp_buff, '\0', sizeof(temp_buff));
sprintf(temp_buff, "quit\n");
write(ftp_send_sockfd, temp_buff, strlen(temp_buff));
exit(0);
}
/*그외 명령어 처리 : PWD, CWD*/
else
{
write(ftp_send_sockfd, temp_buff, strlen(temp_buff));
sleep(1);
}
}
return 0;
}
/*사용자 입력으로 부터 파일명 파싱 ex)get lib.h => lib.h를 문자열로 저장*/
void parse_filename(char * temp_buff, char * buff)
{
int index = 3;
int count = 0;
for (; index < strlen(temp_buff); index++)
{
if (temp_buff
{
continue;
}
else
{
buff[count] = temp_buff
count++;
}
}
}
/*
업로드 파일 전송 stor 업로드 파일
업로드할 파일을 열어 해당 파일을 data_buff에 담아서 서버로 전송
한번의 연결후 소켓 종료
*/
void * send_data_thread(void* sockfd)
{
int fd;
char data_buff[4096] = {0};
int data_size;
fd = open(file_name, O_RDONLY);
if (fd == -1)
printf("**>no exist file\n");
else
{
sprintf(data_buff, "stor %s\n", file_name);
write(ftp_send_sockfd,data_buff, strlen(data_buff));
while(1)
{
memset(data_buff, '\0', sizeof(data_buff));
data_size = read(fd, data_buff, sizeof(data_buff));
if (data_size == 0)
break;
else
write((int)sockfd, data_buff, data_size);
}
}
close((int)sockfd);
close(fd);
}
/*
서버에서 수신되는 컨트롤 메세지가 아닌 실제 데이타에 대한 수신 처리
한번의 연결후 소켓 종료
*/
void * recv_data_thread(void* sockfd)
{
int fd;
char data_buff[4096] = {0};
int data_size;
if (command_index == 3)
fd = open(file_name, O_WRONLY|O_CREAT,0644);
while(1)
{
memset(data_buff, '\0', sizeof(data_buff));
data_size = read((int)sockfd, data_buff, sizeof(data_buff));
if (data_size == 0)
{
break;
}
else
{
if (command_index == 1)
printf("**>%s\n", data_buff);
else if (command_index == 3)
{
write(fd, data_buff, data_size);
}
}
}
close((int)sockfd);
if (command_index == 3)
close(fd);
}
/*서버로 부터 수신되는 컨트롤 메세지*/
void * recv_ctrl_thread(void* sockfd)
{
char ctrl_buff[1024] = {0};
int msg_size;
int temp_port;
while(1)
{
memset(ctrl_buff, '\0', sizeof(ctrl_buff));
msg_size = read((int)sockfd, ctrl_buff, sizeof(ctrl_buff));
if (msg_size <= 0)
continue;
/*
passive모드 설정을 위해서 227응답과 함께 서버로부터 해당 IP와 PORT가 전달된다.
클라이언트는 해당 IP와 PORT를 파싱하여 그쪽으로 접속을 한다. 이후 실제 데이타는
현재 연결된 커넥션으로 수신 및 송신된다
*/
if (strncmp(ctrl_buff, "227", 3) == 0)
{
printf("**>%s\n", ctrl_buff);
get_remote_port(ctrl_buff);
}
/*
데이타 다운로드 실패응답 코드
실제 클라이언트에서는 다운로드시 먼저 로컬에 해당 파일을 생성하는데 다운로드 실패시는
해당 파일을 삭제하는 코드
*/
else if (strncmp(ctrl_buff, "550", 3) == 0 && command_index == 3)
{
printf("**>%s\n", ctrl_buff);
memset(ctrl_buff, '\0', sizeof(ctrl_buff));
sprintf(ctrl_buff, "rm -f %s", file_name);
system(ctrl_buff);
}
else
printf("**>%s\n", ctrl_buff);
}
close((int)sockfd);
printf("Exit Thread");
exit(0);
}
/*도움말 */
void help()
{
printf("#########################################################\n");
printf("# #\n");
printf("# FTP Client Command #\n");
printf("# #\n");
printf("# 1. ls or dir : Show Directory File List #\n");
printf("# 2. get remote file name : Download File FTP Server #\n");
printf("# 3. put local file name?: Upload File FTP Server #\n");
printf("# 4. pwd : current location #\n");
printf("# 5. cwd : change directory location #\n");
printf("# 6. bye : quit FTP client #\n");
printf("# 7. help : Show Help Menu #\n");
printf("#########################################################\n");
}
/*
pasv명령시 서버로부터 227응답과 함께 서버의 IP와 PORT를 보내주는 데 이함수는 이 IP와 PORT를 파싱하여
해당 서버로 접속하는 역할을 한다
*/
void get_remote_port(char * message)
{
pthread_t t;
int index;
int parse_start = 0;
char ip_buff[512] = {0};
char port_buff1[10] = {0};
char port_buff2[10] = {0};
char cport_number[2];
int iport_number;
char * ref_number;
int comma_count = 0;
int buff_count = 0;
struct sockaddr_in connect_addr;
int connect_fd;
int connect_result;
for (index = 0; index < strlen(message); index++)
{
if (message
{
parse_start = 1;
continue;
}
else if (message
break;
if (parse_start == 1)/*addr process*/
{
if (message
{
comma_count++;
if (comma_count == 4)
{
buff_count = 0;
parse_start = 2;
continue;
}
else
{
ip_buff[buff_count] = '.';
buff_count++;
}
}
else
{
ip_buff[buff_count] = message
buff_count++;
}
}
if (parse_start == 2)/*port process*/
{
if (message
{
comma_count++;
buff_count = 0;
}
else
{
if (comma_count == 5)
{
port_buff2[buff_count] = message
buff_count++;
}
else
{
port_buff1[buff_count] = message
buff_count++;
}
}
}
}
/*전달된 포트는 상위바이트와 하위 바이트가 뒤바뀐채로 온다.
여기에서 바로 잡아주는 역할을 한다*/
ref_number = (char*)&iport_number;
ref_number[0] = (char)atoi(port_buff2);
ref_number[1] = (char)atoi(port_buff1);
/*데이타를 전달을 위한 서버 접속*/
connect_addr.sin_family = AF_INET;
connect_addr.sin_port = htons(iport_number);
connect_addr.sin_addr.s_addr = inet_addr(ip_buff);
memset(&(connect_addr.sin_zero), 0, 8);
connect_fd = socket(AF_INET, SOCK_STREAM, 0);
connect_result = connect(connect_fd, (struct sockaddr*)&connect_addr, sizeof(connect_addr));
/*데이타 커넥션후 각 명령에 따라 서버로부터 수신을 전용으로 하는 쓰레드
서버로부터 송신을 전용으로하는 쓰레드가 따로 호출된다
ls 명령의 경우 수신 쓰레드, get명령의 경우 수신 쓰레더
put명령의 경우 송신 쓰레드 생성
*/
if (command_index == 1 || command_index == 3)
pthread_create(&t, NULL,recv_data_thread, (void*)connect_fd);
else if (command_index == 2)
pthread_create(&t, NULL,send_data_thread, (void*)connect_fd);
}
음.. 일단 올려주신
음.. 일단 올려주신 코드를 보진 않았지만, 어쨋든, 서버 코드를 구하시는 것보다는 FTP의 RFC 문서를 보시고 직접 구현을 해보시는 좋은 경험이 되실 거 같습니다. FTP 프로토콜은 사실 그리 복잡하지 않으며, 처음부터 모든 걸 구현하시려고 하기 보다는 command 하나하나를 구현하시면서 추가해 나가셔도 될 거 같습니다.
------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.
------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.
댓글 달기