前面写了一篇文章是基于IO+多线程的文件上传下载服务器模型,这篇文章是它的后续,基于NIO实现文件的上传下载功能。
服务端的代码:
package com.myftpnio.server;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import com.myftpnio.handler.NioHandler;
import com.myftpnio.handler.ServerHandler;
public class FtpNioServer {
public static final int PORT = 5000;
public static int connum = 0;
public static final int MAX = 5000;
public static final String ROOT = "D:/FtpDir/";
public static void main(String argv[]) {
try {
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
ss.bind(new InetSocketAddress(PORT));
SelectionKey skey = ssc.register(selector, SelectionKey.OP_ACCEPT);
skey.attach(new ServerHandler(ssc, selector));
System.out.println("Start ftp server on " + PORT);
while(!Thread.interrupted()) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
NioHandler handler = (NioHandler)key.attachment();
handler.execute(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String ByteBufferToString(ByteBuffer dst) {
String ret = null;
if (dst != null) {
dst.flip();
byte[] tempb = new byte[dst.limit()];
dst.get(tempb);
ret = new String(tempb);
}
return ret;
}
public static ByteBuffer StringToByteBuffer(String s) {
ByteBuffer other = null;
if (s != null) {
other = ByteBuffer.wrap(s.getBytes());
}
return other;
}
public static int AnalyCmd(String cmd) {
int ret = -1;
if (cmd.toLowerCase().startsWith("upload")) {
ret = 0;
} else if (cmd.toLowerCase().startsWith("download")) {
ret = 1;
} else if (cmd.toLowerCase().equals("ls")) {
ret = 2;
}
return ret;
}
}
package com.myftpnio.handler;
import java.nio.channels.SelectionKey;
public interface NioHandler {
void execute(SelectionKey key);
}
package com.myftpnio.handler;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import com.myftpnio.server.FtpNioServer;
public class ServerHandler implements NioHandler {
private ServerSocketChannel ssc;
private Selector selector;
public ServerHandler(ServerSocketChannel ssc, Selector selector) {
this.ssc = ssc;
this.selector = selector;
}
@Override
public void execute(SelectionKey key) {
// TODO Auto-generated method stub
try {
String cmd = null;
SocketChannel sc = ssc.accept();
//读取客户端的命令,并且给客户端回应消息
ByteBuffer dst = ByteBuffer.allocate(1024);
int ret = sc.read(dst);
if (ret > 0) {
cmd = FtpNioServer.ByteBufferToString(dst);
int n = FtpNioServer.AnalyCmd(cmd);
if (n != -1) {
String s = "ack";
ByteBuffer b = ByteBuffer.wrap(s.getBytes());
sc.write(b);
} else {
String s = "cmd is invalid, please check and try again!!!";
ByteBuffer b = ByteBuffer.wrap(s.getBytes());
sc.write(b);
sc.close();
return;
}
} else {
System.out.println("client no send cmd!!!");
sc.close();
return;
}
//配置客户端Socket为非阻塞
sc.configureBlocking(false);
//new 一个新的客户端对象
ClientHandler h = new ClientHandler(sc, selector);
h.InitClientHandler(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.myftpnio.handler;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import com.myftpnio.server.FtpNioServer;
public class ClientHandler implements NioHandler {
private String cmd;
private Selector selector;
private SocketChannel sc = null;
private FileChannel fileChannel = null;
private ByteBuffer buf = ByteBuffer.allocate(1024);
private long sum = 0;
public ClientHandler(SocketChannel sc, Selector selector) {
this.sc = sc;
this.selector = selector;
FtpNioServer.connum++;
System.out.println(FtpNioServer.connum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " open");
}
public int InitClientHandler(String cmd) {
// 参数判空
if (cmd == null) {
return 1;
}
this.cmd = cmd;
try {
SelectionKey key = null;
int n = FtpNioServer.AnalyCmd(this.cmd);
if (n == 0) {
key = this.sc.register(this.selector, SelectionKey.OP_READ);
key.attach(this);
} else {
key = this.sc.register(this.selector, SelectionKey.OP_WRITE);
key.attach(this);
}
} catch (Exception e) {
e.printStackTrace();
return 1;
}
return 0;
}
@Override
public void execute(SelectionKey key) {
if (key.isReadable()) {
try {
int n = FtpNioServer.AnalyCmd(this.cmd);
switch(n) {
case 0:
proccessUpLoadCmd(key);
break;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
return;
}
if (key.isWritable()) {
try {
int n = FtpNioServer.AnalyCmd(this.cmd);
switch(n) {
case 1:
proccessDownLoadCmd(key);
break;
case 2:
proccessLsCmd(key);
break;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
return;
}
}
private void proccessUpLoadCmd(SelectionKey key) {
String s[] = this.cmd.split(":");
String filepath = FtpNioServer.ROOT + s[1];
try {
int n = sc.read(buf);
if (n >= 0) {
sum += n;
WriteToFile(filepath, buf);
} else {
ReleaseResource(key);
System.out.println(FtpNioServer.connum + " read sum:" + sum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " close");
FtpNioServer.connum--;
return;
}
} catch (IOException e) {
try {
ReleaseResource(key);
FtpNioServer.connum--;
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("IOException " + FtpNioServer.connum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " close");
return;
}
}
private void proccessDownLoadCmd(SelectionKey key) {
String s[] = this.cmd.split(":");
String filepath = FtpNioServer.ROOT + s[1];
try {
int n = ReadFromFile(filepath, buf);
if (n >= 0) {
sum += n;
buf.flip();
sc.write(buf);
} else {
ReleaseResource(key);
System.out.println(FtpNioServer.connum + " send sum:" + sum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " close");
FtpNioServer.connum--;
return;
}
} catch (Exception e) {
try {
ReleaseResource(key);
FtpNioServer.connum--;
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("IOException " + FtpNioServer.connum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " close");
return;
}
}
private void proccessLsCmd(SelectionKey key) throws IOException {
File dir = new File(FtpNioServer.ROOT);
File files[] = dir.listFiles();
String ret = null;
for (File f : files) {
if (ret == null) {
ret = f.getName();
} else {
ret += ";";
ret += f.getName();
}
}
ByteBuffer src = null;
if (ret != null) {
src = ByteBuffer.wrap(ret.getBytes());
} else {
String error = "cmd execute fail!!!";
src = ByteBuffer.wrap(error.getBytes());
}
// write data to client socket channel
this.sc.write(src);
ReleaseResource(key);
System.out.println(FtpNioServer.connum + " Client:" + sc.socket().getRemoteSocketAddress().toString() + " close");
FtpNioServer.connum--;
}
private void WriteToFile(String path, ByteBuffer buf) throws IOException {
if (fileChannel == null) {
fileChannel = new RandomAccessFile(path, "rw").getChannel();
}
buf.flip();
fileChannel.write(buf);
buf.clear();
}
@SuppressWarnings("resource")
private int ReadFromFile(String path, ByteBuffer buf) throws IOException {
if (fileChannel == null) {
fileChannel = new RandomAccessFile(path, "r").getChannel();
}
buf.clear();
return fileChannel.read(buf);
}
private void ReleaseResource(SelectionKey key) throws IOException {
sc.close();
key.cancel();
if (fileChannel != null) {
fileChannel.close();
}
}
}