Каждый раз, когда сервер передает файл, в заголовке указывается дескриптор файла и конечный индекс. Если файл разделен двоеточиями, вывод сервера в порядке, но когда дело доходит до клиента, он становится беспорядком。
Длина кода такая длинная, но простая.
сервер:
package niotest;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
public class NIOServerTest {
public static void main(String[] args)throws IOException {
new Server(new InetSocketAddress(1235)).start();
}
}
class Server{
private static final int BUFF_SIZE_RE=1024;
private static final int BUFF_SIZE_SE=1024*1024;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
private InetSocketAddress port;
private boolean ing;
private String rootPath;
Server(InetSocketAddress port){
this.port=port;
}
void start()throws IOException {
this.serverSocketChannel=ServerSocketChannel.open();
this.serverSocketChannel.configureBlocking(false);
this.serverSocketChannel.socket().bind(this.port);
this.ing=true;
this.selector=Selector.open();
this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
new Thread(new Listener()).start();
this.rootPath="F:\\xiazai\\";
System.out.println("server starting……");
}
private void stop(){
this.ing=false;
}
class Listener implements Runnable{
@Override
public void run(){
while(Server.this.ing){
try {
selector.select();
Iterator<SelectionKey> keys= selector.selectedKeys().iterator();
while(keys.hasNext()){
SelectionKey key=keys.next();
keys.remove();
if(key.isValid()){
if(key.isAcceptable()){
doCon(key);
}else if(key.isReadable()){
handleRead(key);
}
}
}
}catch (IOException ioe){
ioe.printStackTrace();
}
}
try{
selector.close();
serverSocketChannel.close();
}catch (IOException IOE){
IOE.printStackTrace();
}
}
void doCon(SelectionKey key)throws IOException{
SocketChannel sc=((ServerSocketChannel)(key.channel())).accept(); //
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
}
void handleRead(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUFF_SIZE_RE);
try {
int len = sc.read(buffer);
if (len > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String meg = new String(bytes);
System.out.println("Request FileName: "+meg);
buffer.clear();
if (meg.equals("end;")) {
key.cancel();
key.channel().close();
stop();
}else {
new Thread(new FileHandler(Server.this.rootPath + meg, sc)).start();
}
} else if (len < 0) {
key.cancel();
sc.close();
}
}catch (IOException IOE){
IOE.printStackTrace();
key.cancel();
}
}
class FileHandler implements Runnable{
private String fileName;
private SocketChannel out;
FileHandler(String fileName,SocketChannel out){
this.fileName=fileName;
this.out=out;
}
@Override
public void run(){
try {
handleTransfer();
}catch (IOException | InterruptedException ioe){
try {
out.close();
}catch (IOException ie){
ie.printStackTrace();
}
ioe.printStackTrace();
}
}
private void handleTransfer()throws IOException,InterruptedException{
ByteBuffer littleBuffer=ByteBuffer.allocate(BUFF_SIZE_RE);
File file=new File(this.fileName);
if(file.exists()&&file.isFile()) try
// (
// FileChannel in = FileChannel.open(Paths.get(this.fileName), StandardOpenOption.READ)
// )
{
FileChannel in = FileChannel.open(Paths.get(this.fileName), StandardOpenOption.READ);
long len = in.size();
if (in.size() < 1024 * 1024 * 50) {
this.fileName = this.fileName.replace("../", "");
String[] temp = this.fileName.split("\\\\");
int fd = (int) (Math.random() * 100);
String saveName = fd + temp[temp.length - 1];
System.out.println("The size of file " + saveName + " is " + len + " , fd is " + fd);
littleBuffer.put(("Y;" + fd + ";" + len + ";" + saveName + ";").getBytes());
littleBuffer.flip();
this.out.write(littleBuffer);
littleBuffer.clear();
Thread.sleep(3000);
littleBuffer.clear();
transfer(in, littleBuffer, fd);
in.close();
}else {
System.out.println("The file " + fileName + " too big!");
littleBuffer.put(("N;" + fileName+";too big!").getBytes());
littleBuffer.flip();
this.out.write(littleBuffer);
}
}catch(IOException ioe){
ioe.printStackTrace();
}
else {
System.out.println("The file " + fileName + " is doesn't exist or is a Directory!");
littleBuffer.put(("N;" + fileName+";is doesn't exist or is a Directory!").getBytes());
littleBuffer.flip();
this.out.write(littleBuffer);
}
}
private void transfer(FileChannel in,ByteBuffer littleBuffer,int fd) throws IOException{
final int HEAD_SIZE=(BUFF_SIZE_RE+"").length();
ByteBuffer buffer=ByteBuffer.allocate(BUFF_SIZE_SE);
byte[] temp=new byte[BUFF_SIZE_RE];
while ((in.read(buffer)) != -1){
buffer.flip();
while(buffer.position()<buffer.limit()){
int len=buffer.limit()-buffer.position();
int tempRead;
String dataHead;
if(len>(BUFF_SIZE_RE-HEAD_SIZE-3)) {
dataHead=fd+":"+BUFF_SIZE_RE+":";
tempRead =BUFF_SIZE_RE-dataHead.length();
}
else {
int fdLen=fd>9?2:1;
int lenLen=(len+":").length();
int allLen=fdLen+1+lenLen+len;
dataHead=fd+":"+allLen+":";
tempRead =len;
}
littleBuffer.put(dataHead.getBytes(Charset.forName("UTF-8")));
System.out.println(littleBuffer.position());
System.out.println(tempRead);
buffer.get(temp,0,tempRead);
littleBuffer.put(temp,0,tempRead);
for(int i=0;i<(BUFF_SIZE_RE-tempRead-dataHead.length());i++){
littleBuffer.put("\0".getBytes());
}
System.out.println(littleBuffer.limit());
System.out.println(new String(littleBuffer.array()));
littleBuffer.flip();
this.out.write(buffer);
littleBuffer.clear();
}
buffer.clear();
}
}
}
}
}
Клиент:
package niotest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
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 java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class NIOClientTest {
public static void main(String[] args) throws IOException{
new Client(new InetSocketAddress("localhost",1235)).start();
}
}
class Client {
private static final int BUFF_SIZE=1024;
private SocketChannel sc;
private Selector selector;
private boolean ing;
private final ByteBuffer bb;
private Map<Integer,FileObject > downMap;
private String downPath;
Client(InetSocketAddress isa) throws IOException {
this.sc = SocketChannel.open();
this.sc.configureBlocking(false);
this.sc.connect(isa);
this.selector = Selector.open();
this.sc.register(this.selector, SelectionKey.OP_CONNECT);
this.ing = true;
bb=ByteBuffer.allocate(BUFF_SIZE);
this.downPath="F:\\multiThreadDL";
this.downMap=new HashMap<>();
}
void start() {
System.out.println("client start");
File path=new File(this.downPath);
if(!path.exists()) path.mkdir();
new Thread(new KeyboardInput()).start();
new Thread(new Listener()).start();
}
private void stop() {
this.ing = false;
}
class KeyboardInput implements Runnable {
@Override
public void run() {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
while (Client.this.ing) {
if (sc.isConnected()) {
try {
String input = br.readLine();
// System.out.println(input);
synchronized (bb) {
bb.clear();
if (input.equals("exit;")) {
bb.put("end;".getBytes());
bb.flip();
sc.write(bb);
br.close();
stop();
break;
}
bb.put(input.getBytes()); //本来写成了get,导致白白移动了position,却没有内容,内容是空的,服务器就输出不了。
bb.flip();
sc.write(bb);
}
} catch(IOException ioe){
ioe.printStackTrace();
}
}
}
}
}
class Listener implements Runnable {
@Override
public void run() {
while (Client.this.ing) {
try {
selector.select();
Iterator<SelectionKey>keys=selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isValid()) {
if(key.isConnectable()&&!sc.isConnected()){
sc.finishConnect();
sc.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable()) {
handleRead(key);
}
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
stop();
}
}
try{
selector.close();
sc.close();
}catch (IOException ioe){
ioe.printStackTrace();
}
}
private void handleRead(SelectionKey key)throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
synchronized (bb) { //虽然通道是双向的,但这样公用一个缓冲区,就要做同步,很耗性能。
bb.clear();
int len = sc.read(bb);
System.out.println(len);
if (len > 0) {
byte[] bytes = new byte[len];
bb.flip(); //绝杀之句。不加这一句,又会读不出东西来。
bb.get(bytes);
String[] temp;
String meg=meg = new String(bytes, 0, len);
if(meg.startsWith("N")||meg.startsWith("Y")) {
temp = meg.split(";");
if (meg.startsWith("N") && temp.length==3) {
System.out.println("The file " + temp[1] + " : "+temp[2]);
} else if (meg.startsWith("Y") && temp.length==4) {
FileChannel out=FileChannel.open(Paths.get(Client.this.downPath+"\\"+temp[3]), StandardOpenOption.READ,StandardOpenOption.CREATE,StandardOpenOption.WRITE);
int fd=Integer.parseInt(temp[1]);
long size=Integer.parseInt(temp[2]);
Client.this.downMap.put(fd,new FileObject(size,temp[3],out));
}
}else{
meg=new String(bytes,0,(BUFF_SIZE+"").length()+4,"UTF-8");
System.out.println(meg);
temp=meg.split(":",2);
int fd=Integer.parseInt(temp[0]);
int endIndex=Integer.parseInt(temp[1]);
bb.clear();
int offset=temp[0].length()+1;
bb.put(bytes,offset,endIndex-offset); //不知道为什么要这样写?看一下三参put的源码就知道了,
// 第三个参数指的是长度,而不是指结束位置,所以叫偏移而不是截取
bb.flip();
if(Client.this.downMap.containsKey(fd)) {
FileObject fb = Client.this.downMap.get(fd);
int written=bb.remaining();
fb.setDowned(written);
if((written+fb.fileBuffer.position())>=fb.fileBuffer.capacity()){
fb.fileBuffer.flip();
fb.getOut().write(fb.fileBuffer);
fb.fileBuffer.clear();
}
fb.fileBuffer.put(bb);
if(fb.isCompeleted()){
fb.fileBuffer.flip();
fb.getOut().write(fb.fileBuffer);
System.out.println(fb.getFileName() + ": 100%");
Client.this.downMap.remove(fd);
fb.getOut().close();
}else {
String outPut=fb.getFileName() + ": " + fb.getPercent()+"%";
System.out.println(outPut);
// StringBuilder backOut=new StringBuilder();
// for(int i=0;i<outPut.length();i++){
// backOut.append("\b"); //java中\b没有用
// }
// System.out.println(backOut.toString());
}
}else {
System.out.println("野包? "+fd);
}
}
} else if (len < 0) {
key.cancel();
sc.close();
}
}
}
}
class FileObject{
private static final int FILE_BUFF_SIZE=5*1024*1024;
private FileChannel out;
private long fileSize;
private long downed;
private String fileName;
ByteBuffer fileBuffer;
FileObject(long fileSize,String fileName,FileChannel out){
this.fileSize=fileSize;
this.out=out;
this.downed=0;
this.fileName=fileName;
this.fileBuffer=ByteBuffer.allocate(FILE_BUFF_SIZE);
}
void setDowned(int hasGet){
this.downed+=hasGet;
}
boolean isCompeleted(){
return this.downed==this.fileSize;
}
int getPercent(){
return (int)(((double)this.downed/(double)this.fileSize)*100.0d);
}
String getFileName(){
return this.fileName;
}
FileChannel getOut(){
return this.out;
}
}
}
запуск сервера …… Имя файла запроса: bizhi1.jpeg Размер файла 28bizhi1.jpeg - 278605, fd - 28 8 1016 102428: 1024: ���� JFIF ��; СОЗДАТЕЛЬ: gd-jpeg v1.0 (с использованием IJG JPEG v62), качество = 90 �� C
�� 8� »��
�A �}! 1AQa "q2�� # B��R�� $ 3br�% & '() 456789: CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������������������������
�� � w! 1AQaq "2B���� # 3R�br� �� ^, ��_C] �r�� @ ��h�B�P�ҷ, / RO�W [� = yU��th�c����PΤ���� ��I� = j���, �Ӛ��c�q���, # �ҵ / 0m-� [RL� ^ ~ Z�4�6N��H�-������aZ�S� ��e��� �s��� $ 3��Eu7z���dY g + ����eWE�f�b; �s�wj����� ��Ug�h��N + �RL����]г [KWzWQ1r ск | ֩) п $ уу] KL B���
\ 7м [9YS����6�Ud�F��X% �isD��) � ֶ, ��� ',
запуск клиента bizhi1.jpeg 26 1024 V��� # �� Исключение в потоке«Thread-1» java.lang.NumberFormatException: для входной строки: «V��� # ��» в java.lang.NumberFormatException.forInputString (NumberFormatException.java:65) в java.lang.Integer.parseInt (целое число. java: 580) в java.lang.Integer.parseInt (Integer.java:615) в niotest.Client $ Listener.handleRead (NIOClientTest.java:156) в niotest.Client $ Listener.run (NIOClientTest.java:106) вjava.lang.Thread.run (Thread.java:748)