TCP/IP 协议详解 第19、20章 TCP的数据流

第19章 交互数据流

交互数据总是以小于最大报文段长度(MSS,Maximum Segment Size)的分组(一般也称为微小分组,tinygram)发送。

经受时延的确认

通常TCP在收到数据时并不立即发送 ACK,它会推迟发送,以便将 ACK 与需要沿该方向发送的数据一起发送(这种现象也称为数据捎带 ACK)。绝大多数实现采用的时延是200ms。

Nagle 算法

Nagle 算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的分组,并在确认到来时以一个分组的方式发出去。该算法的优越之处在于它是自适应的:确认到达的越快,数据也就发送得越快。在希望减少微小分组数目的低速广域网上,则会发送更少的分组。

对于低延时的应用则需要关闭Nagle 算法,可以通过 TCP_NODELAY 选项来关闭。

在发送的报文段中如果包含了丢失的报文段中的数据,则称为重新分组化。

继续阅读

TCP/IP 协议详解 第18章 TCP 连接的建立与终止

TCP是一个面向连接的协议,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。

连接建立与终止

连接建立协议

为建立一条TCP连接的步骤:

  • 客户端发送一个 SYN 段(SYN 标志置 1)指明客户端打算连接的服务器的端口,以及其初始序号 ISN。
  • 服务器发回包含服务器的初始序号的 SYN-ACK 报文段(SYN、ACK标志都置 1)作为应答,确认序号设置为客户端的 ISN 加 1 以对客户端的 SYN 报文段进行确认(一个 SYN 占用一个序号)。
  • 客户端必须将确认序号设置为服务器的 ISN 加 1 以对服务器的 SYN-ACK 报文段进行确认。

这三个报文段完成连接的建立,这个过程称为三次握手(three-way handshake)。

继续阅读

TCP/IP 协议详解 第17章 TCP 传输控制协议

TCP使用网络层(IP),提供了一种面向连接的、可靠的字节流服务。

面向连接意味着两个使用TCP协议的应用在彼此交换数据之前必须先建立一个TCP连接。

TCP使用以下方式来提供可靠服务:

  • 应用数据被分割成TCP认为最适合发送的数据块。由TCP传递给IP的信息单位称为报文段或段(segment)。
  • 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
  • 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
  • TCP将保持它首部和数据的校验和。这是一个端到端的校验和,目的是检测在传输过程中的任何变化。如果收到的校验和有差错,TCP将丢弃这个报文段和不缺人收到这个报文段(希望发端超时并重发)。
  • TCP报文段接收到的顺序是不确定的,所以TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
  • IP数据包还会发生重复,TCP的接收端必须丢弃重复的数据。
  • TCP还能提供流量控制。TCP连接的双方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。

继续阅读

MySQL 事件调度器 定时调度

MySQL 5.1 引入的时间调度器可以作为定时任务调度器,取代系统的cron调度,调度时间可以精确到秒,实时性好。

开启事件调度器

首先查看是否开启了事件调度器: show variables like "event_scheduler";SELECT @@event_scheduler;

开启事件调度器: SET GLOBAL event_scheduler = ON;,这个命令需要具有 SUPER 权限才能执行,可以用 root 用户来执行,说明是作用在整个数据库服务上的,而不单是某个数据库实例。

MySQL服务器默认是没有开启事件调度器的,这样上述通过命令开启的在MySQL服务器重启后会失效,可以在配置文件里默认开启,在 [mysqld] 下添加配置: event_scheduler=ON 默认开启。

查看事件的执行情况

SELECT * FROM information_schema.EVENTS;

继续阅读

Struts2 备忘

今天帮两个同事各看了一个问题,有点意思。

Struts2 JSON 序列化与 Action get 方法

场景:同事跑来说他的ajax请求总是进入ajax的错误处理的回调函数,他需要用ajax提交请求到action,然后处理返回结果。

异常现象:这个同事也没什么经验,他告诉我的是ajax请求总是进入ajax的错误处理的回调函数,我在chrome开发工具那里一看,http响应是500,肯定进入错误处理的回调函数了,所以问题应该是在服务端。

分析与解决:在eclipse里debug跟踪发现action执行是ok的,返回的结果名也是 SUCCESS,配置也没问题,但控制返回给Struts2框架后却抛出一堆异常,异常信息跟请求完全无关,从异常栈来看,抛出异常的方法也没有在请求方法里调用,虽然它们都在同一个action里,异常栈的方法基本都是JSON序列化有关的方法的,也就是说问题应该是出在Struts2框架在把action结果序列化为JSON返回给浏览器之前。一开始也看不出具体问题,后来同事在网上搜索看到说是 setter/getter 导致的,然后他的那个被抛出异常的方法也是以get开头命名的,坑就这么形成了:Struts2在序列化action的结果时,会把action的所有公开的getter方法的结果序列化到json里,那个方法以get开头,自然也就被调用了,然后异常就抛出来了。

这个问题自己写的时候一般都会注意避开,但不小心的人还是会踩上的。

ajax 提交 JS 数组对象与 Struts2 参数解析

场景:在页面输入一组用户信息,然后用jQuery的ajax提交给后台的Struts2 action,在JS里,这组信息是用数组来存储,数组的每个元素是一个JSON对象。

异常现象:在tomcat的控制台输出的异常信息是这样的:No result defined for action package4.business.action and result input

分析与解决:一开始我也以为是Java代码的问题,毕竟异常是在控制台里抛出的,但代码也看不出什么问题。这个请求的url在浏览器地址栏直接打开是没有问题的,所以我想,是不是跟请求方法有关,改为 GET 后还是出错。再想,地址栏打开是不带数据的,so在ajax里也不带数据,结果也没异常了,所以可以确定是请求参数的问题了。在chrome的开发工具那里看到,数组参数都被序列化为 persons[0][name]=name 的形式发出去了,这在js里是没问题的,访问对象的属性可以用句点(json.prop),也可以用方括号(json[prop]),参数被提交到Struts2时,它进行参数解析并映射到Java对象,它的规则却与这个有冲突了,对于句点,Struts2也是解析为对象的属性,但方括号却是被当作数组的,所以解析肯定失败,会抛出异常,捕获到参数解析异常后,请求会被路由到命名为 input 的结果那里,但我们这个项目都没配置 input,所以抛出的异常是找不到 result input


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

wordpress 文章索引 存储过程

最近想给本站加上文章索引的页面,就是在一个页面上展示所有的分类及该分类的文章,然后置顶到首页,效果见本站文章索引。开始的思路是:先新建一个页面,然后设置好置顶等,用存储过程更新该页面的内容,用触发器在有新文章发表或文章标题有更新时触发执行存储过程。看起来应该挺顺利的,不过实现过程倒没想象那么好了。

更新索引存储过程

这个存储过程的功能是从数据库里查询出所有的分类以及该分类下的文章的ID和标题,然后用游标生成html内容(wordpress直接存储页面的html内容),再更新为索引页面的内容。
继续阅读

MySQL 乱码 与 字符集

MySQL 安装后默认的字符集是 latin1,这样在处理中文时容易出现乱码问题,所以需要改为 utf8

注:我的MySQL版本是5.6.*

用语句 show variables like 'character%'; 查看MySQL当前的字符集设置(也可以使用命令 status查看):

mysql> show variables like 'character%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | latin1                           |
| character_set_connection | latin1                           |
| character_set_database   | latin1                           |
| character_set_filesystem | binary                           |
| character_set_results    | latin1                           |
| character_set_server     | latin1                           |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/share/mysql/share/charsets/ |
+--------------------------+----------------------------------+
8 rows in set (0.03 sec)

从结果可以看出,MySQL字符集涉及到:

  • 客户端使用的字符集:character_set_client
  • 客户端域服务器之间的连接使用的字符集:character_set_connection
  • 数据库实例使用的字符集:character_set_database
  • MySQL服务器使用的字符集:character_set_serve
    继续阅读

《把时间当作朋友:运用心智获得解放》笔记

《把时间当作朋友:运用心智获得解放》是 李笑来 的一本书,在微信上看到有人多次推荐,就买了电子版来看。里面讲的一些东西非常现实,现实到让人有些难以接受。摘录一些话,看了有兴趣的可以去买本看看。

不思考的人是没有问题可问的;会思考的人有问题往往并不去问人,因为他们最终能够自己解决问题。

教是最好的学习方法。

往往并不是有兴趣才能做好,而是做好了才有兴趣。

方法固然重要,但是比起“用功”来说,方法几乎可以忽略不计。(这让我想起经常看到的一句话:以大多数人的努力程度,根本轮不到拼天赋。)

所有学习上的成功,都只靠两件事:策略和坚持,而坚持本身就是最重要的策略。

与其不停地找更好的方法,还不如马上开始行动,省得虚度更多的时间。

有些哪怕是常识的东西也需要亲身经历过后才能体会。

坚持不懈就是策略加上重复。

平静接受并且正确认识自己的天性是改变天性的第一步。

见识越少的人越喜欢用自己所有的见识作为判断依据,并且完全不顾自己见识的局限,也不知道自己的见识有局限。

一个故事至少有三个版本:你的、我的、真实的。

事实上,买回来一本书里,哪怕有一句话给我们带来惊喜、带来思考、带来改变,就已经值回价值。

决定一个人富有的三个条件:出身、运气、努力,这三者中,努力是最微不足道的。(这是一个经济学家奈特说的)

我们所面临的今天很大程度取决于我们的过去,无论后悔程度多么强烈都无济于事。

我们所使用的语言往往会限制我们的思维。

耐心就是甘于把时间投入到简单、枯燥但是最终意义非凡的重复当中去。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

JUC ConcurrentLinkedQueue

java.util.concurrent.ConcurrentLinkedQueue 是一个基于链接结点的、无界、线程安全的、FIFO队列。它的是实现采用了无等待(wait-free)、无锁(lock-free)算法,该算法基于 Maged M. Michael 和 Michael L. Scott 合著的 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms 中描述的算法。

一、设计思路

  1. 使用 CAS 原子指令来处理对数据的并发访问,把同步最小化到单个硬件指令上;这是无锁算法的基础。
  2. 分别用 head、tail 两个原子指针来指向队头和队尾,可以同时进行入队和出队操作,但允许 head、tail 并不总是指向有效的头和尾;把入队、出队需要同步更新的范围最小化到单个原子变量上,这是无锁算法实现的关键。
  3. 在入队、出队操作上并不总是更新 head、tail,而是在多次操作后才进行更新,节省了CAS指令。具有批量更新的效果。
  4. 由于 head、tail 并不总是指向头和尾,这就是说,队列会出现不一致的情况,所以在 head、tail 上定义了一些不变式来维护算法的正确性。

二、不变式

不变式是在执行方法之前和之后,队列必须要保持的。可变式是在执行过程中,队列允许出现的不一致情况。

head 的约束

通过这个结点能够在 O(1) 时间到达第一个存活(非已删除)结点,如果有的话。

不变式
  • 所有存活结点可以 从 head 开始通过 succ() 访问。
  • head != null
  • (tmp = head).next != tmp || tmp != head,这个不变式是说 head 在方法开始之前、之后,head 指向的结点应该是在队列上的。
可变式
  • head.item 可能是、也可能不是 空的。
  • 允许 tail 落后于 head,那是因为 tail 不能从 head 到达。

tail 的约束

从它可以在 O(1) 时间访问到队列的最后一个结点(唯一的满足 node.next == null 的结点)。这个也就是说 tail 指向的并不总是最后一个存活结点。

不变式
  • 最后的结点总是可以从 tail 开始通过 succ() 访问。
  • tail != null
可变式
  • tail.item 可能是、也可能不是空。
  • 允许 tail 落后于 head,那是因为 tail 不能从 head 到达。
  • tail.next 可能是、也可能不是 自指向到 tail。
    继续阅读