NIO Server Example

来源:互联网 发布:人工智能用途 编辑:程序博客网 时间:2024/05/17 23:55

 This is the NIO version of the[url=http://forum.java.sun.com/thread.jsp?forum=11&thread=530820]simplemessage switch I posted earlier today[/url]. I only include the 2classes that differ from the multithreaded version. This code uses asingle thread for the socket I/O. It attempts to address the issueswith OP_WRITE and most of the things we discussed in[url=http://forum.java.sun.com/thread.jsp?forum=4&thread=459338]Tamingthe NIO Circus[/url]. The same requests and caveats apply and there aredukes.

pkwnet NIO message switch

//================= NIOSwitch.java ==========================================//

package pkwnet.msgswitch;

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

/**
* a simple message switch using NIO based socket i/o
* part of the pkwnet package
* a very simple text message switching program
* default command line is java StreamSwitch -p5050 -i600
* -p port to listen on
* -i default idle time in seconds
* user commands start with $ and consist of blank seperated arguments
* other lines sent by the user are forwarded
* $on nickname targets
*     sign on as nickname, sending to targets
* $to targets
*     change target list, reports current value
* $list nicknames
*     list status of specified nicknames
* $list
*     list all connected users
* $off
*     sign off
*
* @author PKWooster
* @version 1.0 June 14,2004
*/

public class NIOSwitch
{
static int debugLevel=2;
private UserTable perUser = new UserTable();
private Timer idleTimer;
private ServerSocket ss; // the listening socket
private ServerSocketChannel sschan; // the listening channel
private Selector selector; // the only selector
private int bufsz = 8192;
private int idleTime;

private void listen(int port, int idleTime)
{
this.idleTime = idleTime;
idleTimer = new Timer();
idleTimer.scheduleAtFixedRate(new TimerTask(){public void run(){oneSec();}},0,1000);

int n=0;
Iterator it;
SelectionKey key;
Object att;
int io;

Functions.dout(12,"listening on port="+port);
try
{
sschan = ServerSocketChannel.open();
sschan.configureBlocking(false);
ss = sschan.socket();
ss.bind(new InetSocketAddress(port));
selector = Selector.open();
sschan.register(selector, SelectionKey.OP_ACCEPT);
}
catch(IOException ie)
{
ie.printStackTrace();
idleTimer.cancel();
System.exit(0);
}

while(true)
{
// now we select any pending io
try{n = selector.select();} // select
catch(Exception e){Functions.fail(e,"select failed");}
Functions.dout(0,"select n="+n);

// process any selected keys
Set selectedKeys = selector.selectedKeys();
it = selectedKeys.iterator();
while(it.hasNext())
{
key = (SelectionKey)it.next();
int kro = key.readyOps();
Functions.dout(0,"kro="+kro);
if((kro & SelectionKey.OP_READ) == SelectionKey.OP_READ)doRead(key);
if((kro & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)doWrite(key);
if((kro & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT)doAccept(key);
it.remove(); // remove the key
}
}
}

private void doAccept(SelectionKey sk)
{
ServerSocketChannel sc = (ServerSocketChannel)sk.channel();
Functions.dout(2,"accept");
SocketChannel usc = null;
ByteBuffer data;
try
{
usc = sc.accept();
usc.configureBlocking(false);
Socket sock = usc.socket();
String nm = sock.getInetAddress()+":"+sock.getPort();
System.out.println("connection from "+nm);
sock.setKeepAlive(true);
data = ByteBuffer.allocate(bufsz);
data.position(data.limit()); // looks like write complete
SelectionKey dsk = usc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE,null);
Connection conn = new NIOConnection(dsk); // contains socket i/o code
conn.setName(nm);
new User(conn, perUser, idleTime); // contains the user application code
dsk.attach(conn); // link it to the key so we can find it
}
catch(IOException re){Functions.fail(re,"registration error");}
}

private void doRead(SelectionKey sk)
{
NIOConnection conn = (NIOConnection)sk.attachment(); // get our connection
conn.doRead();
}

private void doWrite(SelectionKey sk)
{
NIOConnection conn = (NIOConnection)sk.attachment(); // get our connection
conn.doWrite();
}

static public void main(String [] args)
{
int port = 5050;
int idleTime = 600;
int level = 2;
for(int i = 0; i<args.length; i++)
{
if(args[i].startsWith("-p"))port = Functions.toInt(args[i].substring(2),port);
if(args[i].startsWith("-i"))idleTime = Functions.toInt(args[i].substring(2),idleTime);
if(args[i].startsWith("-d"))level = Functions.toInt(args[i].substring(2),level);
}
Functions.setDebugLevel(level);
new NIOSwitch().listen(port,idleTime);
}

        private void oneSec()
        {
                 Object[] uv = perUser.allUsers();
                for(int i=0; i><uv.length; i++)((User)uv[i]).oneSec();
        }
}

//================= NIOConnection.java ======================================//

package pkwnet.msgswitch;
/**
* describes a connection between an NIO selection key and a user
* @author PKWooster
* @version 1.0 June 14,2004
*/


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

public class NIOConnection implements Connection
{
private SelectionKey sk;
private ConnectionUser cu;
private int state;
private LinkedList sendQ = new LinkedList();

private CharsetEncoder encoder;
private CharsetDecoder decoder;
private ByteBuffer recvBuffer=null;
private ByteBuffer sendBuffer=null;
private StringBuffer recvString = new StringBuffer();
private String crlf = "/r/n";
private boolean writeReady = false;
private String name="";
        
/**
* construct a NIOConnection from a selection key
*/

NIOConnection(SelectionKey sk)
{
state = Connection.OPENED;
this.sk = sk; // link this to the key
         sk.attach(this);

Charset charset = Charset.forName("ISO-8859-1");
decoder = charset.newDecoder();
encoder = charset.newEncoder();
recvBuffer = ByteBuffer.allocate(8196);
}

/**
* attach a connection user to this connection
*/

public void attach(ConnectionUser cu)
{
this.cu = cu;
}

        /**
          * process a read ready selection
          */

public void doRead()
{
SocketChannel sc = (SocketChannel)sk.channel();
if(sc.isOpen())
{
int len;
recvBuffer.clear();
try{len = sc.read(recvBuffer);}
catch(IOException e){e.printStackTrace(); len=-1;} // error look like eof
Functions.dout(1,"read len="+len);

if(len > 0)
{
recvBuffer.flip();
CharBuffer buf = null;
try{buf = decoder.decode(recvBuffer);} // convert bytes to chars
catch(Exception ce){ce.printStackTrace(); len = -1;}
toUser(buf);
}
if(len < 0)close();
}
else System.out.println("read closed");
}
        /*
          * split up received data and forward it to the user
          */

private void toUser(CharBuffer buf)
{
if(buf != null)
{
int i = 0;
int j = 0;
recvString.append(buf); // as a string buffer
int z = recvString.length();
while(i >< z)
{
char c = recvString.charAt(i);
if(c == '/r' || c == '/n')
{
i++;
if(c == '/r' && i < z && '/n' == recvString.charAt(i))i++;
cu.receive(recvString.substring(j,i)); // to user
j = i+1; // start of next
}
else i++;
}
if(j < z)recvString.delete(0,j); // drop front of string buffer
else recvString = new StringBuffer();
}
}

        /**
          * process a write ready selection
          */

public void doWrite()
{
Functions.dout(12,"write ready");
sk.interestOps(SelectionKey.OP_READ); // deselect OP_WRITE
writeReady = true; // write is ready
if(sendBuffer != null)write(sendBuffer); // may have a partial write
writeQueued(); // write out rest of queue
}
        /**
          * queue up a text string to send and try to send it out
          */

public void send(String text)
{
sendQ.add(text); // first put it on the queue
writeQueued(); // write all we can from the queue
}

        /*
          * attempt to send all queued data
          */

private void writeQueued()
{
while(writeReady && sendQ.size() > 0) // now process the queue
{
String msg = (String)sendQ.remove(0);
write(msg); // write the string
}
}

        /*
          * convert a text string to a byte buffer and send it
          */

private void write(String text)
{
try
{
ByteBuffer buf = encoder.encode(CharBuffer.wrap(text));
write(buf);
}
catch(Exception e){e.printStackTrace();}
}

        /*
          * write out a byte buffer
          */

private void write(ByteBuffer data)
{
SocketChannel sc = (SocketChannel)sk.channel();
if(sc.isOpen())
{
int len=0;

if(data.hasRemaining())
{
try{len = sc.write(data);}
catch(IOException e){e.printStackTrace(); close();}
}
if(data.hasRemaining()) // write would have blocked
{
Functions.dout(12,"write blocked");
writeReady = false;
sk.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); // select OP_WRITE
sendBuffer = data; // save the partial buffer
}
else sendBuffer = null;
}
else Functions.dout(12,"write closed");
}

        /*
          * close the connection and its socket
          */

public void close()
{
if(state != Connection.CLOSED)
{
SocketChannel sc = (SocketChannel)sk.channel();
if(sc.isOpen())
{
Functions.dout(2,"closing channel");
try
                                {
                                         sc.close();
                                         sk.selector().wakeup();
                                         sk.attach(null);
                                }
catch(IOException ce){Functions.fail(ce,"close failed");}
}
else Functions.dout(12,"already closed");
state = Connection.CLOSED;
cu.stateChange(state);
}
}

public String getName(){return name;}
public void setName(String nm){name = nm;}
public int getState(){return state;}
}