Java實現(xiàn)FTP服務(wù)器功能實例代碼
FTP(File Transfer Protocol 文件傳輸協(xié)議)是Internet 上用來傳送文件的協(xié)議。在Internet上通過FTP 服務(wù)器可以進(jìn)行文件的上傳(Upload)或下載(Download)。FTP是實時聯(lián)機(jī)服務(wù),在使用它之前必須是具有該服務(wù)的一個用戶(用戶名和口令),工作時客戶端必須先登錄到作為服務(wù)器一方的計算機(jī)上,用戶登錄后可以進(jìn)行文件搜索和文件傳送等有關(guān)操作,如改變當(dāng)前工作目錄、列文件目錄、設(shè)置傳輸參數(shù)及傳送文件等。使用FTP可以傳送所有類型的文件,如文本文件、二進(jìn)制可執(zhí)行文件、圖象文件、聲音文件和數(shù)據(jù)壓縮文件等。
FTP 命令
FTP 的主要操作都是基于各種命令基礎(chǔ)之上的。常用的命令有:
設(shè)置傳輸模式,它包括ASCⅡ(文本) 和BINARY 二進(jìn)制模式;
目錄操作,改變或顯示遠(yuǎn)程計算機(jī)的當(dāng)前目錄(cd、dir/ls 命令);
連接操作,open命令用于建立同遠(yuǎn)程計算機(jī)的連接;close命令用于關(guān)閉連接;
發(fā)送操作,put命令用于傳送文件到遠(yuǎn)程計算機(jī);mput 命令用于傳送多個文件到遠(yuǎn)程計算機(jī);
獲取操作,get命令用于接收一個文件;mget命令用于接收多個文件。
import java.net.Socket;
import org.apache.log4j.Logger;
/**
* 角色——服務(wù)器A
* @author Leon
*
*/
public class ServerA{
public static void main(String[] args){
final String F_DIR = "c:/test";//根路徑
final int PORT = 22;//監(jiān)聽端口號
Logger.getRootLogger();
Logger logger = Logger.getLogger("com");
try{
ServerSocket s = new ServerSocket(PORT);
logger.info("Connecting to server A...");
logger.info("Connected Successful! Local Port:"+s.getLocalPort()+". Default Directory:'"+F_DIR+"'.");
while( true ){
//接受客戶端請求
Socket client = s.accept();
//創(chuàng)建服務(wù)線程
new ClientThread(client, F_DIR).start();
}
} catch(Exception e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.Random;
import org.apache.log4j.Logger;
/**
* 客戶端子線程類
* @author Leon
*
*/
public class ClientThread extends Thread {
private Socket socketClient;//客戶端socket
private Logger logger;//日志對象
private String dir;//絕對路徑
private String pdir = "/";//相對路徑
private final static Random generator = new Random();//隨機(jī)數(shù)
public ClientThread(Socket client, String F_DIR){
this.socketClient = client;
this.dir = F_DIR;
}
@Override
public void run() {
Logger.getRootLogger();
logger = Logger.getLogger("com");
InputStream is = null;
OutputStream os = null;
try {
is = socketClient.getInputStream();
os = socketClient.getOutputStream();
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
BufferedReader br = new BufferedReader(new InputStreamReader(is,
Charset.forName("UTF-8")));
PrintWriter pw = new PrintWriter(os);
String clientIp = socketClient.getInetAddress().toString().substring(1);//記錄客戶端IP
String username = "not logged in";//用戶名
String password = "";//口令
String command = "";//命令
boolean loginStuts = false;//登錄狀態(tài)
final String LOGIN_WARNING = "530 Please log in with USER and PASS first.";
String str = "";//命令內(nèi)容字符串
int port_high = 0;
int port_low = 0;
String retr_ip = "";//接收文件的IP地址
Socket tempsocket = null;
//打印歡迎信息
pw.println("220-FTP Server A version 1.0 written by Leon Guo");
pw.flush();
logger.info("("+username+") ("+clientIp+")> Connected, sending welcome message...");
logger.info("("+username+") ("+clientIp+")> 220-FTP Server A version 1.0 written by Leon Guo");
boolean b = true;
while ( b ){
try {
//獲取用戶輸入的命令
command = br.readLine();
if(null == command) break;
} catch (IOException e) {
pw.println("331 Failed to get command");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 331 Failed to get command");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
b = false;
}
/*
* 訪問控制命令
*/
// USER命令
if(command.toUpperCase().startsWith("USER")){
logger.info("(not logged in) ("+clientIp+")> "+command);
username = command.substring(4).trim();
if("".equals(username)){
pw.println("501 Syntax error");
pw.flush();
logger.info("(not logged in) ("+clientIp+")> 501 Syntax error");
username = "not logged in";
}
else{
pw.println("331 Password required for " + username);
pw.flush();
logger.info("(not logged in) ("+clientIp+")> 331 Password required for " + username);
}
loginStuts = false;
} //end USER
// PASS命令
else if(command.toUpperCase().startsWith("PASS")){
logger.info("(not logged in) ("+clientIp+")> "+command);
password = command.substring(4).trim();
if(username.equals("root") && password.equals("root")){
pw.println("230 Logged on");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 230 Logged on");
// logger.info("客戶端 "+clientIp+" 通過 "+username+"用戶登錄");
loginStuts = true;
}
else{
pw.println("530 Login or password incorrect!");
pw.flush();
logger.info("(not logged in) ("+clientIp+")> 530 Login or password incorrect!");
username = "not logged in";
}
} //end PASS
// PWD命令
else if(command.toUpperCase().startsWith("PWD")){
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行PWD命令");
pw.println("257 /""+pdir+"/" is current directory");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 257 /""+pdir+"/" is current directory");
}
else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end PWD
// CWD命令
else if(command.toUpperCase().startsWith("CWD")){
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
str = command.substring(3).trim();
if("".equals(str)){
pw.println("250 Broken client detected, missing argument to CWD. /""+pdir+"/" is current directory.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 250 Broken client detected, missing argument to CWD. /""+pdir+"/" is current directory.");
}
else{
//判斷目錄是否存在
String tmpDir = dir + "/" + str;
File file = new File(tmpDir);
if(file.exists()){//目錄存在
dir = dir + "/" + str;
if("/".equals(pdir)){
pdir = pdir + str;
}
else{
pdir = pdir + "/" + str;
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行CWD命令");
pw.println("250 CWD successful. /""+pdir+"/" is current directory");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 250 CWD successful. /""+pdir+"/" is current directory");
}
else{//目錄不存在
pw.println("550 CWD failed. /""+pdir+"/": directory not found.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 550 CWD failed. /""+pdir+"/": directory not found.");
}
}
}
else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end CWD
// QUIT命令
else if(command.toUpperCase().startsWith("QUIT")){
logger.info("("+username+") ("+clientIp+")> "+command);
b = false;
pw.println("221 Goodbye");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 221 Goodbye");
try {
Thread.currentThread();
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
} //end QUIT
/*
* 傳輸參數(shù)命令
*/
//PORT命令,主動模式傳輸數(shù)據(jù)
else if(command.toUpperCase().startsWith("PORT")){
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
try {
str = command.substring(4).trim();
port_low = Integer.parseInt(str.substring(str.lastIndexOf(",")+1));
port_high = Integer.parseInt(str.substring(0, str.lastIndexOf(","))
.substring(str.substring(0, str.lastIndexOf(",")).lastIndexOf(",")+1));
String str1 = str.substring(0, str.substring(0, str.lastIndexOf(",")).lastIndexOf(","));
retr_ip = str1.replace(",", ".");
try {
//實例化主動模式下的socket
tempsocket = new Socket(retr_ip,port_high * 256 + port_low);
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行PORT命令");
pw.println("200 port command successful");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 200 port command successful");
} catch (ConnectException ce) {
pw.println("425 Can't open data connection.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 425 Can't open data connection.");
logger.error(ce.getMessage());
for(StackTraceElement ste : ce.getStackTrace()){
logger.error(ste.toString());
}
} catch (UnknownHostException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
} catch (NumberFormatException e) {
pw.println("503 Bad sequence of commands.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands.");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end PORT
//PASV命令,被動模式傳輸數(shù)據(jù)
else if(command.toUpperCase().startsWith("PASV")) {
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
ServerSocket ss = null;
while( true ){
//獲取服務(wù)器空閑端口
port_high = 1 + generator.nextInt(20);
port_low = 100 + generator.nextInt(1000);
try {
//服務(wù)器綁定端口
ss = new ServerSocket(port_high * 256 + port_low);
break;
} catch (IOException e) {
continue;
}
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行PASV命令");
InetAddress i = null;
try {
i = InetAddress.getLocalHost();
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
pw.println("227 Entering Passive Mode ("+i.getHostAddress().replace(".", ",")+","+port_high+","+port_low+")");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 227 Entering Passive Mode ("+i.getHostAddress().replace(".", ",")+","+port_high+","+port_low+")");
try {
//被動模式下的socket
tempsocket = ss.accept();
ss.close();
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end PASV
//RETR命令
else if(command.toUpperCase().startsWith("RETR")){
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
str = command.substring(4).trim();
if("".equals(str)){
pw.println("501 Syntax error");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 501 Syntax error");
}
else {
try {
pw.println("150 Opening data channel for file transfer.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for file transfer.");
RandomAccessFile outfile = null;
OutputStream outsocket = null;
try {
//創(chuàng)建從中讀取和向其中寫入(可選)的隨機(jī)訪問文件流,該文件具有指定名稱
outfile = new RandomAccessFile(dir+"/"+str,"r");
outsocket = tempsocket.getOutputStream();
} catch (FileNotFoundException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
byte bytebuffer[]= new byte[1024];
int length;
try{
while((length = outfile.read(bytebuffer)) != -1){
outsocket.write(bytebuffer, 0, length);
}
outsocket.close();
outfile.close();
tempsocket.close();
}
catch(IOException e){
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行RETR命令");
pw.println("226 Transfer OK");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 226 Transfer OK");
} catch (Exception e){
pw.println("503 Bad sequence of commands.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands.");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
}
else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
}//end RETR
//STOR命令
else if(command.toUpperCase().startsWith("STOR")){
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
str = command.substring(4).trim();
if("".equals(str)){
pw.println("501 Syntax error");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 501 Syntax error");
}
else {
try {
pw.println("150 Opening data channel for file transfer.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for file transfer.");
RandomAccessFile infile = null;
InputStream insocket = null;
try {
infile = new RandomAccessFile(dir+"/"+str,"rw");
insocket = tempsocket.getInputStream();
} catch (FileNotFoundException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
byte bytebuffer[] = new byte[1024];
int length;
try{
while((length =insocket.read(bytebuffer) )!= -1){
infile.write(bytebuffer, 0, length);
}
insocket.close();
infile.close();
tempsocket.close();
}
catch(IOException e){
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行STOR命令");
pw.println("226 Transfer OK");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 226 Transfer OK");
} catch (Exception e){
pw.println("503 Bad sequence of commands.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands.");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
} else {
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end STOR
//NLST命令
else if(command.toUpperCase().startsWith("NLST")) {
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
try {
pw.println("150 Opening data channel for directory list.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for directory list.");
PrintWriter pwr = null;
try {
pwr= new PrintWriter(tempsocket.getOutputStream(),true);
} catch (IOException e1) {
e1.printStackTrace();
}
File file = new File(dir);
String[] dirstructure = new String[10];
dirstructure= file.list();
for(int i=0;i<dirstructure.length;i++){
pwr.println(dirstructure[i]);
}
try {
tempsocket.close();
pwr.close();
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行NLST命令");
pw.println("226 Transfer OK");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 226 Transfer OK");
} catch (Exception e){
pw.println("503 Bad sequence of commands.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands.");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}else{
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end NLST
//LIST命令
else if(command.toUpperCase().startsWith("LIST")) {
logger.info("("+username+") ("+clientIp+")> "+command);
if(loginStuts){
try{
pw.println("150 Opening data channel for directory list.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for directory list.");
PrintWriter pwr = null;
try {
pwr= new PrintWriter(tempsocket.getOutputStream(),true);
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
FtpUtil.getDetailList(pwr, dir);
try {
tempsocket.close();
pwr.close();
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
// logger.info("用戶"+clientIp+":"+username+"執(zhí)行LIST命令");
pw.println("226 Transfer OK");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 226 Transfer OK");
} catch (Exception e){
pw.println("503 Bad sequence of commands.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands.");
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
} else {
pw.println(LOGIN_WARNING);
pw.flush();
logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING);
}
} //end LIST
// 輸入非法命令
else{
logger.info("("+username+") ("+clientIp+")> "+command);
pw.println("500 Syntax error, command unrecognized.");
pw.flush();
logger.info("("+username+") ("+clientIp+")> 500 Syntax error, command unrecognized.");
}
} //end while
try {
logger.info("("+username+") ("+clientIp+")> disconnected.");
// logger.info("用戶"+clientIp+":"+username+"退出");
br.close();
socketClient.close();
pw.close();
if(null != tempsocket){
tempsocket.close();
}
} catch (IOException e) {
logger.error(e.getMessage());
for(StackTraceElement ste : e.getStackTrace()){
logger.error(ste.toString());
}
}
}
}
import java.io.File;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* FTP工具類
* @author Leon
*
*/
public class FtpUtil {
public static void getDetailList(PrintWriter pw, String path){
File dir = new File(path);
if (!dir.isDirectory()) {
pw.println("500 No such file or directory./r/n");
}
File[] files = dir.listFiles();
String modifyDate;
for (int i = 0; i < files.length; i++) {
modifyDate = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss")
.format(new Date(files[i].lastModified()));
if (files[i].isDirectory()) {
pw.println("drwxr-xr-x ftp ftp 0 "
+ modifyDate + " " + files[i].getName());
} else {
pw.println("-rw-r-r--1 ftp ftp "
+ files[i].length() + " " + modifyDate + " "
+ files[i].getName());
}
pw.flush();
}
pw.println("total:" + files.length);
}
}
### set log levels ###
log4j.logger.com =debug,stdout,D,E
### 輸出到控制臺 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
## 輸出DEBUG級別以上的日志
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=c:/logs/logs.log
log4j.appender.D.Append =true
## 輸出DEBUG級別以上的日志
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存異常信息到單獨文件 ###
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
## 異常日志文件名
log4j.appender.E.File=c:/logs/errors.log
log4j.appender.E.Append=true
## 只輸出ERROR級別以上的日志!!!
log4j.appender.E.Threshold=ERROR
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
以上內(nèi)容是小編給大家介紹的Java實現(xiàn)FTP服務(wù)器功能實例代碼的相關(guān)知識,希望大家喜歡。
相關(guān)文章
SpringBoot中@Autowired爆紅原理分析及解決
這篇文章主要介紹了SpringBoot中@Autowired爆紅原理分析及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
IDEA在SpringBoot項目使用Maven打包后jar包太小問題及解決
這篇文章主要介紹了IDEA在SpringBoot項目使用Maven打包后jar包太小問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04
idea2019.1.4 鼠標(biāo)放到方法上顯示注解的實現(xiàn)操作
這篇文章主要介紹了idea2019.1.4 鼠標(biāo)放到方法上顯示注解的實現(xiàn)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
Java設(shè)計模式編程中的工廠方法模式和抽象工廠模式
這篇文章主要介紹了Java設(shè)計模式編程中的工廠方法模式和抽象工廠模式,設(shè)計模式的建立有利于團(tuán)隊協(xié)作時代碼的共同維護(hù),需要的朋友可以參考下2016-01-01
java中實體類實現(xiàn)時間日期自動轉(zhuǎn)換方式
這篇文章主要介紹了java中實體類實現(xiàn)時間日期自動轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06

