首页 > 代码库 > 《用Java写一个通用的服务器程序》03 处理新socket
《用Java写一个通用的服务器程序》03 处理新socket
在讲监听器时说过处理的新的socket要尽快返回,监听器调用的是ClientFactory的createPhysicalConnection方法,那么就来看这个方法:
public boolean createPhysicalConnection(PushClientSocket socket, boolean isObserver, ListenerOptions listenerOptions) { PhysicalConnectionPool thePhysicalConnectionPool = serverImpl.getPhysicalConnectionPool(); IOQueue<PhysicalConnection> ioQueue = serverImpl.getIOQueue(); // 内置了一个PhysicalConnection的对象池,这样可以避免每次都要 // 创建PhysicalConnection对象,可以加快处理速度 PhysicalConnection connection = thePhysicalConnectionPool.borrowObject(); // 把PhysicalConnection对象和socket对象关联起来 connection.reset(socket, isObserver, listenerOptions); // 初始化协议,分配buffer,用来缓存解析请求时的数据 if (!connection.setUpProtocolContexts()) { thePhysicalConnectionPool.returnObject(connection); return false; } Debug.debug("Physical Connection Created for client from: " + socket.getIP()); // 把连接注册到I/O队列中,这样就可以监听请求 if (!ioQueue.addSocketContext(socket, connection)) { thePhysicalConnectionPool.returnObject(connection); //leave socket close to acceptor return false; } Debug.debug("Queue adds client from: " + socket.getIP()); // 把创建的PhysicalConnection加入pending队列中,此时连接 // 还不算是真正的已连接状态,要等到第一个请求到达并正确 // 处理之后才会是已连接状态,并且会创建一个LogicalConnection // 和这个PhysicalConnection相关联 addPhysicalConnection(connection); // 初始化PhysicalConnection serverImpl.getDispatcher().handleInitialize(connection); return true; }
ClientFactory是PhysicalConnection的管理程序,这个方法的的作用就是创建PhysicalConnection和新的socket相关联,并且把PhysicalConnection加入请求监听的I/O队列。因此来说说IOQueue。
IOQueue本身是一个接口:
public interface IOQueue<T> { public boolean create(); public void free(); // 从队列中获取事件,默认实现是带有阻塞超时的,即当没有事件 // 时会阻塞一段时间,超时就会返回null public IOEvent<T> getQueuedEvent(boolean isInputEvents); // 注册连接,context是关联对象,类似于附件 public boolean addSocketContext(PushClientSocket socket, T context); // 取消注册 public void deleteSocketContext(PushClientSocket socket); // IOQueue的事件监听是一次性,这是为了防止事件在没有被处理之前,这个事件 // 再次被捕捉到(Java的read/write事件都是这样),因此这个方法会在事件 // 被处理后调用,再次注册。 public boolean rearmSocketForWrite(PushClientSocket socket, T context); // Read事件代表的就是从客户端有数据过来 public boolean rearmSocketForRead(PushClientSocket socket, T context);}
IOQueueImpl是IOQueue的Java NIO版本的实现。IOQueueImpl会内置一个独立线程以及一个Selector,这里关于注册有一点需要说明:
PushClientSocketImpl的registerSelector方法用于注册socket,这里需要调用wakeup方法。因为如果独立线程会调用Selector的select方法等待新的数据,这个时候直接
调用register方法会被阻塞,因此需要先调用wakeup唤醒selector。
public SelectionKey registerSelector(Selector selector, int ops, Object attachment) throws IOException { selector.wakeup(); // To prevent block when calling register method return channel.register(selector, ops, attachment); }
接着说说独立线程监听事件,因为OP_WRITE的特殊性,这里只监听OP_READ事件。
private void pollEvents(boolean isOutPoll) { Selector selector; BlockingQueue<SelectionKey> queue; if (isOutPoll) { return; } else { selector = this.inPollSelector; queue = this.inPollQueue; } List<SelectionKey> cache = new LinkedList<SelectionKey>(); while (isPolling) { try { selector.select(); } catch (IOException e) { continue; } // 这里调用yield释放控制权是为了刚刚提到的register方法能被顺利执行 Thread.yield(); // Add into cache (Add into the blocking queue directly // may block so that the selector cannot release the selection // key in time) if (selector.isOpen()) { for (SelectionKey key : selector.selectedKeys()) { // 前面提到监听事件是一次性的,因此这里取消监听 // 后面再调用rearm方法重新注册 key.cancel(); cache.add(key); } // Clear the keys selector.selectedKeys().clear(); // 因为使用了限定长度的BlockingQueue,可能因为队列已满导致阻塞 // 因此先把事件转移到缓存中,释放Selector queue.addAll(cache); cache.clear(); } else { break; // selector closed } } }
顺便说说Demultiplexor获取事件调用的getQueuedEvent方法,这里使用BlockingQueue来实现阻塞等待:
public IOEvent<PhysicalConnection> getQueuedEvent(boolean isInputEvents) { final IOEventType type; final BlockingQueue<SelectionKey> pollQueue; if (isInputEvents) { type = IOEventType.read; pollQueue = inPollQueue; } else { type = null; pollQueue = null; } if (pollQueue == null) { return null; } try { // 设置1秒的超时,这样后面关闭时清空I/O队列的时候不会导致 // Demultiplexor一直被阻塞 SelectionKey key = pollQueue.poll(1000L, TimeUnit.MILLISECONDS); if (key != null) { if (key.attachment() instanceof PhysicalConnection) { return new IOEvent<PhysicalConnection>(type, (PhysicalConnection)(key.attachment())); } } } catch (InterruptedException e) { // Ignore } return null; }
关于新socket的处理就说这么多吧。
《用Java写一个通用的服务器程序》03 处理新socket
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。