org.apache.tomcat.util.net包的内容都与网络连接和socket有关,比较主要和常见的是JIOEndpoint这个类,前面提到Coyote连接器的时候,就有涉及到JIOEndpoint,它用于监听某个socket端口、将socket对象交给coyote,并提供基本的线程池功能。除了JIOEndpoint,还有AprEndpoint、NioEndpoint等。由于对apr和nio不熟悉,所以只研究了一下JIOEndpoint

org.apache.tomcat.util.net.JIoEndpoint

JIOEndpoint其实和我们本科时上计算机网络或者分布式系统,做实验写的socket服务器差不多,结构也是经典的“Listen-Accept-Handle”,这里简单描述一下:JIOEndpoint使用JDK的ServerSocket类监听某个端口,有socket连接进来的时候便返回一个socket对象,交给专门的处理器。当然,具体的实现没那么简单,下面会按照socket的处理过程,详细说明其中的机理。

初始化

public void init()

throws Exception {

if (initialized)

return;

//

Initialize thread count defaults for acceptor

if

(acceptorThreadCount == 0) {

acceptorThreadCount = 1;

}

if (serverSocketFactory == null) {

serverSocketFactory = ServerSocketFactory.getDefault();

}

if (serverSocket == null) {

try {

if (address == null) {

serverSocket

= serverSocketFactory.createSocket(port, backlog);

} else {

serverSocket = serverSocketFactory.createSocket(port,

backlog, address);

}

} catch (BindException

be) {

throw new BindException(be.getMessage() + ":" + port);

}

}

//if( serverTimeout >= 0 )

//    serverSocket.setSoTimeout( serverTimeout );

initialized = true;

}

在这里,利用serverSocketFactory新建了一个serverSocket对象,用于监听特定的端口

启动JIOEndpoint

// Create worker collection

if

(executor == null) {

workers = new WorkerStack(maxThreads);

}

// Start acceptor threads

for (int i = 0; i < acceptorThreadCount; i++) {

Thread

acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);

acceptorThread.setPriority(threadPriority);

acceptorThread.setDaemon(daemon);

acceptorThread.start();

}

这里有几个类:worker,workerStack,Acceptor。这些都是JIOEndpoint的一些内部类,下面按照处理顺序依次讲述

Acceptor内部类

Acceptor实现了Runnable接口,只有一个方法run,做的事情就是通过ServerSocket.accept方法,得到socket,然后调用JIOEndpoint的processSocket方法

protected boolean processSocket(Socket socket) {

try {

if (executor == null) {

getWorkerThread().assign(socket);

} else {

executor.execute(new SocketProcessor(socket));

}

}

catch (Throwable t) {

// This means we got an OOM or similar

creating a thread, or that

// the pool and its queue are full

log.error(sm.getString("endpoint.process.fail"), t);

return false;

}

return true;

}

在这里,JIOEndpoint有两种处理socket的方式:使用JDK5的executor,或者内部的worker类。executor怎么用大家可以直接翻书了,我们继续讨论第二种方法。

首先我们要通过getWorkerThread()方法,得到一个Worker对象。具体逻辑是,看看WorkerStack(存放所有worker的一个堆栈)里面有没有空余的worker,有则直接拿来用,无则看看能不能新建一个worker线程,假如不能(比如超出了最大线程数限制),则返回null,那样就只能委屈一下这个acceptor,稍微等一下了(wait()方法),直到有新的worker可用时,通过notify方法唤醒等待的acceptor

/**

* Return a new worker thread, and block while to

worker is available.

*/

protected Worker getWorkerThread() {

// Allocate a new worker thread

Worker workerThread =

createWorkerThread();

while (workerThread == null) {

try {

synchronized (workers) {

workers.wait();

}

} catch

(InterruptedException e) {

// Ignore

}

workerThread = createWorkerThread();

}

return workerThread;

}

如下,当有worker被回收后,通知等待的acceptor

protected void recycleWorkerThread(Worker workerThread) {

synchronized (workers) {

workers.push(workerThread);

curThreadsBusy--;

workers.notify();

}

}

ok,回到前面的processSocket方法,得到worker后,通过worker.assign方法,将socket对象传递给worker

Worker内部类

Worker也实现了runnable接口,有三个方法:assign、await、start和run

所谓的start方法,就是new一个Thread对象,把worker自己传进去,我们知道这个thread就开始执行run方法了。

public void run() {

// Process requests until we

receive a shutdown signal

while (running) {

// Wait for the next socket to be assigned

Socket socket = await();

if (socket ==

null)

continue;

// Process the

request from this socket

if (!setSocketOptions(socket) ||

!handler.process(socket)) {

// Close socket

try {

socket.close();

} catch (IOException e) {

}

}

// Finish up this request

socket = null;

recycleWorkerThread(this);

}

}

run方法首先调用await方法

private synchronized Socket await() {

// Wait for the Connector to provide a new Socket

while (!available) {

try {

wait();

} catch

(InterruptedException e) {

}

}

// Notify the Connector that we have received this Socket

Socket socket = this.socket;

available = false;

notifyAll();

return (socket);

}

通过标记位available,如果当前的worker是“非available”的,则线程会开始等待。直到我们调用的assign方法,把一个可用的socket给worker后,才会notifyall,唤醒一个线程,进而取得assign过来的socekt

synchronized void assign(Socket socket) {

// Wait

for the Processor to get the previous Socket

while (available) {

try {

wait();

}

catch (InterruptedException e) {

}

}

// Store the newly available Socket and notify our thread

this.socket = socket;

available = true;

notifyAll();

}

所以,assign和await方法相当于生产者和消费者方法,两者通过available进行互斥,而this.socket则相当于被竞争的资源

现在,又回到Worker.run方法。在取得socket后,通过setSocketOptions(socket)方法设置socket的相关选项(例如超时值),最后通过handler.process(socket),终于把socket这个接力棒交给coyote了!

handler是什么?就是个简单的接口,如下:

/**

* Bare bones interface used

for socket processing. Per thread data is to be

* stored in the

ThreadWithAttributes extra folders, or alternately in

* thread local

fields.

*/

public interface Handler {

public

boolean process(Socket socket);

}

回顾一下org.apache.coyote.http11.Http11Protocol

的Http11ConnectionHandler内部类,实现的正是这个接口

java socket 缓冲_关于socket的发送缓冲区网上有诸多的讨论,这里个人小结一下,希望对以后有些帮助。首先,看下面一段代码,...相关推荐

  1. java 设备指纹_使用Socket In(JAVA)处理生物识别指纹考勤设备

    我正在尝试使用 Java程序连接生物识别指纹考勤设备(实际上我是新手!).我使用的设备是Biocom指纹考勤系统.但是,我正在搜索和阅读有关这一点,我看到SDK可以使用哪种基于设备类型(这很难,不合逻 ...

  2. mysql的socket文件_修改socket文件, MySQL启动报错

    事情是这样的, 我要搭建MySQL主从, 做读写分离, 然后就要了一台服务器搭建mysql, 最近犯懒, 就寻思搞一个二进制的吧直接启用 一堆问题就出现了 [ERROR] Can't start se ...

  3. python骂人代码大全_求助:没看懂其中一段代码的意思

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 在笨方法学python中有一段代码如下: def cheese_and_crackers(cheese_count, boxes_of_crackers) ...

  4. 深入理解阻塞socket和非阻塞socket

    什么是阻塞socket,什么是非阻塞socket.对于这个问题,我们要先弄清什么是阻塞/非阻塞.阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思是指,当试图对该文件描述符进行 ...

  5. linux send 失败_求助:sendto()发送UDP数据包失败:message too long该如何解决?

    求助:sendto()发送UDP数据包失败:message too long该如何解决? 发布时间:2012-03-24 23:17:25来源:红联作者:zhl2001xlh800 我想使用sendt ...

  6. java 引用传递_详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...

  7. jit java同步消除_聊聊JIT是如何影响JVM性能的

    花半秒钟就能看透事物本质的人,和花一辈子都看不清事物本质的人,注定是截然不同的命运 --教父 个人公众号:月伴飞鱼,欢迎关注 之前说好的这期讲解并发工具类,不过ReentrantLock源码还没肝完, ...

  8. java writelock 问题_【转】java并发编程系列之ReadWriteLock读写锁的使用

    前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读 ...

  9. TCP/UDP的接收缓冲区和发送缓冲区

    转载自:https://blog.csdn.net/Swallow_he/article/details/84392285 1.TCP. SO_RCVBUF & TCP. SO_SNDBUF ...

最新文章

  1. 发福利了|最近发现深圳有一家公司提供的叫8ms的GUI平台不错,好用,最主要所有功能都是免费的
  2. mysql between and 包含边界吗_10分钟让你明白MySQL是如何利用索引的
  3. win7 linux双系统win7启动不了怎么办,双系统windows打不开怎么办|苹果双系统win7打不开怎么解决|mac双系统打不开解决方法-系统城...
  4. 大家都为什么考博?采访了12名考生,发现最主要原因竟是这个
  5. SQL 关于apply的两种形式cross apply 和 outer apply(转)
  6. 顺子(51Nod-2510)
  7. python版本年份_Python问题:至今的年份和年份?
  8. python aes padding_使用PKCS7Padding在python和Node.js之间进行AES加密
  9. delphi 连接DBF
  10. 20191231每日一句
  11. 天刀找不到服务器,《天涯明月刀手游》好友服务器查看方法 怎么查看好友在哪个区...
  12. Excel密码保护怎么解密码
  13. 【学习笔记】人工智能相关概念
  14. 币小秘:这些年,见过的带单老师们,这里有没有你踩过的坑?
  15. 再谈中国的一些文字游戏
  16. 服务器接收协议,协议分析-服务器接收
  17. 网易云音乐为什么这么懂你?
  18. python脚本之批量查询网站权重2.0
  19. Ubuntu18.04配置、软件安装-搜狗输入法,网易云音乐
  20. Python代码制作Wifi万能钥匙,成功获取到隔壁邻居的Wifi密码

热门文章

  1. 如何战胜软件开发的复杂性?
  2. TIOBE 8 月编程语言排行榜:Python 奋力追赶 C,Swift 下跌
  3. 互联网寒冬资讯分析报告!如何在“大裁员”下逆势而为?
  4. 虚拟机才是 Kubernetes 的未来?
  5. 马云宣布退休计划后,阿里巴巴组织架构再次全面升级
  6. 程序员最想要十八般武艺俱全的“保姆型”项目经理!
  7. 颠覆网站 C/S 模式,没有服务器的网站会怎样?
  8. 技术人的 40 岁,真的不惑了吗?
  9. Java 10 正式发布!时隔 6 月带来 109 项新特性
  10. 手工制作机器人用彩泥_印度神奇芒果干制作过程,看一遍顶三遍,游客:不会再吃了...