package jane.test.net;
import java.io.IOException;
import java.net.SocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.Channel;
import java.nio.channels.CompletionHandler;
import java.util.ArrayDeque;
public final class TcpSession implements Channel
{
public static final int DEF_RECV_SOBUF_SIZE = 8192; // socket的接收缓冲区大小
public static final int DEF_SEND_SOBUF_SIZE = 8192; // socket的发送缓冲区大小
public static final int DEF_RECV_BUF_SIZE = 8192; // 每次接收的缓冲区大小
public static final int DEF_SEND_BUF_MAXSIZE = 1024576; // 发送累积数据量的上限(超过则触发异常导致断开)
public static final int CLOSE_ACTIVE = 0; // 调用close()主动断开
public static final int CLOSE_RECV = 1; // 无法接收数据而断开
public static final int CLOSE_SEND = 2; // 无法发送数据而断开
public static final int CLOSE_SEND_OVERFLOW = 3; // 发送数据积累超过上限而断开
public static final int CLOSE_EXCEPTION = 4; // 由于异常导致断开(响应onException后触发)
private final TcpManager _manager;
private final AsynchronousSocketChannel _channel;
private final ArrayDeque<ByteBuffer> _sendBuf = new ArrayDeque<>();
private final RecvHandler _recvHandler = new RecvHandler();
private final SendHandler _sendHandler = new SendHandler();
private volatile Object _userObject;
private final int _recvBufSize;
private final int _sendSoBufSize;
private int _sendBufMaxSize;
private int _sendBufSize;
TcpSession(TcpManager manager, AsynchronousSocketChannel channel, int recvBufSize) throws IOException
{
_manager = manager;
_channel = channel;
_recvBufSize = (recvBufSize > 0 ? recvBufSize : DEF_RECV_BUF_SIZE);
_sendSoBufSize = channel.getOption(StandardSocketOptions.SO_SNDBUF);
_sendBufMaxSize = DEF_SEND_BUF_MAXSIZE;
}
void beginRecv()
{
if(!_channel.isOpen()) return;
try
{
ByteBuffer bb = ByteBufferPool.def().allocateDirect(_recvBufSize);
_channel.read(bb, bb, _recvHandler);
}
catch(Throwable e)
{
_manager.doException(this, e);
close(CLOSE_EXCEPTION);
}
}
public int getSendQueueSize()
{
synchronized(_sendBuf)
{
return _sendBuf.size();
}
}
public int getSendBufSize()
{
return _sendBufSize; // dirty read
}
public int getSendBufMaxSize()
{
return _sendBufMaxSize;
}
public void setSendBufMaxSize(int sendBufMaxSize)
{
_sendBufMaxSize = sendBufMaxSize;
}
@Override
public boolean isOpen()
{
return _channel.isOpen();
}
public AsynchronousSocketChannel getChannel()
{
return _channel;
}
public SocketAddress getRemoteAddr()
{
try
{
return _channel.isOpen() ? _channel.getRemoteAddress() : null;
}
catch(Exception e)
{
return null;
}
}
public Object getUserObject()
{
return _userObject;
}
public void setUserObject(Object obj)
{
_userObject = obj;
}
/**
* 异步发送数据
* @param bb 发送内容是缓冲区的[position,limit]部分. 调用此方法以后会被回收,不能再使用
* @return 是否异步发送成功(不能确保真的发送出去,更无法确保对方收到)
*/
public boolean send(ByteBuffer bb)
{
if(!_channel.isOpen())
{
ByteBufferPool.def().free(bb);
return false;
}
int size = bb.remaining();
if(size <= 0)
{
ByteBufferPool.def().free(bb);
return true;
}
int overflowOldSize = Integer.MIN_VALUE;
synchronized(_sendBuf)
{
int newSize = _sendBufSize + size;
if(newSize <= _sendBufMaxSize && newSize >= 0)
{
_sendBufSize = newSize;
if(newSize != size) // 尚未完成上次发送
{
_sendBuf.addLast(bb);
return true;
}
}
else
overflowOldSize = _sendBufSize;
}
if(overflowOldSize != Integer.MIN_VALUE)
{
ByteBufferPool.def().free(bb);
_manager.doException(this, new IllegalStateException(String.format("send overflow(%d+%d)", overflowOldSize, size)));
close(CLOSE_SEND_OVERFLOW);
return false;
}
try
{
_channel.write(bb, bb, _sendHandler);
}
catch(Throwable e)
{
ByteBufferPool.def().free(bb);
_manager.doException(this, e);
close(CLOSE_EXCEPTION);
return false;
}
return true;
}
public void close(int reason)
{
_manager.closeChannel(_channel);
ByteBufferPool bbp = ByteBufferPool.def();
synchronized(_sendBuf)
{
for(ByteBuffer bb; (bb = _sendBuf.pollFirst()) != null;)
bbp.free(bb);
_sendBufSize = 0;
}
_manager.removeSession(this, reason);
}
@Override
public void close()
{
close(CLOSE_ACTIVE);
}
private final class RecvHandler implements CompletionHandler<Integer, ByteBuffer>
{
@Override
public void completed(Integer result, ByteBuffer bb)
{
int size = result.intValue();
if(size <= 0)
{
ByteBufferPool.def().free(bb);
close(CLOSE_RECV);
return;
}
try
{
bb.flip();
_manager.onReceived(TcpSession.this, bb);
bb.clear();
_channel.read(bb, bb, this);
}
catch(Throwable e)
{
ByteBufferPool.def().free(bb);
_manager.doException(TcpSession.this, e);
close(CLOSE_EXCEPTION);
}
}
@Override
public void failed(Throwable ex, ByteBuffer bb)
{
ByteBufferPool.def().free(bb);
_manager.doException(TcpSession.this, ex);
close(CLOSE_RECV);
}
}
private final class SendHandler implements CompletionHandler<Integer, ByteBuffer>
{
@Override
public void completed(Integer result, ByteBuffer bb)
{
int size = result.intValue();
if(size <= 0)
{
close(CLOSE_SEND);
ByteBufferPool.def().free(bb);
return;
}
int left = bb.remaining();
ByteBuffer bbNext;
synchronized(_sendBuf)
{
_sendBufSize -= size;
if(_sendBufSize < 0) // 以防清过buf后又处理到回调
_sendBufSize = 0;
if(left > 0)
{
bbNext = bb;
bb = null;
}
else
{
bbNext = _sendBuf.pollFirst();
if(bbNext != null && (left = bbNext.remaining()) < _sendSoBufSize) // 尝试合并小缓存,以提升发送效率
{
ByteBuffer bbNext2 = _sendBuf.peekFirst();
if(bbNext2 != null && (left += bbNext2.remaining()) <= _sendSoBufSize)
{
ByteBuffer bbTemp = bbNext;
bbNext = ByteBufferPool.def().allocateDirect(_sendSoBufSize).put(bbNext);
ByteBufferPool.def().free(bbTemp);
do
{
_sendBuf.pollFirst();
bbNext.put(bbNext2);
ByteBufferPool.def().free(bbNext2);
}
while((bbNext2 = _sendBuf.peekFirst()) != null && (left += bbNext2.remaining()) <= _sendSoBufSize);
bbNext.flip();
}
}
}
}
if(bb != null)
{
if(_manager._enableOnSend)
{
try
{
_manager.onSent(TcpSession.this, bb, bbNext);
}
catch(Throwable e)
{
_manager.doException(TcpSession.this, e);
close(CLOSE_EXCEPTION);
}
}
ByteBufferPool.def().free(bb);
}
if(bbNext != null && _channel.isOpen())
{
try
{
_channel.write(bbNext, bbNext, _sendHandler);
}
catch(Throwable e)
{
_manager.doException(TcpSession.this, e);
close(CLOSE_EXCEPTION);
}
}
}
@Override
public void failed(Throwable ex, ByteBuffer bb)
{
ByteBufferPool.def().free(bb);
_manager.doException(TcpSession.this, ex);
close(CLOSE_SEND);
}
}
}