/* * Copyright 1999-2012 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package fm.liu.timo.net.connection; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import fm.liu.timo.mysql.packet.MySQLPacket; import fm.liu.timo.net.NIOActor; import fm.liu.timo.net.NIOHandler; import fm.liu.timo.net.NIOProcessor; import fm.liu.timo.util.TimeUtil; /** * @author xianmao.hexm */ public abstract class AbstractConnection implements NIOConnection { protected final SocketChannel channel; protected NIOProcessor processor; protected NIOHandler handler; protected ReentrantLock closedLock = new ReentrantLock(); protected volatile int readBufferOffset; protected volatile ByteBuffer readBuffer; protected volatile ByteBuffer writeBuffer; protected final ConcurrentLinkedQueue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<ByteBuffer>(); private final NIOActor actor; protected AtomicBoolean closed = new AtomicBoolean(false); protected volatile int state; protected Variables variables = new Variables(); protected long id; protected String host; protected int port; protected int localPort; static public class State { public static final int connecting = 0; public static final int authenticating = 1; public static final int idle = 2; public static final int borrowed = 3; public static final int running = 4; public static String getStateDesc(int state) { switch (state) { case connecting: return "connecting"; case authenticating: return "authenticating"; case idle: return "idle"; case borrowed: return "borrowed"; case running: return "running"; default: return "unknow"; } } } public String getStateDesc() { return State.getStateDesc(state); } public boolean isRunning() { return this.state == State.running; } public AbstractConnection(SocketChannel channel, NIOProcessor processor) { this.channel = channel; this.processor = processor; this.setReadBuffer(processor.getBufferPool().allocate()); this.actor = new NIOActor(this); long now = TimeUtil.currentTimeMillis(); this.variables.setUpTime(now); this.variables.update(); } public SocketChannel getChannel() { return channel; } public NIOProcessor getProcessor() { return processor; } public ByteBuffer getReadBuffer() { return readBuffer; } public ConcurrentLinkedQueue<ByteBuffer> getWriteQueue() { return writeQueue; } public void read() throws IOException { actor.read(); } public void onRead(int got) { if (isClosed()) { return; } this.variables.update(); ByteBuffer buffer = this.getReadBuffer(); // 循环处理字节信息 int offset = readBufferOffset, length = 0, position = buffer.position(); for (;;) { length = MySQLPacket.getPacketLength(buffer, offset); if (length == -1) { if (!buffer.hasRemaining()) { buffer = checkReadBuffer(buffer, offset, position); } break; } if (position >= offset + length) { buffer.position(offset); byte[] data = new byte[length]; buffer.get(data, 0, length); handle(data); offset += length; if (position == offset) { if (readBufferOffset != 0) { readBufferOffset = 0; } buffer.clear(); break; } else { readBufferOffset = offset; buffer.position(position); continue; } } else { if (!buffer.hasRemaining()) { buffer = checkReadBuffer(buffer, offset, position); } break; } } } private ByteBuffer checkReadBuffer(ByteBuffer buffer, int offset, int position) { if (offset == 0) { if (buffer.capacity() >= MySQLPacket.MAX_PACKET_SIZE) { throw new IllegalArgumentException("packet size over the limit."); } int size = buffer.capacity() << 1; size = (size > MySQLPacket.MAX_PACKET_SIZE) ? MySQLPacket.MAX_PACKET_SIZE : size; ByteBuffer newBuffer = allocate(size); buffer.position(offset); newBuffer.put(buffer); setReadBuffer(newBuffer); recycle(buffer); return newBuffer; } else { buffer.position(offset); buffer.compact(); readBufferOffset = 0; return buffer; } } public ByteBuffer checkWriteBuffer(ByteBuffer buffer, int capacity) { if (capacity > buffer.remaining()) { write(buffer); // 如果分配的buffer比要求的capacity还小,则按着capacity分配 int size = this.processor.getBufferPool().getChunkSize(); if (capacity > size) { size = capacity; } return allocate(capacity); } else { return buffer; } } public final void recycle(ByteBuffer buffer) { this.processor.getBufferPool().recycle(buffer); } public void check() { actor.check(); } public final void write(byte[] data) { ByteBuffer buffer = allocate(); buffer = writeToBuffer(data, buffer); write(buffer); } public final void write(ByteBuffer buffer) { writeQueue.offer(buffer); try { check(); } catch (Exception e) { this.close(e.getMessage()); } } public ByteBuffer allocate() { return processor.getBufferPool().allocate(); } public ByteBuffer allocate(int size) { return processor.getBufferPool().allocate(size); } public ByteBuffer writeToBuffer(byte[] src, ByteBuffer buffer) { int offset = 0; int length = src.length; int remaining = buffer.remaining(); while (length > 0) { if (remaining >= length) { buffer.put(src, offset, length); break; } else { buffer.put(src, offset, remaining); write(buffer); buffer = allocate();// 重新申请一个buffer offset += remaining; length -= remaining; remaining = buffer.remaining(); continue; } } return buffer; } public boolean isClosed() { return closed.get(); } public void cleanup() { if (channel != null) { try { channel.close(); } catch (Throwable e) {} } // 清理回收readBuffer if (getReadBuffer() != null) { recycle(getReadBuffer()); this.setReadBuffer(null); this.readBufferOffset = 0; } // 清理回收writeQueue中的writeBuffer if (this.getWriteBuffer() != null) { getWriteBuffer().clear(); recycle(getWriteBuffer()); setWriteBuffer(null); } ByteBuffer buffer = null; while ((buffer = writeQueue.poll()) != null) { buffer.clear(); recycle(buffer); } } public void setReadBuffer(ByteBuffer readBuffer) { this.readBuffer = readBuffer; } public ByteBuffer getWriteBuffer() { return writeBuffer; } public void setWriteBuffer(ByteBuffer writeBuffer) { this.writeBuffer = writeBuffer; } public Variables getVariables() { return variables; } public long getID() { return id; } public void setID(long id) { this.id = id; } public String getHost() { return host; } public void setHost(String ip) { this.host = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getLocalPort() { return localPort; } public NIOActor getActor() { return actor; } public void setState(int state) { this.state = state; } public void setHandler(NIOHandler handler) { this.handler = handler; } }