如何用Java实现FTP服务器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用Java实现FTP服务器相关的知识,希望对你有一定的参考价值。

FTP(File Transfer Protocol 文件传输协议)是Internet 上用来传送文件的协议。在Internet上通过FTP 服务器可以进行文件的上传(Upload)或下载(Download)。FTP是实时联机服务,在使用它之前必须是具有该服务的一个用户(用户名和口令),工作时客户端必须先登录到作为服务器一方的计算机上,用户登录后可以进行文件搜索和文件传送等有关操作,如改变当前工作目录、列文件目录、设置传输参数及传送文件等。使用FTP可以传送所有类型的文件,如文本文件、二进制可执行文件、图象文件、声音文件和数据压缩文件等。
FTP 命令
FTP 的主要操作都是基于各种命令基础之上的。常用的命令有:
设置传输模式,它包括ASCⅡ(文本) 和BINARY 二进制模式;
目录操作,改变或显示远程计算机的当前目录(cd、dir/ls 命令);
连接操作,open命令用于建立同远程计算机的连接;close命令用于关闭连接;
发送操作,put命令用于传送文件到远程计算机;mput 命令用于传送多个文件到远程计算机;
获取操作,get命令用于接收一个文件;mget命令用于接收多个文件。
?

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
import java.net.Socket;import org.apache.log4j.Logger;/** * 角色——服务器A * @author Leon * */public class ServerA public static void main(String[] args) final String F_DIR = "c:/test";//根路径 final int PORT = 22;//监听端口号 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(); //创建服务线程 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();//随机数 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;//登录状态 final String LOGIN_WARNING = "530 Please log in with USER and PASS first."; String str = "";//命令内容字符串 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+"执行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+"执行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.");
参考技术A 在主函数中,完成服务器端口的侦听和服务线程的创建。我们利用一个静态字符串变量initDir 来保存服务器线程运行时所在的工作目录。服务器的初始工作目录是由程序运行时用户输入的,缺省为C盘的根目录。
具体的代码如下:
public class ftpServer extends Thread
private Socket socketClient;
private int counter;
private static String initDir;
public static void main(String[] args)
if(args.length != 0)
initDir = args[0];
else initDir = "c:";
int i = 1;
try
System.out.println("ftp server started!");
//监听21号端口
ServerSocket s = new ServerSocket(21);
for(;;)
//接受客户端请求
Socket incoming = s.accept();
//创建服务线程
new ftpServer(incoming,i).start();
i++;

catch(Exception e)

在服务器端如何用JNI实现 NavMesh寻路

上一节我们已经讲到的在服务器端使用 Easy3dNav来进行 NavMesh 来做寻路 3D寻路系统NavMesh-服务端篇,但性能上能否更快一点呢?众所周知,C++ 版本的性能向来比JAVA版本要出色,所以我们尝试用JAVA调用JNI native 接口来提升寻路的性能。

如何使用JNI技术来实现,JAVA对C++的调用呢?具体可以参考JNI全流程实例使用总结 这篇文章,本篇文章只提供C++端的实现逻辑。

JAVA端

 /**
     * 加载地图
     *
     * @param navmeshId 寻路数据地图ID
     * @param content   地图文件的路径例
     * @param length    数据长度
     * @return navmeshId, 为0或负数表示加载失败,为正数表示加载成功,后续寻路时传入此id为参数
     */
    public native int load(int navmeshId, byte[] content, int length);

    /**
     * 寻路
     *
     * @param navmeshId 寻路数据地图ID
     * @param start     起始点
     * @param end       结束点
     * @param extents   搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
     * @return 返回路径点列表,注意,查找不到时,会返回空
     */
    public native float[] find(int navmeshId, float[] start, float[] end, float[] extents);

    /**
     * 找到目标点最近的静态可达点
     *
     * @param navmeshId 寻路数据地图ID
     * @param point     参考点
     * @param extents   搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
     * @return 如果目标点可达,返回目标点即可, 如果搜索范围内没有,返回空
     */
    public native float[] findNearest(int navmeshId, float[] point, float[] extents);


    /**
     * 光线照射发,寻找可以支线通过的hit点,如果可通过则返回hit
     *
     * @param navmeshId 寻路数据地图ID
     * @param start     起始点
     * @param end       结束点
     * @param extents   搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
     * @return 返回射线射过去遇到的第一个阻挡点,如果到终点都没有阻挡,返回终点
     */
    public native float[] raycast(int navmeshId, float[] start, float[] end, float[] extents);

C++ 端:
这里只展示寻路的API实现:

/**
 * 寻路
 *
 * @param navmeshId 寻路数据地图ID
 * @param start     起始点
 * @param end       结束点
 * @param extents   搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
 * @return 返回路径点列表,注意,查找不到时,会返回空
 */
JNIEXPORT  jfloatArray JNICALL Java_com_gamioo_ooxx_GamiooJNI_find(JNIEnv* env, jobject jobj, jint id,  jfloatArray start, jfloatArray end, jfloatArray extent)

	jfloatArray ret=  env->NewFloatArray(0);
	float straightPath[MAX_POLYS * 3];
	jfloat* startPos = (jfloat*)env-> GetFloatArrayElements(start, 0);
	jfloat* endPos = (jfloat*)env->GetFloatArrayElements(end, 0);
	jfloat* extents = (jfloat*)env->GetFloatArrayElements(extent, 0);
	if (startPos == nullptr)
	
		return ret;
	
	if (endPos == nullptr)
	
		return ret;
	
	if (extents == nullptr)
	
		return ret;
	

	dtPolyRef startRef = 0;
	dtPolyRef endRef = 0;
	float startNearestPt[3];
	float endNearestPt[3];

	dtQueryFilter filter;
	filter.setIncludeFlags(0xffff);
	filter.setExcludeFlags(0);
	NavMeshContex* navMeshContex =RecastGet(id);
	//cout << navMeshContex->toString()<<endl;
	navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, startNearestPt);
	navMeshContex->navQuery->findNearestPoly(endPos, extents, &filter, &endRef, endNearestPt);
	dtPolyRef polys[MAX_POLYS];
	int npolys;
	unsigned char straightPathFlags[MAX_POLYS];
	dtPolyRef straightPathPolys[MAX_POLYS];
	int nstraightPath = 0;

	navMeshContex->navQuery->findPath(startRef, endRef, startNearestPt, endNearestPt, &filter, polys, &npolys, MAX_POLYS);
	//printf("npolys: %d\\n", npolys);
	if (npolys)
	
		float epos1[3];
		dtVcopy(epos1, endNearestPt);

		if (polys[npolys - 1] != endRef)
		
			navMeshContex->navQuery->closestPointOnPoly(polys[npolys - 1], endNearestPt, epos1, 0);
		
		navMeshContex->navQuery-> findStraightPath(startNearestPt, endNearestPt, polys, npolys, straightPath, straightPathFlags, straightPathPolys, &nstraightPath, MAX_POLYS, 0);
		ret= Util::ConvertFloatStarToJfloatArray(env, straightPath,nstraightPath * 3);
	
	
	env->ReleaseFloatArrayElements(start, startPos, JNI_ABORT);
	env->ReleaseFloatArrayElements(end, endPos, JNI_ABORT);
	env->ReleaseFloatArrayElements(extent, extents, JNI_ABORT);
	return ret;

接下去,我们用单测来做下数据对比:

经过数据轨迹对比(C++和JAVA,以及工具的导出轨迹数据),完全一致!说明API实现没问题。

接着,我们做下性能的对比:
系统配置,WIN 11,64G内存,8核数,16线程数
JAVA版本:

C++版本:


从压测数据对比得出结论,寻路性能提高了2倍多,其他的接口也有一定幅度的提升。
总结:
我们可以对寻路的耗时以及稳定性做监控,进行性能的对比。经过对比,通过对C++版本的导航接口调用,我们一定幅度提升了寻路的性能,并且在寻路的稳定性上也得到了提高。

参考文献:
基于NavMesh寻路、漏斗寻路、RVO动态避障自创的服务器大规模寻路+动态避障算法的实现
从零开始学习导航网格#2 编译RecastNavigation
构建recastnavigation并部署服务端到Linux
接入C++版本recastnavigation寻路库到Unity/服务端中

以上是关于如何用Java实现FTP服务器的主要内容,如果未能解决你的问题,请参考以下文章

如何用java实现ftp客户端程序

如何用c语言实现上传文件

如何用手机登陆ftp服务器

如何用filezilla 访问服务器的ftp

ftp服务器如何用IIS管理器设置成HTTP访问???

如何用安卓手机建立个ftp服务器,让外网手机或电脑连接