이 프로젝트는 FTP(File Transfer Protocol) 서버와 클라이언트를 C 언어를 사용하여 구현한 시스템 프로그래밍 과제입니다. TCP/IP 소켓 프로그래밍을 기반으로 하며, 표준 FTP 명령어를 지원하는 완전한 FTP 서비스 시스템을 제공합니다.
작성자: 김준원
작성일: 2024/05/28
운영체제: Ubuntu 20.04.06 LTS 64bits
- ✅ 서버 연결 및 사용자 인증
- ✅ FTP 명령어 변환 (사용자 명령 → FTP 명령)
- ✅ 파일 목록 조회 (
ls,ls -a,ls -l,ls -al) - ✅ 현재 작업 디렉토리 확인 (
pwd) - ✅ 디렉토리 이동 (
cd,cd ..) - ✅ 디렉토리 생성/삭제 (
mkdir,rmdir) - ✅ 파일 삭제 (
delete) - ✅ 파일 이름 변경 (
rename) - ✅ 파일 다운로드 (
get) - ✅ 파일 업로드 (
put) - ✅ 전송 모드 설정 (
bin,ascii,type binary,type ascii) - ✅ 프로그램 종료 (
quit) - ✅ 데이터 연결(PORT 모드) 자동 생성 및 관리
- ✅ 전송된 파일 크기 표시
- ✅ 동시 다중 클라이언트 지원 (Concurrent Server)
- ✅ IP 주소 기반 접근 제어 (
access.txt) - ✅ 사용자 인증 (
passwd파일 기반) - ✅ 로그인 실패 3회 제한
- ✅ 모든 FTP 명령어 처리 및 실행
- ✅ 데이터 연결 분리 (Control/Data Connection)
- ✅ 바이너리/ASCII 전송 모드 지원
- ✅ 파일 시스템 연동 (디렉토리 탐색, 파일 조작)
- ✅ KILL 신호(^C) 처리
- 운영체제: Linux/Unix (Ubuntu 20.04 이상 권장)
- 컴파일러: GCC
- 라이브러리: 표준 C 라이브러리
- 네트워크: TCP/IP 지원
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>Assignment3_3_D_2020202095/
│
├── cli.c # FTP 클라이언트 소스 코드
├── srv.c # FTP 서버 소스 코드
├── Makefile # 빌드 스크립트
├── access.txt # IP 접근 제어 파일
├── passwd # 사용자 인증 파일
├── ARCHITECTURE.md # 시스템 아키텍처 문서
└── README.md # 프로젝트 설명서 (본 파일)
- FTP 클라이언트 프로그램
- 사용자 입력을 받아 FTP 명령으로 변환하고 서버로 전송
- 서버 응답을 처리하여 결과를 출력
- FTP 서버 프로그램
- 클라이언트 연결을 받아 인증 후 명령 처리
fork()를 사용한 동시 다중 클라이언트 지원
- 허용할 IP 주소 패턴 목록
- 와일드카드(
*) 지원 - 예시:
127.0.0.1 128.134.64.* 203.245.*.*
- 사용자 인증 정보 저장 파일
/etc/passwd형식으로 구성- 사용자명과 비밀번호 검증에 사용
# 모든 프로그램 빌드
make
# 또는 개별 빌드
make cli # 클라이언트만 빌드
make srv # 서버만 빌드빌드 결과:
cli: 클라이언트 실행 파일srv: 서버 실행 파일
make clean # 생성된 실행 파일 삭제# 기본 사용법
./srv [포트번호]
# 예시: 포트 2121에서 서버 실행
./srv 2121서버 실행 시 주의사항:
- 서버는 지정된 포트에서 대기 상태로 시작됩니다
- 클라이언트 연결을 기다립니다
- KILL 신호(^C)로 종료합니다
# 기본 사용법
./cli [서버IP] [포트번호]
# 예시: 로컬 서버(포트 2121)에 연결
./cli 127.0.0.1 2121클라이언트 실행 후:
- 서버에서 접근 제어 확인 → Welcome 메시지 또는 거부 메시지
- 사용자명 입력 (
Input ID :) - 비밀번호 입력 (
Input Password :) - 로그인 성공 시
ftp>프롬프트 표시 - FTP 명령어 입력
현재 디렉토리의 파일 목록을 간단히 표시합니다.
ftp> ls
file1.txt
file2.txt
directory1/숨김 파일을 포함한 모든 파일을 표시합니다.
ftp> ls -a
./
../
file1.txt
.hidden_file파일의 상세 정보를 표시합니다 (권한, 소유자, 크기, 수정일 등).
ftp> ls -l
-rw-r--r-- 1 user group 1024 May 28 10:00 file1.txt
drwxr-xr-x 2 user group 4096 May 28 10:01 directory1/숨김 파일을 포함한 상세 정보를 표시합니다.
현재 작업 디렉토리의 절대 경로를 표시합니다.
ftp> pwd
257 "/home/user/ftp" is current directory지정한 디렉토리로 이동합니다.
ftp> cd directory1
250 CWD command suceeds.상위 디렉토리로 이동합니다.
ftp> cd ..
250 CWD command suceeds.새 디렉토리를 생성합니다.
ftp> mkdir newdir
250 MKD command performed successfully.빈 디렉토리를 삭제합니다.
ftp> rmdir olddir
250 RMD command performed successfully.파일을 삭제합니다.
ftp> delete file.txt
250 DELE command performed succesfully파일 또는 디렉토리 이름을 변경합니다.
ftp> rename old.txt new.txt
350 File exists, ready to rename
250 RNTO command succeeds서버에서 파일을 다운로드합니다.
ftp> get file.txt
150 Opening binary mode data connection.
226 Complete transmission.
OK. 1024 bytes is received.클라이언트에서 서버로 파일을 업로드합니다.
ftp> put localfile.txt
150 Opening binary mode data connection.
226 Complete transmission.
OK. 2048 bytes sent.바이너리 모드로 전환합니다 (기본값).
ftp> bin
201 Type set to I.ASCII 텍스트 모드로 전환합니다.
ftp> ascii
201 Type set to A.FTP 세션을 종료하고 클라이언트 프로그램을 종료합니다.
ftp> quit
221 Goodbye.이 FTP 구현은 분리된 연결(Split Connection) 방식을 사용합니다:
-
Control Connection (제어 연결)
- 포트: 사용자 지정 (예: 2121)
- 용도: FTP 명령어 전송 및 응답 수신
- 프로토콜: TCP/IP
-
Data Connection (데이터 연결)
- 포트: 동적으로 생성 (10001~30000 범위의 랜덤 포트)
- 용도: 파일 데이터 및 디렉토리 목록 전송
- 프로토콜: TCP/IP
- 생성 방식: PORT 모드 (클라이언트가 서버에 포트 정보 제공)
서버는 Concurrent Server 방식으로 구현되었습니다:
fork()시스템 콜을 사용하여 각 클라이언트마다 별도의 자식 프로세스 생성- 부모 프로세스는 새로운 연결을 계속 수락
- 자식 프로세스는 해당 클라이언트의 명령을 처리
1. 클라이언트 연결 요청
↓
2. 서버: IP 주소 확인 (access.txt)
├─ 허용된 IP → Welcome 메시지 (220)
└─ 거부된 IP → 접근 거부 메시지 (431) 및 연결 종료
↓
3. 클라이언트: USER [사용자명] 전송
↓
4. 서버: 사용자명 확인 (passwd 파일)
├─ 유효 → 331 Password required
└─ 무효 → 430 Invalid username → 재시도 (최대 3회)
↓
5. 클라이언트: PASS [비밀번호] 전송
↓
6. 서버: 비밀번호 확인 (passwd 파일)
├─ 유효 → 230 User logged in
└─ 무효 → 430 Invalid password → 재시도 (최대 3회)
↓
7. 세션 시작 (fork()로 자식 프로세스 생성)
1. 클라이언트: 사용자 명령 입력 (예: "ls -l")
↓
2. 클라이언트: FTP 명령으로 변환 ("NLST -l")
↓
3. [데이터 연결 필요한 경우]
- 클라이언트: 데이터 소켓 생성 및 대기
- 클라이언트: PORT 명령으로 포트 정보 전송
- 서버: 200 Port command successfully
- 서버: 클라이언트의 데이터 소켓에 연결
↓
4. 클라이언트: 변환된 FTP 명령 전송
↓
5. 서버: 명령 파싱 및 실행
↓
6. 서버: 실행 결과를 데이터 연결로 전송 (또는 제어 연결로 응답)
↓
7. 서버: 완료 메시지 전송 (226 Complete transmission)
↓
8. 클라이언트: 결과 출력 및 데이터 연결 종료
access.txt 파일에 허용할 IP 주소 패턴을 작성합니다.
형식:
- 정확한 IP:
127.0.0.1 - 와일드카드 사용:
128.134.64.*(128.134.64.0~255 허용) - 다중 와일드카드:
203.245.*.*
예시 파일:
127.0.0.1
128.134.64.*
128.134.57.*
128.134.54.171
203.245.*.*
*.*.0.1
passwd 파일을 사용하여 사용자를 인증합니다. 이 파일은 시스템의 /etc/passwd 파일 형식을 따릅니다.
인증 방식:
- 사용자명과 비밀번호를 평문으로 비교 (실제 시스템과 달리 암호화되지 않음)
- 로그인 실패 시 최대 3회까지 재시도 가능
- 3회 실패 시 세션 종료 (530 Failed to log-in)
| 사용자 명령 | FTP 명령 | 설명 |
|---|---|---|
ls |
NLST |
파일 목록 (이름만) |
ls -a |
NLST -a |
숨김 파일 포함 목록 |
ls -l |
NLST -l |
상세 정보 포함 목록 |
ls -al |
NLST -al |
숨김 파일 포함 상세 목록 |
dir |
LIST |
디렉토리 상세 목록 |
pwd |
PWD |
현재 작업 디렉토리 |
cd [경로] |
CWD [경로] |
디렉토리 변경 |
cd .. |
CDUP |
상위 디렉토리로 이동 |
mkdir [이름] |
MKD [이름] |
디렉토리 생성 |
rmdir [이름] |
RMD [이름] |
디렉토리 삭제 |
delete [이름] |
DELE [이름] |
파일 삭제 |
rename [old] [new] |
RNFR [old]RNTO [new] |
파일 이름 변경 (2단계) |
get [파일] |
RETR [파일] |
파일 다운로드 |
put [파일] |
STOR [파일] |
파일 업로드 |
bin |
TYPE I |
바이너리 모드 |
ascii |
TYPE A |
ASCII 모드 |
type binary |
TYPE I |
바이너리 모드 |
type ascii |
TYPE A |
ASCII 모드 |
quit |
QUIT |
세션 종료 |
이 구현에서 사용하는 주요 FTP 응답 코드:
| 코드 | 의미 | 사용 시점 |
|---|---|---|
150 |
데이터 연결 열림 | 파일 전송 또는 목록 전송 시작 전 |
200 |
명령 성공 | PORT 명령 성공 등 |
201 |
타입 설정 성공 | TYPE 명령 성공 |
220 |
서비스 준비됨 | 서버 시작 시 Welcome 메시지 |
226 |
전송 완료 | 파일/목록 전송 완료 |
230 |
로그인 성공 | 인증 성공 |
250 |
파일 작업 성공 | CWD, MKD, RMD, DELE 등 성공 |
257 |
경로명 표시 | PWD 명령 응답 |
331 |
비밀번호 필요 | USER 명령 후 |
350 |
파일 존재, 이름 변경 준비 | RNFR 명령 후 |
430 |
잘못된 사용자명/비밀번호 | 인증 실패 |
431 |
접근 거부 | IP 접근 제어 실패 |
502 |
명령 미구현 또는 오류 | TYPE 명령 오류 등 |
550 |
파일/디렉토리 작업 실패 | 파일 없음, 권한 없음 등 |