<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>-Flyぁ梦- &#187; Socket</title>
	<atom:link href="http://blog.11034.org/tag/socket/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.11034.org</link>
	<description></description>
	<lastBuildDate>Sun, 22 Jun 2025 08:59:05 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>Java客户端Socket在服务端重启后的异常情况处理</title>
		<link>http://blog.11034.org/2016-03/java_client_socket_exceptions.html</link>
		<comments>http://blog.11034.org/2016-03/java_client_socket_exceptions.html#comments</comments>
		<pubDate>Thu, 17 Mar 2016 11:25:04 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Socket]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2799</guid>
		<description><![CDATA[碰到场景如下：Java服务器端A（使用NIO的异步IO方式），Java客户端B（使用普通IO的同步方式），在服 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>碰到场景如下：Java服务器端A（使用NIO的异步IO方式），Java客户端B（使用普通IO的同步方式），在服务端A重启后，B-&gt;A的Socket毫无疑问的断了，然后就会出现一些异常情况，分析一下。</p>
<p><span id="more-2799"></span></p>
<h2>同步IO客户端既发也收</h2>
<p>在服务端A重启后，B再次发送数据到A（虽然socket连接已断，但这个过程无异常，但其实数据并无法发送），然后B从A读数据的结果中，<code class="markdown_inline_code">inputStream.read(byte[] array)会返回-1</code>，表示end-of-file。</p>
<p>这种情况就比较好处理，在底层获取到end-of-file后抛出个异常，上层捕获异常后通知底层进行新建socket重连，然后上层再重发数据再读回包即可。</p>
<h2>同步IO客户端只发不收</h2>
<p>在服务端A重启后，B尝试第一次发送数据到A（虽然socket连接已断，但这个过程无异常，检测socket.isConnected()=true或者socket.isClosed()=false也都显示正常，但其实数据并无法发送），当B第二次再发送数据到A，在outputStream.write中，会报异常<code class="markdown_inline_code">java.net.SocketException: Broken pipe</code>。</p>
<p>所以通过抓取异常重连重发的办法，要在第二次发送失败时才能被发现并work。</p>
<h2>客户端不发只收</h2>
<p>这种情况在实践中感觉比较少见，即使业务逻辑如此，客户端应该也要发送心跳包到服务器，然后就是 既发也收 了。</p>
<h2>异步IO客户端</h2>
<p>如果客户端也是NIO的异步IO方式，在服务端A正常关闭时，客户端B会接收到SelectionKey.isReadable() == true的通知，然后从SocketChannel中read返回-1值，表示EOF，就可以处理关闭链接，然后下一次write的时候就可以进行重连了。</p>
<p>如果是服务端非正常关闭，比如断电、宕机等情况，有待验证，理论上应该不会返回read通知也不会返回-1的结果。</p>
<h4  class="related_post_title">看看 Socket</h4><ul class="related_post"><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/java_client_socket_exceptions.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tomcat的Connector：Coyote</title>
		<link>http://blog.11034.org/2013-05/coyote.html</link>
		<comments>http://blog.11034.org/2013-05/coyote.html#comments</comments>
		<pubDate>Mon, 20 May 2013 14:01:09 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[coyote]]></category>
		<category><![CDATA[Socket]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[异步IO]]></category>
		<category><![CDATA[线程池]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1808</guid>
		<description><![CDATA[上一篇博文讲了Tomcat的系统架构，今天花了一天时间研究了下coyote的源码，大致对普通IO版本有所了解。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>上一篇博文讲了<a title="Tomcat6源码学习" href="/2013-05/tomcat6.html" target="_blank">Tomcat的系统架构</a>，今天花了一天时间研究了下coyote的源码，大致对普通IO版本有所了解。被同学提醒后发现，Tomcat6已经支持异步IO了，但默认是普通IO版本，需要在Connector的protocol参数中定义为Http11NioProtocol才开启异步IO模式。</p>
<p>主要的代码，Socket的服务在org.apache.tomcat.util.net.JIoEndpoint类，解析处理HTTP请求在org.apache.coyote.http11包的Http11Protocol类和Http11Processor类，org.apache.catalina.connector.CoyoteAdapter负责连接Connector模块（coyote）和Container模块。</p>
<p>线程池默认用的JIoEndpoint类中的子类WorkerStack，若在server.xml配置使用了Executor，则使用org.apache.catalina.core.StandardThreadExecutor（对ThreadPoolExecutor的一层包装）。并发量高的情况下Executor的效率会高一些。<span id="more-1808"></span></p>
<h2>Coyote普通IO流程</h2>
<p>在Catalina主线程启动过程中：</p>
<p>StandardService的init()中，在JIoEndpoint.init()中，ServerSocket(port = 8080, backlog = 100)被new出来。</p>
<p>StandardService的start()中，在JIoEndpoint.start()中，一个新的线程（名叫http-8080-Acceptor-0）被start()，这个由JIoEndpoint.Accept内部类执行run()的线程干的活就是不停地从serverSocket.accept()，然后根据线程池的选择拿一个线程去处理socket。因为处理在新的线程，Acceptor线程继续等待accept()。</p>
<p>若使用默认的WorkerStack线程池，JioEndpoint.getWorkerThread().assign(socket);使Worker线程获得socket实例后，就可以在新线程中处理socket。</p>
<p>若使用Executor线程池，则new出一个JioEndpoint.SocketProcessor()交给Executor.execute()，在新线程中处理socket。</p>
<p>不管哪种线程池方法，新线程中使用一样的代码，调用Http11Protocol$Http11ConnectionHandler.process(socket)，这里使用了一个ConcurrentLinkedQueue用来保存Http11Processor实例，拿一个进行处理。</p>
<p>Http11Protocol.process()具体解析HTTP请求。解析过程中，将参数塞进request和response，再调用CoyoteAdapter.service(request, response)，这样就进入Container模块去调用Servlet代码。</p>
<p>结束后，回收Http11Processor实例，回收线程。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;流程结束分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>好像代码也没有太难看不懂，大多数复杂的线程同步操作都由java.util.concurrnt包里的类实现了。</p>
<h2>Worker线程的assign()和await()</h2>
<p>Acceptor线程调用Worker.assign( socket )，将socket对象赋给Worker线程，然后Worker线程中的await()方法通过线程同步，Worker线程被唤醒并拿到了socket就可以在自己线程里顺利执行了。</p>
<p>这块线程同步的代码蛮有意思的，而且Worker线程也借助这边的wait()和notifyall()实现挂起和唤醒。</p>
<h2>server.xml参数调优</h2>
<p><strong>开启异步IO</strong></p>
<p>Connector的配置中，将protocol=“HTTP/1.1”改为“org.apache.coyote.http11.Http11NioProtocol”就可以使用nio包的异步IO，效率高不少。</p>
<p>对java.nio包特别是异步IO这一块了解甚浅，而且Tomcat对这一块的实现略复杂，看懂略有难度，努力！</p>
<p>同样地方还能改为“org.apache.coyote.http11.Http11AprProtocol”，据说利用底层OS的异步IO，需要APR和Native这其他两个组件的支持才能工作，效率更高，不是太确定。</p>
<p><strong>开启Executor并在Connector中使用</strong></p>
<p>Executor的2个配置参数，minSpareThreads和maxThreads，对应到ThreadPoolExecutor(int <strong>corePoolSize</strong>,  int <strong>maximumPoolSize</strong>, &#8230;)这个构造的前面2个参数。分别表示活跃线程数和最大线程数。</p>
<p><strong>Connector的参数：</strong></p>
<ul>
<li>maxThreads，对应JIoEndpoint类中的maxThreads变量，默认为200。若有Executor加入则maxThreads被取代。</li>
<li>acceptCount，对应JIoEndpoint类中的backlog变量，默认为100，用作ServerSocket的第二个参数。ServerSocket里等待队列的长度。</li>
</ul>
<h4  class="related_post_title">看看 coyote , Socket , Tomcat , 多线程 , 异步IO , 线程池</h4><ul class="related_post"><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li><li>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li><li>2016-06-06 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_https.html" title="Tomcat启用https服务">Tomcat启用https服务</a></li><li>2016-03-17 -- <a target="_blank" href="http://blog.11034.org/2016-03/java_client_socket_exceptions.html" title="Java客户端Socket在服务端重启后的异常情况处理">Java客户端Socket在服务端重启后的异常情况处理</a></li><li>2016-03-01 -- <a target="_blank" href="http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html" title="jdk中SimpleDateFormat的实例线程不安全">jdk中SimpleDateFormat的实例线程不安全</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2013-05/coyote.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tomcat6源码学习</title>
		<link>http://blog.11034.org/2013-05/tomcat6.html</link>
		<comments>http://blog.11034.org/2013-05/tomcat6.html#comments</comments>
		<pubDate>Sun, 19 May 2013 10:27:43 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[ClassLoader]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Servlet]]></category>
		<category><![CDATA[Socket]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[事件驱动]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[生命周期]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1803</guid>
		<description><![CDATA[在上一篇博文中说到因为电脑风扇坏了2天于是看完了《How tomcat works》译本，不过这本书是针对To [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在<a title="小黑风扇坏两天，被逼看完一本书" href="/2013-05/fan_error.html" target="_blank">上一篇博文</a>中说到因为电脑风扇坏了2天于是看完了《How tomcat works》译本，不过这本书是针对Tomcat4和5来讲的比较老了，然后再结合Tomcat6.0.26的源代码调试学习了总共一周的时间，算是大致将Tomcat6.0的一部分工作机制（JSP、JMX、安全管理等模块没研究）给搞清楚了。大概是因为平时基于Java的Web程序写的太多，对Tomcat这一块源码研究特别感兴趣，有时候为了想通一个问题兴奋地吃饭睡觉都在一直想，非常有动力并且也有效率，好久没有这种感觉了。</p>
<p><span id="more-1803"></span>列几点觉得Tomcat设计上比较有意思的地方。</p>
<h2>Connector模块</h2>
<p>Tomcat主要分为Connector模块和Container模块，前者就是处理Socket和HTTP请求，后者是就是常听到的Servlet容器。</p>
<p>Connector模块由于功能比较独立，然后Tomcat内部接口的设计允许第三方组件的使用，就使用了Coyote组件，虽然也是apache下的。有空可以再好好来学习下Coyote的源代码，因为这块涉及了多线程和线程同步、资源优化、效率优化等方面，很值得学习。</p>
<p>Tomcat默认一共占用了3个端口，8080（HTTP/1.1）、8009（AJP/1.3，接受其他服务器转发的请求）和8005（等待SHUTDOWN命令，由Server类在主线程中开启）。其中8080和8009由Connector模块管理，所以默认有2个Connector在运作。</p>
<h2>Container模块</h2>
<p>由Engine、Host、Context、Wrapper四个容器组合而成，分别表示整个引擎、虚拟主机、Web应用、Servlet，通过父子关系连接，然后配套相应的Pipeline和Valve实现过程流。</p>
<h2>Lifecycle接口</h2>
<p>包括启动类（Catalina）、Server、Service、Connector、Container、Executor、Loader、Manager、Realm、Resources、Pipeline都实现了这个接口，通过简单的start()和stop()来递归实现生命周期的管理。</p>
<p>并且这一模块中还用到了LifecycleListener和LifecycleEvent这套事件驱动模型，虽然觉得Tomcat的事件驱动做的比较一般性，注册在某个Lifecycle的LifecycleListener会收到这个Object的所有事件通知，要靠自己去辨别事件类型并作出相应反应，效率较低且定制性弱。</p>
<h2>ClassLoader的设计</h2>
<p>Tomcat为了使类库的私有性，有commonLoader用来加载lib下的jar包，catalinaLoader和sharedLoader分别用来加载Tomcat类库和应用程序共享类库且都以commonLoader为parent。但是在Tomcat6的配置中，默认没有开启catalinaLoader和sharedLoader也没有相应的server和shared目录，导致这3个loader就是同一个commonLoader。这3个loader是StandardClassLoader的实例。</p>
<p>然后对于每一个Web应用程序，都有自己的一个WebappClassLoader，其parent为sharedLoader。这样可以防止不同Web应用程序之间加载互相类库。WebappClassLoader和前面提到的StandardClassLoader都继承自java.net.URLClassLoader，直接从指定目录中读取jar包或class文件。WebappClassLoader直接通过管理Servlet类的字节流来实现Servlet的加载然后缓存。</p>
<p>一般的Servlet只有一个实例，若是实现了STM（SingleThreadModel接口，已Deprecated）的Servlet则会有多个实例缓存着。</p>
<p>但似乎无论怎么设计，都无法避免Web应用程序中的Servlet可以获取到Tomcat启动库中的Bootstrap类（这个包由JVM自带的AppClassLoader加载），默认情况下还可以获取到Tomcat主类库中的所有类（比如Catalina类，因为都是由commonLoader加载，而它又是WebappClassLoader的parent）。尝试过将Tomcat的lib目录中关于Tomcat的类库放到server目录下并配置server.loader属性值（conf/catalina.properties）启动catalinaLoader，这样可以避免Servlet访问到Tomcat主类库，但是这样勉强的同时也阻止了比如ContainerServlet接口（使Servlet能访问Wrapper容器）的运作。</p>
<p>这样的一个坏处就是，可以在Web应用程序的Servlet中获取到Catalina类或者Bootstrap类，再启动一次Tomcat。Servlet中，如果在service方法中这么干会被Tomcat的安全机制给发现使得Tomcat整个关闭，若是配置了loadOnStartup后在init方法中这么干则会引发无限循环导致死机&#8230;</p>
<p>不过通过对Tomcat的ClassLoader的学习，对Java的ClassLoader机制有了大大的实践的了解，无论Tomcat是否真正实现了它设计上的初衷，整个设计思想是值得学习的。</p>
<h2>backgroundProcessor()和后台线程</h2>
<p>Engine容器和Context容器都会通过启动一个后台线程间断执行backgroundProcessor方法，比如重新加载Web应用程序的类文件和资源、扫描Session过期等。</p>
<h2>静态资源</h2>
<p>向Tomcat请求静态资源，和请求Servlet没有太大区别，仍然通过Connector和Container这一流程后，交到Tomcat内部的DefaultServlet来处理读取文件内容并写回客户端，下一次请求会返回304 Not Modified。</p>
<p>所以Tomcat对静态资源的管理比较差速度比较慢，才有apache + tomcat结合的说法吧。</p>
<h4  class="related_post_title">看看 ClassLoader , HTTP , Servlet , Socket , Tomcat , 事件驱动 , 多线程 , 容器 , 生命周期</h4><ul class="related_post"><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li><li>2016-06-06 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_https.html" title="Tomcat启用https服务">Tomcat启用https服务</a></li><li>2016-03-17 -- <a target="_blank" href="http://blog.11034.org/2016-03/java_client_socket_exceptions.html" title="Java客户端Socket在服务端重启后的异常情况处理">Java客户端Socket在服务端重启后的异常情况处理</a></li><li>2016-03-01 -- <a target="_blank" href="http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html" title="jdk中SimpleDateFormat的实例线程不安全">jdk中SimpleDateFormat的实例线程不安全</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2013-05/tomcat6.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
