Re: NIO best practice




EJP wrote:
John wrote:

Thanks for all your suggestions that was great :)

Let us know how you're getting along after these improvements.


Well the code is still a little bit of a work in progress but I have
integrated your suggestions into the following code base. Its amazing
how simple it is to implement persistant connections in http. I had
originally thought it was going to be a little tougher buts its amazing
how easy really is! :)

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;

class ChannelState
{
public ChannelState(FileChannel fchannel, long position, long total,
ByteBuffer buffer) {
this.fchannel=fchannel;
this.position=position;
this.total_file_size=total;
this.buffer=buffer;
}
public FileChannel fchannel;
public ByteBuffer buffer;
public long position;
public long total_file_size;
}

public class webserver
{
public static void main(String[] args)
{
//File wwwBase=new File("h:/workspace/web server/");
//File wwwBase=new File("/home/jacasey/workspace/web server/");

File wwwBase=new File("c:/Eclipse/workspace/web server/");

try {
ServerSocketChannel channel= ServerSocketChannel.open();

InetSocketAddress address=new InetSocketAddress(8080);
channel.socket().bind(address);
channel.configureBlocking(false);

Selector selector=Selector.open();
channel.register(selector,SelectionKey.OP_ACCEPT);

while(true)
{
int n=selector.select();

if(n==0)
{
continue;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> ready = readyKeys.iterator();

while(ready.hasNext())
{
SelectionKey selkey=ready.next();
ready.remove();
try
{
if(selkey.isValid()==false)
{
continue;
}
else if(selkey.isAcceptable())
{
ServerSocketChannel server=(ServerSocketChannel)
selkey.channel();
SocketChannel client=server.accept();

if(client==null)
continue;

client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
ByteBuffer buffer=ByteBuffer.allocate(32768);

SelectionKey key=client.keyFor(selector);
key.attach(buffer);
}
else if(selkey.isReadable())
{
handle_ready_read(wwwBase,selkey);
}
else if(selkey.isWritable())
{
handle_write_ready(selector, selkey);
}
}
catch(IOException err)
{
selkey.channel().close();
err.printStackTrace();
}

}

}
} catch (IOException e) {

e.printStackTrace();
}


}

public static void handle_write_ready(Selector selector, SelectionKey
selkey) throws IOException, ClosedChannelException {
SocketChannel client=(SocketChannel) selkey.channel();

ChannelState state=(ChannelState) selkey.attachment();

if(state.buffer!=null)
{
client.write(state.buffer);

if(state.buffer.hasRemaining())
{
state.buffer.compact();
client.register(selkey.selector(),SelectionKey.OP_WRITE);
selkey.attach(state);
}
else
{
transfer_file(selkey, client, state);
}
}
else
{
transfer_file(selkey, client, state);
}
}

public static void handle_ready_read(File wwwBase, SelectionKey
selkey) throws IOException, MalformedURLException,
FileNotFoundException
{
ByteBuffer buffer=(ByteBuffer) selkey.attachment();
SocketChannel client=(SocketChannel) selkey.channel();
long BYTES_READ=client.read(buffer);
int position=buffer.position();
buffer.flip();

if(BYTES_READ>=4)
{
// String tmp=new String(buffer.array());
// System.err.println(tmp);

boolean short_read = process_request(buffer);
if(short_read==false)
{
// determine what sort of request it is
String requestMethod="";

String queryString = process_query_string(buffer, requestMethod);

File data=new File(wwwBase+queryString);
FileInputStream fis=new FileInputStream(data);
FileChannel fchannel=fis.getChannel();

// use the inbuilt class to guess the type of data we are going to
write back to the client
String contentType="application/octed-stream";
System.out.println("request: "+data.toString());
contentType=URLConnection.guessContentTypeFromName(data.toString());

if(data.isFile())
{
StringBuffer output=new StringBuffer();
output.append("HTTP/1.0 200 OK\r\n");
output.append("Server: proxy\r\n");
//output.append("Connection: close\r\n"); // eventually I will
have to detect whether a client is capable of persistance OK for now.
output.append("Connection: keep-alive\r\n");
output.append("Content-Type: "+contentType+"\r\n");
output.append(("Content-Length: "+data.length()+"\r\n"));
output.append("\r\n");

buffer.clear();
buffer.put(output.toString().getBytes());
buffer.flip();

client.write(buffer);

if(buffer.hasRemaining())
{
// short header write
buffer.compact();

client.register(selkey.selector(),SelectionKey.OP_WRITE);
selkey.attach(buffer);
}
else
{
transfer_file(selkey, client, new ChannelState(fchannel, 0,
data.length(),buffer));
}
}
else
{
// file does not exist
}
}
else
{
// short read
client.register(selkey.selector(),SelectionKey.OP_READ);

// reset the buffer so that we can read more data in
buffer.position(position);
buffer.limit(buffer.capacity());
selkey.attach(buffer);
}
}
else if(BYTES_READ>0)
{
// short read
client.register(selkey.selector(),SelectionKey.OP_READ);

// reset the buffer so that we can read more data in
buffer.position(position);
buffer.limit(buffer.capacity());
selkey.attach(buffer);
}
else if(BYTES_READ==-1)
{
System.err.println("closed");
client.close();
}
}

public static String process_query_string(ByteBuffer buffer, String
requestMethod) {
byte[] request=new byte[buffer.position()];
buffer.flip();
buffer.get(request);
buffer.clear();
int i=0;
for(;i<request.length;i++)
{
if(request[i]==' ')
{
i++;
break;
}
requestMethod+=(char)request[i];
}

// extract the query_string if we are dealing with a get request
StringBuffer queryString=new StringBuffer(100);
if(requestMethod.equalsIgnoreCase("GET"))
{
for(;i<request.length;i++)
{
if(request[i]==' ')
break;

queryString.append((char)request[i]);
}
}
else
{
System.err.println("unhandled method"+requestMethod);
// unhandled method
}
return queryString.toString();
}

public static boolean process_request(ByteBuffer buffer) {
boolean short_read=false;
byte END_HTTP[]="\r\n\r\n".getBytes();
byte END_OF_REQUEST[]=new byte[4];

buffer.position(buffer.limit()-4);
buffer.get(END_OF_REQUEST,0,4);

for(int j=0;j<END_OF_REQUEST.length;j++)
{
if(END_HTTP[j]!=END_OF_REQUEST[j])
{
short_read=true;
break;
}
}
return short_read;
}

public static void transfer_file(SelectionKey selkey, SocketChannel
client, ChannelState cs) throws IOException, ClosedChannelException {

ByteBuffer buffer=cs.buffer;
// pipe the file channel data back to our socket channel
long write=cs.fchannel.transferTo(cs.position,32768,client);

// if we have a short write select OP_WRITE
if(cs.position+write<cs.total_file_size)
{
client.register(selkey.selector(),SelectionKey.OP_WRITE);
cs.position+=write;
selkey.attach(cs);
}
else
{
cs.fchannel.close();
client.register(selkey.selector(),SelectionKey.OP_READ);
buffer.clear();
selkey.attach(buffer);
}
}
}

.



Relevant Pages

  • Re: Socket write behaviour is inconsistent?
    ... copy 1 byte to buffer, copy many bytes to buffer, copy one byte to ... Then why did you write "the client throws an error"? ... remote endpoint for your connection. ... When the response is sent using the first chunk of code, ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: TCPClient Synchronous or Asynchronous Writes
    ... acting as the "server" receives quality assurance data from different ... Each client can register to look at differnt data so there ... is actually a Circular Buffer for each client. ... >> stream data to my clients over a TCPClient connection. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: USB data Transfer question
    ... I'm afraid you can't pass 1-byte length buffer ... the pBuffer!=0 if there is data sending from client. ... and both USB analizer and the pBuffer value is the ... and the write function which write data from USB host to client works ...
    (microsoft.public.windowsce.platbuilder)
  • Re: TCPClient Synchronous or Asynchronous Writes
    ... there's no guarantee the first packet of data ... post it off to the client every few seconds in one block. ... > stream data to my clients over a TCPClient connection. ... > On the server I have a custom circular data buffer that receives byte ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Java NIO Strategy
    ... SocketChannel state, ... could come up with is to maintain state for each SelectionKey via the ... You are right, the NIO libraries are not simple to work with, SSL is ... I keep everything about the client in the attachment, ...
    (comp.lang.java.programmer)