package org.fanhongtao.net.frame.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.fanhongtao.log.RunLogger;
import org.fanhongtao.net.frame.LoopedData;
import org.fanhongtao.thread.ExRunnable;
/**
* 实现读取Socket的线程,负责从 key 中读取数据<br>
* 读取数据后,是否能够组成一个完整的消息,如果可以,则从<br>
* 这里采用静态队列来对读线程进行调度:当有Socket事件时,将对应的Socket放入pool中,再由各个线程去竞争。
*
* @author Dharma
* @created 2009-5-2
*/
public class ChannelReader extends ExRunnable
{
private static BlockingQueue<SelectionKey> pool = new LinkedBlockingQueue<SelectionKey>();
private static int BUFFER_SIZE = 1024;
@Override
public void run()
{
while (!isStoped())
{
SelectionKey key = null;
try
{
// 线程从队列中获取任务并执行
key = pool.take();
readMessage(key);
}
catch (Exception e)
{
RunLogger.warn("Failed to read from key [" + key + "]", e);
}
}
}
/**
* 处理连接数据读取
* @param key SelectionKey
* @throws IOException
*/
private void readMessage(SelectionKey key)
{
// 读取客户端数据
SocketChannel sc = (SocketChannel)key.channel();
LoopedData data = new LoopedData();
boolean closeKey = false;
try
{
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (true)
{
buffer.clear();
int num = sc.read(buffer);
if (num == 0)
{
break;
}
else if (num == -1)
{
closeKey = true;
break;
}
byte[] buf = buffer.array();
data.add(buf, 0, num);
}
}
catch (IOException e)
{
RunLogger.info("Catch IOException while reading", e);
closeKey = true;
}
finally
{
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
if (closeKey)
{
NetUtils.closeKey(key);
return;
}
}
Connection conn = (Connection)key.attachment();
conn.getBuffer().add(data.getData());
MessageHandler.process(conn);// 提交给处理线程进行处理
}
/**
* 处理客户请求,管理用户的联结池,并唤醒队列中的线程进行处理
*/
public static void processRequest(SelectionKey key)
{
synchronized (pool)
{
key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
try
{
pool.put(key);
}
catch (InterruptedException e)
{
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
RunLogger.warn("Failed to put key into pool.", e);
}
}
}
}