socket编程总结
昨天进行了一些测试,所以觉得可以写一个总结性的文字了。当然了,这个也就是归纳性质的,把之前写的东西统统的串在一起,在稍微高一点的事视野中描述一下自己对socket,特别是基于java的socket的理解和看法。另外这个东西也是一个循序渐进而定过程,也不期望这次一下子完全分析的很透彻,而是一个不断晚上的过程,在以后的工作学习中,如果还可以遇到类似的问题,或者还有机会写到这种代码也许就会有更新和更深层次的理解吧。
首先,socket这个东西我觉得是一个非常基础的协议。绝大部分通信,任意两台计算机,甚至是本机的两个程序之间都可能是基于socket。同时在socket这个基础上自然有派生出多种多样的协议和应用,比较著名和容易理解的就是HTTP服务器了。其就是架构在socket的基础上,更重要的是socket是一个标准,所以不同语言和程序之间的调用和通信更是socket的实用之处,如JDBC。
接下来就是正题了,本人之一做java方面,所以对系统底层不是很了解,所以什么windows的IOCP,linux的poll/epoll/aio之类,也仅仅是听说过的程度,另外JDK7会有BIO的特性,据说在windows下面底层直接使用IOCP实现,当然JDK7也是大势所趋,之后也会补上这一章节。也正是基于上述原因,这里讨论的socket仅限于阻塞和非阻塞情况,当然阻塞和非阻塞都是对于TCP来说,UDP自然也暂时不在本主题讨论的范围。
阻塞是socket稍后在详细说明一下。(稍后补充)
这个时候首先需要认识的就是socket是基于流的东西,而这个流有时基于网络的流,所以这个流无法本标记,进而被重置。他只有一个起点,一个结尾,每个数据都无法标记他的与众不同,即使用特殊内容标记流内容都是不值得推荐的,当然了API封装的readUTF()之类的方法就另当别论了。
《java基于nio的socket通信》首先就是这个了,网络数据是一个不间断的流。在实验室环境下(可以简单理解为内网环境下),我们想获得一个特定的传输内容最有效的办法就是事先通知内容长度。如果不是很严格的话,使用特殊字符标记内容也是很多人的选择(但是这是非常有局限性的)。
《java基于TCP的socket数据包拆分方法》接下来就是考虑的稍微复杂的情况了,实际的网络环境自然千差万别,在通过各个网关路由器等硬件环境下难免会造成网络通信的延时(这只是在读取数据的情况,自然服务器和客户端存在同样的问题)。此时在连接没有关闭的情况下我们我发预计接下来的数据书否可以正常的接收。
接下来的写的问题,也正是存在网络问题于是就出现所谓的”慢客户端”。简单的说就是客户端网络状况非常糟糕,服务器无法正常写出数据,当然这也是基于NIO来说(我认为这个是比较有趣的也尝试写了些东西出来),接下来会专门写个东西描述一下。
然后是硬件和系统软件的问题,主要是socket断开的问题。
这图基本可以说明socket的各个阶段的状态,我们仅仅关注FIN。此时需要了解两个概念就是所谓正常断开与异常断开,正常断开即软件通知的断开连接情况。这里主要反应在流read为-1,EOF。或者read抱异常,如IOException,connection reset by peer之类。这类异常和问题都是可以捕获进而可以控制的,所以归类为正常断开。这里主要说的网络情况造成为问题。我在《java基于TCP的socket数据包拆分方法》 里面多次说TCP协议会维护socket的连接状态,但是实际的网络设备为了节省资源后有自己的判断标准。如网关或者路由器会判断socket建立的流通道使用情况,如果长时间未使用此通道通信则网络设备会断开此连接。而此时软件方面是无从得知的,知道下一次使用此通道通信是绘报出异常,此时主要是Connection reset by peer之类。其实这个东西如果有人注意一下有个地方出现的比较频繁那就是数据库,特别是JDBC连接的时候,当然了这种问题实际被数据库连接池技术跟弥补了。但是如果你使用自己的JDBC连接数据库,而没有进行特殊处理的话,大概程序跑一段时间就会出现这个问题,不同数据库自然不同,从几小时到几天不等。
首先出现这种问题的本质就是socket已经能够关闭但是仍然使用此socket通信,当然你可以保证你并没有做出关闭socket的动作,但是这是网络或本级系统造成的。就是你长时间没有操作数据库这个连接就会被断开。于是连接池都有testSql()这种方法,使用特殊的SQL语句如”SELECT 1 FROM 1},当然不同数据库语句也是不同的。然后定时执行此SQL,作用就是维持数据库的正常连接。
所以应由此种思想,即使是TCP的SOCKET也是必须使用所谓的”心跳包”,这数据包维护SOCKET连接,当然心跳包的作用不仅仅是维护SOCKET连接,更为重要的作用是精确的探测到客户端的状态。经过测试使用心跳之后socket即便几天不做任何操作,依旧可以维护socket的正常连接。
然后是另一个比较严重的问题,就是就是FIN没有正常发送,比如突然拔网线,系统dwon掉。这个问题在服务器是windows的时候也没问题,但是在linux下面却有问题,特别是在NIO情况下。服务器干本不知道客户端已经down掉,甚至服务器还可以正常写出数据,虽然网络上有人说这是因为什么windows经socket事件和窗口事件混为一谈,但是linux没有,但是起初我还不相信,于是昨天在做完最后一种情况的测试之后我表示真的是这个样子。
说道这里就稍微扯的远一些就是关于观察者的问题,观察者的目的就是等待事件的发生。但是反过来我们如何知道事件没有发生?虽然这个想法很奇怪,甚至是荒谬,但是本人的一个缺点就是喜欢胡思乱想。比如最近在写代码的时候不能很好的设计一个框架,于是想是不是设计一个协议。名字暂时叫”Access Point”,接入点协议。简单的说就是N个应用接入到一个点上。这个点可以指示任意应用将相互访问和调用,但是这有和抽象与接口有很大区别……总之这个好像有点异想天开而定样子。
回到之前的话题,解决这种断开的唯一有效方式还是心跳。于是消息驱动是一种比较有效的反应事件未发生方式,相对于事件驱动模式来说。与上一个关闭空连接的情况一样,客户端也是定时向服务器发送特定数据包告知自己的情况。虽然主要的作用是socket通道的连接判断,但是同样可以将业务逻辑应用其中。服务器判断某段时间没有收到客户端的数据就可以了理解为客户端已经断开了连接。
目前可以理解文socket问题就是以上内容,其中很多还需要专门的去了解一下。稍后会补充完整。