public void send2server(int command, byte[] msg) throws IOException
{
if (out != null)
{
msg = MessageFilter.addHeader(command, msg);
_log.info("发送字节数:" + msg.length);
//msg为byte[]类型
out.write(msg);
out.flush();
}
else
{
throw new IOException("输出流为空");
}
}
try
{
// 处理IO事件
if (key.isAcceptable())
accept(key);
else if (key.isReadable())
{
_log.debug("发现读IO");
//与此问题相关的关键代码为这句
Reader2.processKey(key); // 提交读服务线程读取客户端数据
SocketChannel sc = (SocketChannel) key.channel();
//如果为长连接
if (sc.socket().getKeepAlive())
{
key.interestOps(key.interestOps()
& ~SelectionKey.OP_READ);
_log.debug("移除兴趣读");
}
else
key.cancel();
}
else if (key.isWritable())
{
_log.debug("发现写IO");
SocketChannel sc = (SocketChannel) key.channel();
Writer.processRequest(key); // 提交写服务线程向客户端发送回应数据
//如果为长连接
if (sc.socket().getKeepAlive())
key.interestOps(SelectionKey.OP_READ);
else
key.cancel();
}
}
catch (Exception e)
{
key.cancel();
_log.info("处理key出错,连接可能意外中断了");
e.printStackTrace();
}
package com.gdtec.nmt.nioserver.io;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import com.gdtec.nmt.filter.MessageFilter;
import com.gdtec.nmt.nioserver.InterestInfo;
import com.gdtec.nmt.nioserver.Notifier;
import com.gdtec.nmt.nioserver.Request;
import com.gdtec.nmt.nioserver.Server;
import com.gdtec.nmt.pojo.ClientInfo;
import com.gdtec.nmt.pojo.MessagePackage;
/**
* Title: 读线程
* Description: 该线程用于读取客户端数据
* @author zhuhongzheng
* @version 1.0
*/
public class Reader2 extends Thread {
private static Logger _log = Logger.getLogger(Reader2.class);
private static List requestsPool = new LinkedList();
private static Notifier notifier = Notifier.getNotifier();
public Reader2() {
}
public void run() {
while (true) {
try {
Request request;
synchronized (requestsPool) {
while (requestsPool.isEmpty()) {
requestsPool.wait();
}
request = requestsPool.remove(0);
}
// 读取数据
processRequest(request);
}
catch (Exception e) {
_log.info("读取池出现异常!", e);
}
}
}
private static int BUFFER_SIZE = 20480;
/**
* 读取客户端发出请求数据
* @param sc 套接通道
* @return
* @throws IOException
*/
private static byte[] readInput(SocketChannel sc) throws IOException
{
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int off = 0;
int r = 0;
byte[] data = new byte[BUFFER_SIZE];
//与此问题相关的关键代码,在稍后我贴的日志可以看出,在这里只read了4380B的数据,这个while循环,只进行了一次
//何解?
while ((r = sc.read(buffer)) > 0)
{
_log.debug("发现数据:" + r);
if ((off + r) > data.length)
{
data = grow(data, BUFFER_SIZE * 2);
// System.out.println("容量扩展为:" + data.length);
_log.debug("容量扩展为:" + data.length);
}
byte[] buf = buffer.array();
System.arraycopy(buf, 0, data, off, r);
off += r;
buffer.clear();
_log.debug("共读了:" + off);
}
String memoryMsg = ", freeMemory="
+ (Runtime.getRuntime().freeMemory() / (1024)
+ "k,totalMemory="
+ (Runtime.getRuntime().totalMemory() / (1024)) + "k");
_log.info("共读了:" + off + memoryMsg);
byte[] req = new byte[off];
System.arraycopy(data, 0, req, 0, off);
return req;
}
/**
* 处理连接数据读取
* @param request Request
*/
public static void processRequest(Request request) {
try {
// 读取客户端数据
byte[] received = request.getDataInputByte();
_log.debug("接收到字节数:" + received.length);
//System.out.println("reader 收到数据包:" + new String(received, "GBK"));
MessagePackage msgPackage = MessageFilter.decodeMessage(received);
// String clientData = readInput(sc);
String clientData = null;
//数据不完整或者数据为空,则不做处理
if (msgPackage == null || null == msgPackage.getBody())
{
throw new Exception("数据包格式有问题,请检查");
}
clientData = msgPackage.getBody();
request.setDataInput(clientData);
request.setDataInputByte(new byte[0]);
request.setParameter("command", msgPackage.getCommand());
request.setParameter("bodyLength", msgPackage.getBodyLength());
// 触发onRead
notifier.fireOnRead(request);
}
catch (Exception e)
{
_log.error(e);
}
try
{
//SocketChannel 在accept事件中被置入request对象
SocketChannel sc = request.getSc();
if (sc == null)
return;
//如果是长连接模式,read完了就要把write事件注册到selector,否则无法触发该channel的write事件
if (sc.socket().getKeepAlive())
{
Server.registerInterest(new InterestInfo(sc,
SelectionKey.OP_WRITE, request));
}
}
catch (SocketException e)
{
_log.error("注册写兴趣时出现异常!", e);
}
}
public static void processKey(SelectionKey key)
{
SocketChannel sc = (SocketChannel) key.channel();
Request request = (Request) key.attachment();
try
{
byte[] received = readInput(sc);
if(received.length==0)
{
// TODO:收到空数据。。怎么回事;
return ;
}
_log.debug("接收到字节数:" + received.length);
request.setDataInputByte(received);
put2RequestPool(request);
}
catch (IOException e1)
{
key.cancel();
_log.error(e1);
ClientInfo client = (ClientInfo) request.getParameter("ClientInfo");
Server.clientConnectionError(client);
}
catch (Exception e)
{
_log.error(e);
}
}
/**
* 处理客户请求,管理用户的联结池,并唤醒队列中的线程进行处理
*/
public static void put2RequestPool(Request request) {
synchronized (requestsPool) {
requestsPool.add(requestsPool.size(), request);
requestsPool.notifyAll();
}
}
/**
* 数组扩容
* @param src byte[] 源数组数据
* @param size int 扩容的增加量
* @return byte[] 扩容后的数组
*/
public static byte[] grow(byte[] src, int size) {
byte[] tmp = new byte[src.length + size];
System.arraycopy(src, 0, tmp, 0, src.length);
return tmp;
}
}