J
John
Hi All, I have developed a simple NIO web server and was just looking
to elicit some comments on my coding style and the like. I would also
appreciate it if you could out any potentially dangerous/unsafe NIO
code that I have used. As I have been debugging my program a lot of the
NIO stuff doesn't seem to be entirely intuitive. The program works OK.
for the most part but occasionally I do get the exception "An existing
connection
was forcibly closed by the remote host " occasionally. Any ideas ?
The code is attached inline below:
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 write, long total) {
this.fchannel=fchannel;
this.position=write;
this.total=total;
}
public FileChannel fchannel;
long position;
long total;
}
public class proxy
{
public static void main(String[] args)
{
HashMap<SocketChannel,ChannelState> remote_clients=new HashMap();
//File wwwBase=new File("h:/workspace/web server/");
File wwwBase=new File("/home/jacasey/workspace/web server/");
ByteBuffer buffer=ByteBuffer.allocateDirect(16384);
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.isAcceptable() && selkey.isValid())
{
ServerSocketChannel server=(ServerSocketChannel)
selkey.channel();
SocketChannel client=server.accept();
if(client==null)
return;
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
}
else if(selkey.isReadable() && selkey.isValid())
{
if(selkey.isValid())
{
handle_ready_read(wwwBase,buffer,selkey,remote_clients);
}
}
else if(selkey.isWritable() && selkey.isValid())
{
if(selkey.isValid())
{
handle_write_ready(remote_clients, selector, selkey);
}
}
}
catch(IOException err)
{
selkey.channel().close();
selkey.cancel();
err.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void handle_write_ready(HashMap<SocketChannel,
ChannelState> remote_clients, Selector selector, SelectionKey selkey)
throws IOException, ClosedChannelException {
SocketChannel client=(SocketChannel) selkey.channel();
ChannelState state=remote_clients.get(client);
FileChannel fchannel=state.fchannel;
long write=fchannel.transferTo(state.position,16384,client);
if(write>0)
{
state.position+=write;
}
else
{
state.fchannel.close();
selkey.channel().close();
selkey.cancel();
}
}
public static void handle_ready_read(File wwwBase, ByteBuffer buffer,
SelectionKey selkey, HashMap<SocketChannel, ChannelState>
remote_clients) throws IOException, MalformedURLException,
FileNotFoundException {
SocketChannel client=(SocketChannel) selkey.channel();
int read=client.read(buffer);
// process the request if we actually read anything in
if(read>1)
{
// determine what sort of request it is
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++;
break;
}
requestMethod+=(char)request;
}
// 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==' ')
break;
queryString.append((char)request);
}
}
else
{
System.err.println("unhandled method"+requestMethod);
// unhandled method
}
File data=new File(wwwBase+queryString.toString());
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");
output.append("Content-Type: "+contentType+"\r\n");
output.append(("Content-Length: "+data.length()+"\r\n"));
output.append("\r\n");
ByteBuffer header=ByteBuffer.allocate(1024);
header.put(output.toString().getBytes());
header.flip();
long n=client.write(header);
if(n<output.length())
{
System.err.println("something weird happening");
}
// pipe the file channel data back to our socket channel
long write=fchannel.transferTo(0,16384,client);
// if we have a short write select OP_WRITE
if(write<1)
{
selkey.cancel();
client.close();
}
else if(write<data.length())
{
remote_clients.put(client,new
ChannelState(fchannel,write,data.length()));
client.register(selkey.selector(),SelectionKey.OP_WRITE);
}
else
{
selkey.cancel();
client.close();
}
}
}
else
{
selkey.cancel();
client.close();
}
}
}
to elicit some comments on my coding style and the like. I would also
appreciate it if you could out any potentially dangerous/unsafe NIO
code that I have used. As I have been debugging my program a lot of the
NIO stuff doesn't seem to be entirely intuitive. The program works OK.
for the most part but occasionally I do get the exception "An existing
connection
was forcibly closed by the remote host " occasionally. Any ideas ?
The code is attached inline below:
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 write, long total) {
this.fchannel=fchannel;
this.position=write;
this.total=total;
}
public FileChannel fchannel;
long position;
long total;
}
public class proxy
{
public static void main(String[] args)
{
HashMap<SocketChannel,ChannelState> remote_clients=new HashMap();
//File wwwBase=new File("h:/workspace/web server/");
File wwwBase=new File("/home/jacasey/workspace/web server/");
ByteBuffer buffer=ByteBuffer.allocateDirect(16384);
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.isAcceptable() && selkey.isValid())
{
ServerSocketChannel server=(ServerSocketChannel)
selkey.channel();
SocketChannel client=server.accept();
if(client==null)
return;
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
}
else if(selkey.isReadable() && selkey.isValid())
{
if(selkey.isValid())
{
handle_ready_read(wwwBase,buffer,selkey,remote_clients);
}
}
else if(selkey.isWritable() && selkey.isValid())
{
if(selkey.isValid())
{
handle_write_ready(remote_clients, selector, selkey);
}
}
}
catch(IOException err)
{
selkey.channel().close();
selkey.cancel();
err.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void handle_write_ready(HashMap<SocketChannel,
ChannelState> remote_clients, Selector selector, SelectionKey selkey)
throws IOException, ClosedChannelException {
SocketChannel client=(SocketChannel) selkey.channel();
ChannelState state=remote_clients.get(client);
FileChannel fchannel=state.fchannel;
long write=fchannel.transferTo(state.position,16384,client);
if(write>0)
{
state.position+=write;
}
else
{
state.fchannel.close();
selkey.channel().close();
selkey.cancel();
}
}
public static void handle_ready_read(File wwwBase, ByteBuffer buffer,
SelectionKey selkey, HashMap<SocketChannel, ChannelState>
remote_clients) throws IOException, MalformedURLException,
FileNotFoundException {
SocketChannel client=(SocketChannel) selkey.channel();
int read=client.read(buffer);
// process the request if we actually read anything in
if(read>1)
{
// determine what sort of request it is
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++;
break;
}
requestMethod+=(char)request;
}
// 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==' ')
break;
queryString.append((char)request);
}
}
else
{
System.err.println("unhandled method"+requestMethod);
// unhandled method
}
File data=new File(wwwBase+queryString.toString());
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");
output.append("Content-Type: "+contentType+"\r\n");
output.append(("Content-Length: "+data.length()+"\r\n"));
output.append("\r\n");
ByteBuffer header=ByteBuffer.allocate(1024);
header.put(output.toString().getBytes());
header.flip();
long n=client.write(header);
if(n<output.length())
{
System.err.println("something weird happening");
}
// pipe the file channel data back to our socket channel
long write=fchannel.transferTo(0,16384,client);
// if we have a short write select OP_WRITE
if(write<1)
{
selkey.cancel();
client.close();
}
else if(write<data.length())
{
remote_clients.put(client,new
ChannelState(fchannel,write,data.length()));
client.register(selkey.selector(),SelectionKey.OP_WRITE);
}
else
{
selkey.cancel();
client.close();
}
}
}
else
{
selkey.cancel();
client.close();
}
}
}