在讲监听器时说过处理的新的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的更多相关文章

  1. 《用Java写一个通用的服务器程序》01 综述

    最近一两年用C++写了好几个基于TCP通信类型程序,都是写一个小型的服务器,监听请求,解析自定义的协议,处理请求,返回结果.每次写新程序时都把老代码拿来,修改一下协议解析部分和业务处理部分,然后就一个 ...

  2. 《用Java写一个通用的服务器程序》02 监听器

    在一个服务器程序中,监听器的作用类似于公司前台,起引导作用,因此监听器花在每个新连接上的时间应该尽可能短,这样才能保证最快响应. 回到编程本身来说: 1. 监听器最好由单独的线程运行 2. 监听器在接 ...

  3. 五:用JAVA写一个阿里云VPC Open API调用程序

    用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...

  4. 用JAVA写一个多线程程序,写四个线程,其中二个对一个变量加1,另外二个对一个变量减1

    package com.ljn.base; /** * @author lijinnan * @date:2013-9-12 上午9:55:32 */ public class IncDecThrea ...

  5. CBrother脚本10分钟写一个拯救“小霸王服务器”的程序

    CBrother脚本语言10分钟写一个拯救“小霸王服务器”的程序 到了一家新公司,接手了一坨c++服务器代码,到处内存泄漏,这服务器没有数据库,挂了后重启一下就好了,公司就这么凑活着用了几年了,定时重 ...

  6. (原创)如何使用boost.asio写一个简单的通信程序(一)

    boost.asio相信很多人听说过,作为一个跨平台的通信库,它的性能是很出色的,然而它却谈不上好用,里面有很多地方稍不注意就会出错,要正确的用好asio还是需要花一番精力去学习和实践的,本文将通过介 ...

  7. 用JAVA写一个函数,功能例如以下: 随意给定一组数, 找出随意数相加之后的结果为35(随意设定)的情况

    用JAVA写一个函数.功能例如以下:随意给定一组数,比如{12,60,-8,99,15,35,17,18},找出随意数相加之后的结果为35(随意设定)的情况. 能够递归算法来解: package te ...

  8. 用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载

    用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载,将一个完整的项目进行展示,主要有以下几个部分: 1.servlet部分   Export 2.工具类:TxtFileU ...

  9. 使用JAVA写一个简单的日历

    JAVA写一个简单的日历import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateF ...

随机推荐

  1. Python学习笔记(六)

    Python学习笔记(六) Ubuntu重置root密码 Ubuntu 16.4 目录结构 Ubuntu 命令讲解 1. Ubuntu重置root密码 启动系统,显示GRUB选择菜单(如果默认系统启动 ...

  2. NOIP2012疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  3. 在unity3d游戏中添加中文语音控制

    最近打算尝试一下OLAMI在游戏中应用的可能性,这里做一下记录. unity官方教程中的几个项目很精简,但看起来很不错,里面有全套的资源.最后我选择了tanks-tutorial来做这个实验. 下载和 ...

  4. MyBatis 开发记录

    1.在classpath下创建log4j.properties # Global logging configuration log4j.rootLogger=DEBUG, stdout # Cons ...

  5. C#用到windows 消息列表Message类MSG的id代号

    C# Constants:private const UInt32 WM_ACTIVATE = 0x0006;private const UInt32 WM_ACTIVATEAPP = 0x001C; ...

  6. tomcat启动报错Several ports (8080, 8009) required by Tomcat v6.0

    tomcat启动报错 如下图: 问题:8080.8009端口已经被占用. 解决办法: 1.在命令提示符下,输入netstat -aon | findstr 8080 2.继续输入taskkill -F ...

  7. c语言15行实现简易cat命令

    刚刚和舍友打赌.舍友说PY20行能做xlsx文件分析整理,C20行屁都干不了.我说简单的cat还是能做的嘛.他说不信.我说不处理非文件的参数的话10行能做啊. 下面直接贴代码吧: #include & ...

  8. 微信小程序支付

    @Controllerpublic class UserPayToMerchantController { public static final String appid="******* ...

  9. Python 之简单线程池创建

    try: from Queue import Queue, Empty except: from queue import Queue, Empty import threading import t ...

  10. 版本12.2.0.1.0数据库,复制种子数据库快速创建租户数据库PDB

    实验测试:快速创建一个数据库PDB2: 实验环境:12.2.0.1.0版本数据库,dbca图形化安装,现有环境,CDB容器数据库ORCL,PDB可插拔数据库ABC   ---查询CDB名称,状态 SQ ...