Servidor de arquivo com New IO

Fala Galera,

Estou tentando utilizar o pacote nio para criar um servidor de transfência de arquivo…
Peguei esse exemplo, mas ele apenas transfere uns poucos bytes do arquivo, não transfere muito, mas pelo o que eu li e entendi do nio, bastava fazer como está no exemplo para ele transferir tudo. Será que algum expert em nio conseguiria me ajudar com isso???

Obrigado e abracos!!!

[code]package nioserver;

import java.io.;
import java.net.
;
import java.nio.;
import java.nio.channels.
;
import java.nio.channels.spi.;
import java.nio.charset.
;
import java.util.*;

public class Server {
private static final int HTTPD_PORT = 80;

public static boolean verbose = false;
public static Statistics statistics = new Statistics();

private AcceptThread acceptor;
private ReadWriteThread responder;

public static void main(String[] args) {
int port = HTTPD_PORT;
File baseDirectory = null;

try {
  int argIndex = 0;
  
  if(args.length > argIndex && "-v".equals(args[argIndex])) {
    verbose = true;
    argIndex++;
  }

  if(args.length > argIndex) {
      baseDirectory = new File(args[argIndex++]);
  }

  if(args.length > argIndex) {
    port = Integer.parseInt(args[argIndex++]);
  }

  Server s = new Server(baseDirectory, port);
  s.start();
} catch(Exception ex) {
  System.err.println(ex);
}

}

public Server(File baseDirectory, int port) throws Exception {
Selector acceptSelector = Selector.open();
Selector readSelector = Selector.open();
ConnectionList connections = new ConnectionList(readSelector);
acceptor = new AcceptThread(acceptSelector, connections, port);
responder = new ReadWriteThread(readSelector, connections, baseDirectory);
}

public void start() {
statistics.start();
responder.start();
acceptor.start();
}
}

/**

  • AcceptThread waits for incoming connections. When a connection is made,
  • it registers the new socket on the “readWriteSelector”.
    */
    class AcceptThread extends Thread {
    private ServerSocketChannel ssc;
    private Selector connectSelector;
    private ConnectionList acceptedConnections;

public AcceptThread(Selector connectSelector, ConnectionList list, int port) throws Exception {
super(“Acceptor”);

this.connectSelector = connectSelector;
this.acceptedConnections = list;

ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);

InetSocketAddress address = new InetSocketAddress(port);
ssc.socket().bind(address);

if(Server.verbose)
  System.out.println("Bound to " + address);

ssc.register(this.connectSelector, SelectionKey.OP_ACCEPT);

}

public void run() {
while(true) {
try {
if(Server.verbose)
System.out.println(“AcceptThread: Selecting”);

    connectSelector.select();

    acceptPendingConnections();
  } catch(Exception ex) {
    ex.printStackTrace();
  }
}

}

protected void acceptPendingConnections() throws Exception {
Set readyKeys = connectSelector.selectedKeys();

for(Iterator i = readyKeys.iterator(); i.hasNext(); ) {
  SelectionKey key = (SelectionKey)i.next();
  i.remove();

  ServerSocketChannel readyChannel = (ServerSocketChannel)key.channel();

  SocketChannel incomingChannel = readyChannel.accept();

  if(Server.verbose)
    System.out.println("AcceptThread: Connection from " + incomingChannel.socket().getInetAddress());

  acceptedConnections.push(incomingChannel);
}

}
}

class ReadWriteThread extends Thread {
private static final int READ_BUFFER_SIZE = 16;

private static final Integer INVALID_REQUEST = new Integer(400);
private static final Integer PAGE_NOT_FOUND = new Integer(404);

private Selector readSelector;
private ConnectionList acceptedConnections;
private File baseDirectory;
private Selector readWriteSelector;
private WeakHashMap fileCache = new WeakHashMap();
private WeakHashMap errorBufferCache = new WeakHashMap();
private ByteBuffer[] responseBuffers = new ByteBuffer[2];
private ByteBuffer readBuffer;
private Charset ascii;
private CharsetDecoder asciiDecoder;

public ReadWriteThread(Selector readSelector, ConnectionList acceptedConnections, File dir) throws Exception {
super(“Reader-Writer”);

this.readSelector = readSelector;
this.acceptedConnections = acceptedConnections;
this.baseDirectory = dir;
this.readBuffer = ByteBuffer.allocateDirect(READ_BUFFER_SIZE);

ascii = Charset.forName("US-ASCII");
asciiDecoder = ascii.newDecoder();
responseBuffers[0] = initializeResponseHeader();

if(Server.verbose) {
  System.out.println("Using " + baseDirectory.getAbsolutePath() + " as document root");
}

}

public void run() {
while(true) {
try {
if(Server.verbose)
System.out.println(“ReadWriteThread: Selecting”);

    registerNewChannels();

    int keysReady = readSelector.select();

    if(keysReady > 0) {
      acceptPendingRequests();
    }
  } catch(Exception ex) {
    ex.printStackTrace();
  }
}

}

protected void registerNewChannels() throws Exception {
SocketChannel channel;
while(null != (channel = acceptedConnections.removeFirst())) {
channel.configureBlocking(false);
channel.register(readSelector, SelectionKey.OP_READ, new StringBuffer());
}
}

protected void acceptPendingRequests() throws Exception {
Set readyKeys = readSelector.selectedKeys();

for(Iterator i = readyKeys.iterator(); i.hasNext(); ) {
  SelectionKey key = (SelectionKey)i.next();
  i.remove();

  readRequest(key);
}

}

protected void readRequest(SelectionKey key) throws Exception {
SocketChannel incomingChannel = (SocketChannel)key.channel();
Socket incomingSocket = incomingChannel.socket();

try {
  int bytesRead = incomingChannel.read(readBuffer);
  readBuffer.flip();
  String result = asciiDecoder.decode(readBuffer).toString();
  readBuffer.clear();

  StringBuffer requestString = (StringBuffer)key.attachment();
  requestString.append(result);
  
  if(result.endsWith("\n\n") || result.endsWith("\r\n\r\n")) {
      handleCompletedRequest(requestString.toString(), incomingChannel);
  }
} catch(RequestException re) {
  sendError(incomingChannel, re);
} catch(IOException ioe) {
  sendError(incomingChannel, RequestException.INTERNAL_SERVER_ERROR);
}

}

protected void handleCompletedRequest(String request, SocketChannel channel) throws RequestException, IOException {
StringTokenizer tok = new StringTokenizer(request);
tok.nextToken(); // skip the method
String path = tok.nextToken(); // grab the URI, ignore the rest

sendFile(path, channel);

Server.statistics.request();

// This behaves like HTTP 1.0, close connection
// after handling the request.
channel.close();

}

protected ByteBuffer initializeResponseHeader() throws Exception {
// Pre-load a “good” HTTP response as characters.
CharBuffer chars = CharBuffer.allocate(88);
chars.put(“HTTP/1.1 200 OK\n”);
chars.put(“Connection: close\n”);
chars.put(“Server: Java New I/O Example\n”);
chars.put(“Content-Type: text/html\n”);
chars.put("\n");
chars.flip();

// Translate the Unicode characters into ASCII bytes.
ByteBuffer buffer = ascii.newEncoder().encode(chars);

return buffer;

}

/**

  • Locate the requested file and map it into memory. Use a weak cache
  • to speed up heavily-requested files.
    */
    protected void sendFile(String uri, SocketChannel channel) throws RequestException, IOException {
    if(Server.verbose)
    System.out.println("ReadWriteThread: Sending " + uri);
Object obj = fileCache.get(uri);

if(obj == null) {
  Server.statistics.fileMiss();

  try {
    File f = new File(baseDirectory, uri);
    FileInputStream fis = new FileInputStream(f);
    FileChannel fc = fis.getChannel();
    
    int fileSize = (int)fc.size();
    responseBuffers[1] = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
    fileCache.put(uri, responseBuffers[1]);
  } catch(FileNotFoundException fnfe) {
    throw RequestException.PAGE_NOT_FOUND;
  }
} else {
  Server.statistics.fileHit();

  responseBuffers[1] = (MappedByteBuffer)obj;
  responseBuffers[1].rewind();
}

responseBuffers[0].rewind();
channel.write(responseBuffers);

}

protected void sendError(SocketChannel channel, RequestException error) throws Exception {
ByteBuffer buffer = null;
Object obj = errorBufferCache.get(error);

if(obj == null) {
  if(Server.verbose) 
    System.out.println("Error cache miss");

  CharBuffer chars = CharBuffer.allocate(64);
  chars.put("HTTP/1.0 " + error.getErrorCode() + " OK\n");
  chars.put("Connection: close\n");
  chars.put("Server: Java New I/O Example\n");
  chars.put("\n");
  chars.flip();

  // Translate the Unicode characters into ASCII bytes.
  buffer = ascii.newEncoder().encode(chars);

  errorBufferCache.put(error, buffer);
} else {
  if(Server.verbose) 
    System.out.println("Error cache hit");

  buffer = (ByteBuffer)obj;
  buffer.rewind();
}

channel.write(buffer);

}
}

class RequestException extends Exception {
public static final RequestException INVALID_REQUEST = new RequestException(400);
public static final RequestException PAGE_NOT_FOUND = new RequestException(404);
public static final RequestException INTERNAL_SERVER_ERROR = new RequestException(500);

private int errorCode;

public RequestException(int errorCode) {
super();

this.errorCode = errorCode;

}

public int hashCode() {
return errorCode;
}

public boolean equals(Object that) {
try {
return this.errorCode == ((RequestException)that).errorCode;
} catch(ClassCastException cce) {
return false;
}
}

public int getErrorCode() {
return errorCode;
}

public String toString() {
return “” + errorCode;
}
}

class ConnectionList {
private LinkedList list = new LinkedList();
private Selector selectorToNotify;

public ConnectionList(Selector sel) {
this.selectorToNotify = sel;
}

public synchronized void push(SocketChannel newlyConnectedChannel) {
list.add(newlyConnectedChannel);
selectorToNotify.wakeup();
}

public synchronized SocketChannel removeFirst() {
if(list.size() == 0)
return null;

return (SocketChannel)list.removeFirst();

}
}

class Statistics extends Thread {
private long startTime;
private float fileCacheHits = 0;
private float fileCacheMisses = 0;
private float requests = 0;

public Statistics() {
super(“Statistics”);
}

public void fileHit() {
fileCacheHits++;
}

public void fileMiss() {
fileCacheMisses++;
}

public void request() {
requests++;
}

public void start() {
startTime = System.currentTimeMillis();
super.start();
}

public void run() {
while(true) {
try {
sleep(30*1000);

    reportCacheHitRate();
    reportRequestsPerMinute();
  } catch(InterruptedException ie) {
  }
}

}

private void reportCacheHitRate() {
float hitRate = fileCacheHits / (fileCacheHits + fileCacheMisses);
System.out.println("Statistics: cache hit rate = " + hitRate);
}

private void reportRequestsPerMinute() {
long millis = System.currentTimeMillis() - startTime;
float requestRate = (60 * 1000 * requests) / millis;
System.out.println("Statistics: requests per minute = " + requestRate);
}
}[/code]

Não cheguei a olhar se a parte do NIO esta correta mais vi que não consta o header de http Content-Length isto pode fazer o navegador cortar a conexão por não saber o tamanho dos dados que estão chegando no seu teste.

Será que há alguma influencia?

Abracos!!!

O HTTP possui alguns headers obrigatórios, não sei se somente o keep-alive é suficiente. De qualquer forma na parte server é gerado algum erro? se não ocorre erro acredito que seja falta de algum header sim, você pode dar uma olhada no google ou até mesmo na w3c como é o uso correto do keep-alive.

Fala kaoe,

Eu consegui resolver esse problema!!! :slight_smile:
Não eram os headers, era que no método sendFile, quando rola a chamado

channel.write(responseBuffers);

Ela não está enviando tudo que há no responseBuffers[1]. Tive que acrescentar um loop no final

[code] channel.write(responseBuffers);

while(responseBuffers[1].hasRemaining())
	channel.write(responseBuffers[1]);[/code]

Desse modo consegui enviar todo o arquivo!!!

Obrigado pela ajuda!!!