《用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的更多相关文章
- 《用Java写一个通用的服务器程序》01 综述
最近一两年用C++写了好几个基于TCP通信类型程序,都是写一个小型的服务器,监听请求,解析自定义的协议,处理请求,返回结果.每次写新程序时都把老代码拿来,修改一下协议解析部分和业务处理部分,然后就一个 ...
- 《用Java写一个通用的服务器程序》02 监听器
在一个服务器程序中,监听器的作用类似于公司前台,起引导作用,因此监听器花在每个新连接上的时间应该尽可能短,这样才能保证最快响应. 回到编程本身来说: 1. 监听器最好由单独的线程运行 2. 监听器在接 ...
- 五:用JAVA写一个阿里云VPC Open API调用程序
用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...
- 用JAVA写一个多线程程序,写四个线程,其中二个对一个变量加1,另外二个对一个变量减1
package com.ljn.base; /** * @author lijinnan * @date:2013-9-12 上午9:55:32 */ public class IncDecThrea ...
- CBrother脚本10分钟写一个拯救“小霸王服务器”的程序
CBrother脚本语言10分钟写一个拯救“小霸王服务器”的程序 到了一家新公司,接手了一坨c++服务器代码,到处内存泄漏,这服务器没有数据库,挂了后重启一下就好了,公司就这么凑活着用了几年了,定时重 ...
- (原创)如何使用boost.asio写一个简单的通信程序(一)
boost.asio相信很多人听说过,作为一个跨平台的通信库,它的性能是很出色的,然而它却谈不上好用,里面有很多地方稍不注意就会出错,要正确的用好asio还是需要花一番精力去学习和实践的,本文将通过介 ...
- 用JAVA写一个函数,功能例如以下: 随意给定一组数, 找出随意数相加之后的结果为35(随意设定)的情况
用JAVA写一个函数.功能例如以下:随意给定一组数,比如{12,60,-8,99,15,35,17,18},找出随意数相加之后的结果为35(随意设定)的情况. 能够递归算法来解: package te ...
- 用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载
用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载,将一个完整的项目进行展示,主要有以下几个部分: 1.servlet部分 Export 2.工具类:TxtFileU ...
- 使用JAVA写一个简单的日历
JAVA写一个简单的日历import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateF ...
随机推荐
- 文本可视化[二]——《今生今世》人物关系可视化python实现
文本可视化[二]--<今生今世>人物关系可视化python实现 在文本可视化[一]--<今生今世>词云生成与小说分析一文中,我使用了jieba分词和wordcloud实现了,文 ...
- JQuery-基础学习1
1)JQuery语法 jquery语法是为HTML元素的选取编制,可以对元素执行某些操作. 基础语法是:$(selector).action() 美元符号定义JQuery 选择符(selector)& ...
- [hihoCoder]矩形判断
#1040 : 矩形判断 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 给出平面上4条线段,判断这4条线段是否恰好围成一个面积大于0的矩形. 输入 输入第一行是一个整数T ...
- LeetCode 81. Search in Rotated Sorted Array II(在旋转有序序列中搜索之二)
Follow up for "Search in Rotated Sorted Array":What if duplicates are allowed? Would this ...
- 【JAVA零基础入门系列】Day11 Java中的类和对象
今天要说的是Java中两个非常重要的概念--类和对象. 什么是类,什么又是对象呢?类是对特定集合的概括描述,比如,人,这个类,外观特征上,有名字,有年龄,能说话,能吃饭等等,这是我们作为人类的相同特征 ...
- fastDFS文件服务器迁移
在实际的项目应用中,由于服务器替换或项目变更难免会存在fastDFS文件服务器迁移的工作.本文重点介绍fastDFS文件系统在不同情况下的文件迁移处理方案. 1.迁移时IP地址不变 通过文件服务器存储 ...
- hibernate的集合映射(详细笔记)
- gulp-prompt入个了门
gulp-prompt版本:0.4.1 源码:gulp-prompt 一.gulp-prompt的简介 gulp-prompt 是一个基于gulp的命令行提示. 我们可以用它来完成命令行中互动功能. ...
- 对Numpy广播操作的理解
1.首先检查两个矩阵维数是否相同,若不同,对维数少的补一.注意这里的维数不是指n行d列中的n和d的值,对于这种情况维数就是2.若一个两维的矩阵(n,d)和一个一维的数组(m,)相乘,补一操作就是将那个 ...
- Linux学习(十四)磁盘格式化、磁盘挂载、手动增加swap空间
一.磁盘格式化 分好去的磁盘需要格式化之后才可以使用.磁盘分区一般用mke2fs命令或者mkfs.filesystemtype.这个filesystemtype分为ext4,ext3,xfs等等.xf ...