1:位元組流結構:
每次發送12+1024個位元組
其中,前七個位元組位元組表示命令
第七到第十二個位元組表示後面1024位元組中的有效位元組的長度
例如一幀中前12個位元組的內容為"UPFILEN00012"表示要上傳檔案名稱,檔案名稱的長度是12。那麼程式就在後面的1024個位元組中去12個位元組,在把它轉換為字串,作為要上傳的檔案名稱。
2:命令結構
Server端:
DISCONN:中斷連線
LSFILES:發送目前的目錄檔案清單
ENDFILE:上傳一個檔案的結束標記
UPFILEN:表示要上傳一個新的檔案,並且此包中包含了檔案名稱
UPDATAS:表示本包是要上傳的資料
DNFILEN:表示要下載的檔案名稱,伺服器要執行向用戶端傳輸檔案的操作
Client端:
DISCONN:中斷連線
LSFILES:接收伺服器目前的目錄檔案清單
ENDFILE:下載一個檔案的結束標記
DNDATAS:表示本包是要下載的資料
3:檔案結構
FtpServer:ftp軟體的伺服器端,目前在連接埠2121監聽,支援多線程,檔案的上傳,下載,列表。
FtpClient:ftp軟體的用戶端,預設串連原生伺服器端,在連接埠2121,支援檔案的上傳,下載,列表。
FtpClientUI:ftp軟體的用戶端的使用者介面,完全採用Swing技術,手工編寫,沒有用JB自動產生。
PublicFunc:提供一些公用的靜態方法例如將給定的String對象分裝成要發送的幀。將數字格式化成長度為五位的String類型對象。
package cn.edu.bit.software.ftptrans;
import java.io.*;
import java.net.*;
import java.util.Vector;
import java.util.logging.*;
public class FtpServer
{
//用戶端socket對象
private ServerSocket m_servSocket;
//ftp伺服器的連接埠號碼
private int SERVER_PORT;
//ftp伺服器所允許的最大串連數
private int MAX_CONN;
//連入的用戶端處理對象管理器
private Vector vecClient;
//設定一個log日誌對象
private Logger mylog;
private ConsoleHandler handler;
String strServHome;
public FtpServer(int servPort, int maxConn)
{
SERVER_PORT = servPort;
MAX_CONN = maxConn;
strServHome = "c://";
vecClient = new Vector();
/*------------初始化log------------*/
try
{
handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
mylog = Logger.getLogger("FtpServer");
mylog.addHandler(handler);
mylog.setLevel(Level.ALL);
}
catch (SecurityException e)
{
mylog.warning("在設定程式記錄層級時出現異常");
mylog.warning(e.getMessage());
}
/*--------------初始化伺服器,連接埠2121----------------------*/
try
{
m_servSocket = new ServerSocket(SERVER_PORT, MAX_CONN);
while (true)
{
mylog.finest("FtpServer開始在連接埠2121監聽");
Socket clientSocket = m_servSocket.accept();
vecClient.add(clientSocket);
mylog.info("#" + vecClient.size() + "號用戶端連入");
new TransHandler(this, clientSocket, vecClient.size()).start();
}
}
catch (IOException e)
{
mylog.warning("在初始化FtpServ時出現錯誤");
mylog.warning(e.getMessage());
}
}
public void deleteClient(TransHandler handler)
{
try
{
vecClient.remove(handler);
vecClient.setSize(vecClient.size() - 1);
mylog.info("第#" + handler.iClientNum + "號用戶端斷開了與伺服器的串連!");
}
catch (Exception e)
{
mylog.warning("在刪除第#" + handler.iClientNum + "號用戶端時出現異常");
mylog.warning(e.getMessage());
}
}
public static void main(String[] args)
{
new FtpServer(2121, 50);
}
}
/**
* 當有用戶端連入時,處理用戶端請求的類
* @author findfrog
* @version 1.0
*/
class TransHandler extends Thread
{
//伺服器控制代碼,用於最後銷毀TransHandler對象時用
FtpServer main = null;
//用戶端的socket
private Socket m_clientSocket = null;
//日誌對象
private Logger mylog;
//要上傳的檔案路徑
private String strUpFilePath = null;
//要下載的檔案路徑
private String strDnFilePath = null;
//本用戶端在的序號
int iClientNum = -1;
//緩衝位元組資料的大小
private int ibufferlength;
//緩衝位元組數組
byte[] inputBytes;
//從用戶端傳來的指令
String strClientOrder;
//用於得到從socket端的輸入資訊
InputStream m_inputStream;
//用於向socket輸出的輸出資料流
OutputStream m_outputStream;
//用於上傳檔案的輸出資料流
FileOutputStream m_fileOutputStream;
//用於下載檔案的輸入資料流
FileInputStream m_fileInputStream;
//建構函式
public TransHandler(FtpServer fserver, Socket s, int iNum)
{
try
{
main = fserver;
//將用戶端socket控制代碼付給本機物件
m_clientSocket = s;
//初始化log對象
mylog = Logger.getLogger("TransHandler");
//初始化本用戶端序號
iClientNum = iNum;
//用於得到從socket端的輸入資訊
m_inputStream = m_clientSocket.getInputStream();
m_outputStream = m_clientSocket.getOutputStream();
ibufferlength = 1024;
inputBytes = new byte[ibufferlength + 12];
}
catch (Exception e)
{
mylog.warning("在初始化TransHandler時發生異常!");
mylog.warning(e.getMessage());
}
}
public void run()
{
try
{
int ilength;
while ( (ilength = m_inputStream.read(inputBytes, 0, 12 + ibufferlength)) !=
-1)
{
strClientOrder = new String(inputBytes, 0, 7);
if (strClientOrder.equals("DISCONN"))
{ //中斷連線
mylog.info("得到了DISCONN");
exit();
}
else if (strClientOrder.equals("LSFILES"))
{ //發送目前的目錄檔案清單
mylog.info("伺服器端接收到了LSFILES命令");
File flHome = new File(main.strServHome);
String[] strFileNames = flHome.list();
strFileNames = AdjustStrings(strFileNames);
for (int i = 0; i < strFileNames.length; i++)
{
String strFileNameLength = PublicFunc.formatLength(strFileNames[i].
getBytes().length);
byte[] fileNameBytes = strFileNames[i].getBytes();
byte[] outBytes = PublicFunc.makepackage("LSFILES",
strFileNameLength, fileNameBytes);
m_outputStream.write(outBytes, 0, outBytes.length);
m_outputStream.flush();
}
}
else if (strClientOrder.equals("ENDFILE"))
{ //上傳一個檔案的結束標記
mylog.info("收到檔案結束標誌符號");
m_fileOutputStream.close();
}
else if (strClientOrder.equals("UPFILEN"))
{ //表示要上傳一個新的檔案,並且此包中包含了檔案名稱
int iFileNameLength = Integer.parseInt(new String(inputBytes, 7, 5));
mylog.info("要上傳的檔案名稱的長度為" + iFileNameLength);
String strFileName = new String(inputBytes, 12, iFileNameLength);
mylog.info("要上傳的檔案名稱是:" + strFileName);
//初始化上傳檔案路徑
strUpFilePath = main.strServHome + strFileName;
File upFile = new File(strUpFilePath);
m_fileOutputStream = new FileOutputStream(upFile);
}
else if (strClientOrder.equals("UPDATAS"))
{ //表示本包是要上傳的資料
//本次資料包的長度
mylog.info("正在接收檔案...");
int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
m_fileOutputStream.write(inputBytes, 12, iDataLength);
m_fileOutputStream.flush();
}
else if (strClientOrder.equals("DNFILEN"))
{ //表示要下載的檔案名稱,伺服器要執行向用戶端傳輸檔案的操作
int iFileNameLength = Integer.parseInt(new String(inputBytes, 7, 5));
mylog.info("要下載的檔案名稱的長度為" + iFileNameLength);
String strFileName = new String(inputBytes, 12, iFileNameLength);
mylog.info("要下載的檔案名稱是:" + strFileName);
//初始化上傳檔案路徑
strDnFilePath = main.strServHome + strFileName;
File dnFile = new File(strDnFilePath);
//初始化了檔案輸出資料流
m_fileInputStream = new FileInputStream(dnFile);
//開始向用戶端傳輸檔案
mylog.info("開始向用戶端傳輸檔案" + strFileName + "...");
int iInputLength = 0;
String strInputLength;
byte[] readBytes = new byte[ibufferlength];
while ( (iInputLength = m_fileInputStream.read(readBytes, 0,
ibufferlength)) !=
-1)
{
strInputLength = PublicFunc.formatLength(iInputLength);
byte[] outBytes = PublicFunc.makepackage("DNDATAS", strInputLength,
readBytes);
m_outputStream.write(outBytes, 0, outBytes.length);
m_outputStream.flush();
}
//最後發送一個檔案結束標記
m_outputStream.write(PublicFunc.makepackage("ENDFILE", "00001",
new byte[1]));
m_outputStream.flush();
}
}
}
catch (Exception e)
{
mylog.warning(e.getMessage());
}
}
public void exit()
{
try
{
m_outputStream.write(PublicFunc.makepackage("DISCONN", "00001",
new byte[1]));
m_inputStream.close();
m_outputStream.close();
main.deleteClient(this);
main = null;
}
catch (Exception e)
{
mylog.warning("在斷開用戶端#" + this.iClientNum + "串連時出現異常!");
mylog.warning(e.getMessage());
}
}
public String[] AdjustStrings(String[] strFileNames)
{
String[] strItemNames = new String[strFileNames.length + 1];
strItemNames[0] = "返回上一級";
int j = 1;
for (int i = 0; i < strFileNames.length; i++)
{
File upFile = new File(main.strServHome + strFileNames[i]);
if (!upFile.isFile())
{
strItemNames[j++] = "[檔案夾]" + strFileNames[i];
}
}
for (int i = 0; i < strFileNames.length; i++)
{
File upFile = new File(main.strServHome + strFileNames[i]);
if (upFile.isFile())
{
strItemNames[j++] = strFileNames[i];
}
}
return strItemNames;
}
}
package cn.edu.bit.software.ftptrans;
import java.io.*;
import java.net.*;
import java.util.logging.*;
import java.util.Vector;
public class FtpClient extends Thread
{
Logger mylog = Logger.getLogger("FtpClient");
Socket m_clientSocket;
//緩衝位元組資料的大小
private int ibufferlength;
//緩衝位元組數組
byte[] inputBytes;
Vector vecServFiles;
//用於得到從socket端的輸入資訊
InputStream m_inputStream;
//用於向socket輸出的輸出資料流
OutputStream m_outputStream;
//向本地寫檔案的檔案輸出資料流
FileOutputStream m_fileOutputStream;
//從本地讀檔案的檔案輸入資料流
FileInputStream m_fileInputStream;
//從伺服器端傳來的指令
String strServerOrder;
//主機的ip地址
String strServerIP;
//伺服器的連接埠號碼
int iServerPort;
public FtpClient(String strServIP, int iServPort)
{
strServerIP = strServIP;
iServerPort = iServPort;
}
public void run()
{
try
{
//建立串連
m_clientSocket = new Socket(strServerIP, iServerPort);
mylog.info("已經連到了主機" + strServerIP + "在連接埠" + iServerPort);
m_inputStream = m_clientSocket.getInputStream();
m_outputStream = m_clientSocket.getOutputStream();
mylog.fine("用戶端得到了socket的輸入輸出資料流!");
ibufferlength = 1024;
inputBytes = new byte[ibufferlength + 12];
vecServFiles = new Vector();
doBusiness();
}
catch (UnknownHostException e)
{
mylog.warning("伺服器位址未知");
mylog.warning(e.getMessage());
}
catch (IOException e)
{
mylog.warning(e.getMessage());
}
catch (Exception e)
{
mylog.warning(e.getMessage());
}
}
public void doBusiness()
{
try
{
int iLength = 0;
while ( (iLength = m_inputStream.read(inputBytes, 0, ibufferlength + 12)) !=
-1)
{
strServerOrder = new String(inputBytes, 0, 7);
if (strServerOrder.equals("DISCONN"))
{ //中斷連線
mylog.info("在client端得到了DISCONN");
int length = Integer.parseInt(new String(inputBytes, 7, 5));
mylog.info("長度是" + length);
}
else if (strServerOrder.equals("LSFILES"))
{ //接收伺服器目前的目錄檔案清單
int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
mylog.info("在用戶端這個檔案名稱的長度是:" + iDataLength);
String strFileName = new String(inputBytes, 12, iDataLength);
mylog.info("用戶端正在擷取伺服器目錄資訊....." + strFileName);
vecServFiles.add(strFileName);
}
else if (strServerOrder.equals("ENDFILE"))
{ //下載一個檔案的結束標記
mylog.info("收到下載檔案結束標誌符號");
m_fileOutputStream.close();
}
else if (strServerOrder.equals("DNDATAS"))
{ //表示本包是要下載的資料
int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
m_fileOutputStream.write(inputBytes, 12, iDataLength);
m_fileOutputStream.flush();
}
}
}
catch (Exception e)
{
}
}
/**
* 用戶端上傳檔案名稱
* @param strFileName 要上傳檔案的檔案名稱
*/
public void upFileName(String strFileName)
{
try
{
String strLength = PublicFunc.formatLength(strFileName.getBytes().length);
byte[] outBytes = PublicFunc.makepackage("UPFILEN", strLength,
strFileName.getBytes());
m_outputStream.write(outBytes, 0, outBytes.length);
m_outputStream.flush();
}
catch (Exception e)
{
mylog.warning("在用戶端在向伺服器寫要上傳的檔案名稱時發生異常");
mylog.warning(e.getMessage());
}
}
/**
* 講本地檔案strFilePath上傳到伺服器
* @param strFilePath 本地檔案路徑
*/
public void upFileData(String strFilePath)
{
try
{
File file = new File(strFilePath);
m_fileInputStream = new FileInputStream(file);
int iInputLength = 0;
String strInputLength;
byte[] readBytes = new byte[ibufferlength];
while ( (iInputLength = m_fileInputStream.read(readBytes, 0,
ibufferlength)) !=
-1)
{
strInputLength = PublicFunc.formatLength(iInputLength);
byte[] outBytes = PublicFunc.makepackage("UPDATAS", strInputLength,
readBytes);
m_outputStream.write(outBytes, 0, outBytes.length);
m_outputStream.flush();
}
//最後發送一個檔案結束標記
m_outputStream.write(PublicFunc.makepackage("ENDFILE", "00001",
new byte[1]));
m_outputStream.flush();
}
catch (Exception e)
{
mylog.warning("從用戶端向伺服器傳輸檔案內容是發生異常");
mylog.warning(e.getMessage());
}
}