客户端session初始化完后,就可以写数据了:session.write("Hello,Server!I'm Client.");
AbstractIoSession
public WriteFuture write(Object message) {
return write(message, null);
}
转调
public WriteFuture write(Object message, SocketAddress remoteAddress) {
if (message == null) {
throw new IllegalArgumentException("Trying to write a null message : not allowed");
}
// We can't send a message to a connected session if we don't have
// the remote address
if (!getTransportMetadata().isConnectionless() && (remoteAddress != null)) {
throw new UnsupportedOperationException();
}
// If the session has been closed or is closing, we can't either
// send a message to the remote side. We generate a future
// containing an exception.
if (isClosing() || !isConnected()) {
WriteFuture future = new DefaultWriteFuture(this);
WriteRequest request = new DefaultWriteRequest(message, future, remoteAddress);
WriteException writeException = new WriteToClosedSessionException(request);
future.setException(writeException);
return future;
}
FileChannel openedFileChannel = null;
// TODO: remove this code as soon as we use InputStream
// instead of Object for the message.
try {
if ((message instanceof IoBuffer) && !((IoBuffer) message).hasRemaining()) {
// Nothing to write : probably an error in the user code
throw new IllegalArgumentException("message is empty. Forgot to call flip()?");
} else if (message instanceof FileChannel) {
FileChannel fileChannel = (FileChannel) message;
message = new DefaultFileRegion(fileChannel, 0, fileChannel.size());
} else if (message instanceof File) {
File file = (File) message;
openedFileChannel = new FileInputStream(file).getChannel();
message = new FilenameFileRegion(file, openedFileChannel, 0, openedFileChannel.size());
}
} catch (IOException e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
return DefaultWriteFuture.newNotWrittenFuture(this, e);
}
// Now, we can write the message. First, create a future
WriteFuture writeFuture = new DefaultWriteFuture(this);
WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);
// Then, get the chain and inject the WriteRequest into it
IoFilterChain filterChain = getFilterChain();
filterChain.fireFilterWrite(writeRequest);
// TODO : This is not our business ! The caller has created a
// FileChannel,
// he has to close it !
if (openedFileChannel != null) {
// If we opened a FileChannel, it needs to be closed when the write
// has completed
final FileChannel finalChannel = openedFileChannel;
writeFuture.addListener(new IoFutureListener<WriteFuture>() {
public void operationComplete(WriteFuture future) {
try {
finalChannel.close();
} catch (IOException e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
}
});
}
// Return the WriteFuture.
return writeFuture;
}
将Session、发送的数据message封装成WriteFuture和WriteRequest对象。
再调用过滤器链filterChain对发送的数据进行过滤。
一、过滤器链是如何创建的?
IoFilterChain filterChain = getFilterChain();NioSession类
public IoFilterChain getFilterChain() {
return filterChain;
}
1、filterChain在其构造方法实例化的DefaultIoFilterChain类
protected NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel) {
super(service);
this.channel = channel;
this.processor = processor;
filterChain = new DefaultIoFilterChain(this);
}
DefaultIoFilterChain类构造方法
public DefaultIoFilterChain(AbstractIoSession session) {
if (session == null) {
throw new IllegalArgumentException("session");
}
this.session = session;
head = new EntryImpl(null, null, "head", new HeadFilter());
tail = new EntryImpl(head, null, "tail", new TailFilter());
head.nextEntry = tail;
}
EntryImpl是DefaultIoFilterChain的内部类。通过其构造方法就可以知道:保存着某个过滤器,及其前后过滤器。
private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter)
默认创建了过滤器链的首位、末尾过滤器,用户添加的过滤器将按顺序被添加到这两个过滤器中间。
2、回到例子代码中,有这句
acceptor.getFilterChain().addLast("codec", filter);
这个filter是IoFilter子类ProtocolCodecFilter的对象。
NioSocketSession中有filterChain保存的是过滤器链,而NioSocketAcceptor有filterChainBuilder是为了创建session中的filterChain。
这个filterChainBuilder也是在代码中可以配置的,如果没配置则默认创建一个DefaultIoFilterChainBuilder类的对象。
acceptor.getFilterChain()也是返回其字段filterChainBuilder(必须为DefaultIoFilterChainBuilder类对象)和sesssion的同名方法返回的对象不一样。
DefaultIoFilterChainBuilder.addLast()
public synchronized void addLast(String name, IoFilter filter) {
register(entries.size(), new EntryImpl(name, filter));
}
register
private void register(int index, Entry e) {
if (contains(e.getName())) {
throw new IllegalArgumentException("Other filter is using the same name: " + e.getName());
}
entries.add(index, e);
}
entries是DefaultIoFilterChainBuilder的字段,为CopyOnWriteArrayList类的对象。
先将业务定义的所有过滤器按顺序添加到entries中。
3、等到初始化session时,AbstractPollingIoProcessor中的addNow()方法中,
IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
chainBuilder.buildFilterChain(session.getFilterChain());
session.getService()方法返回的是NioSocketAcceptor对象,chainBuilder就是其字段filterChainBuilder——DefaultIoFilterChainBuilder对象
DefaultIoFilterChainBuilder.buildFilterChain()
public void buildFilterChain(IoFilterChain chain) throws Exception {
for (Entry e : entries) {
chain.addLast(e.getName(), e.getFilter());
}
}
这里就往session的filterChain中不断的添加,之前保存在entries字段中的过滤器。
DefaultIoFilterChain.addLast();
public synchronized void addLast(String name, IoFilter filter) {
checkAddable(name);
register(tail.prevEntry, name, filter);
}
register()
private void register(EntryImpl prevEntry, String name, IoFilter filter) {
EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);
try {
filter.onPreAdd(this, name, newEntry.getNextFilter());
} catch (Exception e) {
throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + filter + " in " + getSession(), e);
}
prevEntry.nextEntry.prevEntry = newEntry;
prevEntry.nextEntry = newEntry;
name2entry.put(name, newEntry);
try {
filter.onPostAdd(this, name, newEntry.getNextFilter());
} catch (Exception e) {
deregister0(newEntry);
throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + filter + " in " + getSession(), e);
}
}
这里就是,将需要添加的filter添加到末位过滤器前。并且保存到name2entry中,以备下次判断是否添加过了。另外再改变改filter前后,都会调用其相应的方法,做些业务上的处理。
二、如何执行过滤器链?
filterChain.fireFilterWrite(writeRequest);
DefaultIoFilterChain.fireFilterWrite()
public void fireFilterWrite(WriteRequest writeRequest) {
Entry tail = this.tail;
callPreviousFilterWrite(tail, session, writeRequest);
}
从过滤器链的末位过滤器开始往前执行。
private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {
try {
IoFilter filter = entry.getFilter();
NextFilter nextFilter = entry.getNextFilter();
filter.filterWrite(nextFilter, session, writeRequest);
} catch (Throwable e) {
writeRequest.getFuture().setException(e);
fireExceptionCaught(e);
}
}
entry.getNextFilter();返回的是EntryImpl内部匿名类NextFilter的对象(EntryImpl是DefaultIoFilterChain的内部类)
filter.filterWrite(nextFilter, session, writeRequest);
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
nextFilter.filterWrite(session, writeRequest);
}
NextFilter.filterWrite
public void filterWrite(IoSession session, WriteRequest writeRequest) {
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterWrite(nextEntry, session, writeRequest);
}
prevEntry就是tail的上一个节点,再迭代上一个过滤器的filterWrite方法。有内部类时,EntryImpl.this表示内部类对象,如果要取外部类的对象:外部类名.this即可。
而我们自己定义的过滤器,其中必定有一条语句调用上一个过滤器的方法:nextFilter.filterWrite(session, writeRequest);不然这个链就断了。
最后会执行到HeadFilter
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
AbstractIoSession s = (AbstractIoSession) session;
// Maintain counters.
if (writeRequest.getMessage() instanceof IoBuffer) {
IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
// I/O processor implementation will call buffer.reset()
// it after the write operation is finished, because
// the buffer will be specified with messageSent event.
buffer.mark();
int remaining = buffer.remaining();
if (remaining == 0) {
// Zero-sized buffer means the internal message
// delimiter.
s.increaseScheduledWriteMessages();
} else {
s.increaseScheduledWriteBytes(remaining);
}
} else {
s.increaseScheduledWriteMessages();
}
WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
if (!s.isWriteSuspended()) {
if (writeRequestQueue.size() == 0) {
// We can write directly the message
s.getProcessor().write(s, writeRequest);
} else {
s.getWriteRequestQueue().offer(s, writeRequest);
s.getProcessor().flush(s);
}
} else {
s.getWriteRequestQueue().offer(s, writeRequest);
}
}
1、如果写入的参数是IoBuffer类型。则需要标记Buffer中剩下空间是多少,这在后面往通道中写数据会用到。
2、WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
这是值时在初始化session是设值的。NioSocketConnector父类AbstractPollingIoConnector.initSession方法中
((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()
.getWriteRequestQueue(session));
值为DefaultIoSessionDataStructureFactory内部类DefaultWriteRequestQueue对象。DefaultWriteRequestQueue又是封装了ConcurrentLinkedQueue的。
3、s.isWriteSuspended()返回false,只要不在业务代码中调用session的suspendWrite方法设值暂停写操作,都会返回false。
4、判断writeRequestQueue保存了多少写操作的请求。刚开始为0,执行s.getProcessor().write(s, writeRequest);
public void write(S session, WriteRequest writeRequest) {
WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
writeRequestQueue.offer(session, writeRequest);
if (!session.isWriteSuspended()) {
this.flush(session);
}
}
将这条写请求保存到writeRequestQueue中,如果下次再有写请求进来就会执行。
s.getWriteRequestQueue().offer(s, writeRequest);
s.getProcessor().flush(s);
不管writeRequestQueue中的写请求多少都会先将其压入队中,writeRequestQueue.offer()方法。
public synchronized void offer(IoSession session, WriteRequest writeRequest) {
q.offer(writeRequest);
}
session这个参数没用到了
再调用SimpleIoProcessorPool的flush方法。
5、SimpleIoProcessorPool.flush
public final void flush(S session) {
getProcessor(session).flush(session);
}
转调NioProcessor父类AbstractPollingIoProcessor的flush()方法,将session添加到flushingSessions中。
public final void flush(S session) {
// add the session to the queue if it's not already
// in the queue, then wake up the select()
if (session.setScheduledForFlush(true)) {
flushingSessions.add(session);
wakeup();
}
}
wakeup是唤醒,AbstractPollingIoProcessor内部类Processor的select阻塞。
6、Processor.run()中,前面部分都是读数据的操作,因为不是读请求,所以都不会执行。直到flush(currentTime);
private void flush(long currentTime) {
if (flushingSessions.isEmpty()) {
return;
}
do {
S session = flushingSessions.poll(); // the same one with firstSession
if (session == null) {
// Just in case ... It should not happen.
break;
}
// Reset the Schedule for flush flag for this session,
// as we are flushing it now
session.unscheduledForFlush();
SessionState state = getState(session);
switch (state) {
case OPENED:
try {
boolean flushedAll = flushNow(session, currentTime);
if (flushedAll && !session.getWriteRequestQueue().isEmpty(session)
&& !session.isScheduledForFlush()) {
scheduleFlush(session);
}
} catch (Exception e) {
scheduleRemove(session);
IoFilterChain filterChain = session.getFilterChain();
filterChain.fireExceptionCaught(e);
}
break;
case CLOSING:
// Skip if the channel is already closed.
break;
case OPENING:
// Retry later if session is not yet fully initialized.
// (In case that Session.write() is called before addSession()
// is processed)
scheduleFlush(session);
return;
default:
throw new IllegalStateException(String.valueOf(state));
}
} while (!flushingSessions.isEmpty());
}
遍历flushingSessions队列,逐个写,flushNow()
private boolean flushNow(S session, long currentTime) {
if (!session.isConnected()) {
scheduleRemove(session);
return false;
}
final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();
final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
// Set limitation for the number of written bytes for read-write
// fairness. I used maxReadBufferSize * 3 / 2, which yields best
// performance in my experience while not breaking fairness much.
final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize()
+ (session.getConfig().getMaxReadBufferSize() >>> 1);
int writtenBytes = 0;
WriteRequest req = null;
try {
// Clear OP_WRITE
setInterestedInWrite(session, false);
do {
// Check for pending writes.
req = session.getCurrentWriteRequest();
if (req == null) {
req = writeRequestQueue.poll(session);
if (req == null) {
break;
}
session.setCurrentWriteRequest(req);
}
int localWrittenBytes = 0;
Object message = req.getMessage();
if (message instanceof IoBuffer) {
localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,
currentTime);
if ((localWrittenBytes > 0) && ((IoBuffer) message).hasRemaining()) {
// the buffer isn't empty, we re-interest it in writing
writtenBytes += localWrittenBytes;
setInterestedInWrite(session, true);
return false;
}
} else if (message instanceof FileRegion) {
localWrittenBytes = writeFile(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,
currentTime);
// Fix for Java bug on Linux
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
// If there's still data to be written in the FileRegion,
// return 0 indicating that we need
// to pause until writing may resume.
if ((localWrittenBytes > 0) && (((FileRegion) message).getRemainingBytes() > 0)) {
writtenBytes += localWrittenBytes;
setInterestedInWrite(session, true);
return false;
}
} else {
throw new IllegalStateException("Don't know how to handle message of type '"
+ message.getClass().getName() + "'. Are you missing a protocol encoder?");
}
if (localWrittenBytes == 0) {
// Kernel buffer is full.
setInterestedInWrite(session, true);
return false;
}
writtenBytes += localWrittenBytes;
if (writtenBytes >= maxWrittenBytes) {
// Wrote too much
scheduleFlush(session);
return false;
}
} while (writtenBytes < maxWrittenBytes);
} catch (Exception e) {
if (req != null) {
req.getFuture().setException(e);
}
IoFilterChain filterChain = session.getFilterChain();
filterChain.fireExceptionCaught(e);
return false;
}
return true;
}
先清除session上的OP_WRITE,等写完后再注册监听OP_WRITE。localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,currentTime);
private int writeBuffer(S session, WriteRequest req, boolean hasFragmentation, int maxLength, long currentTime)
throws Exception {
IoBuffer buf = (IoBuffer) req.getMessage();
int localWrittenBytes = 0;
if (buf.hasRemaining()) {
int length;
if (hasFragmentation) {
length = Math.min(buf.remaining(), maxLength);
} else {
length = buf.remaining();
}
try {
localWrittenBytes = write(session, buf, length);
} catch (IOException ioe) {
// We have had an issue while trying to send data to the
// peer : let's close the session.
session.close(true);
}
}
session.increaseWrittenBytes(localWrittenBytes, currentTime);
if (!buf.hasRemaining() || (!hasFragmentation && (localWrittenBytes != 0))) {
// Buffer has been sent, clear the current request.
int pos = buf.position();
buf.reset();
fireMessageSent(session, req);
// And set it back to its position
buf.position(pos);
}
return localWrittenBytes;
}
localWrittenBytes = write(session, buf, length);在其子类NioProcessor中
protected int write(NioSession session, IoBuffer buf, int length) throws Exception {
if (buf.remaining() <= length) {
return session.getChannel().write(buf.buf());
}
int oldLimit = buf.limit();
buf.limit(buf.position() + length);
try {
return session.getChannel().write(buf.buf());
} finally {
buf.limit(oldLimit);
}
}
往channel中写数据buf,如果buf超过了最大限制的话,那么就会写入最大限制的数据,然后将这个session加到flushingSessions队中,接着写这些这个buf。直到下完为止。