一个简单的TCP客户端/服务器程序示例,这个简单的例子执行如下步骤的一个回射服务器:
1) 客户端从标准输入读入一行文本,并写给服务端
2) 服务端从网络输入读入一行文本,并回射给客户端
3) 客户端从网络输入读入这行回射的文件,并显示在标准输出上。
下图描述了这个简单的客户/服务器:
代码略,见上篇。
服务器主机崩溃
我们接着看当服务器主机崩溃时会发生什么。为了模拟这种情形,我们必须在不同的主机上运行客户端和服务端。我们先启动服务器,再启动客户,接着再客户上键入一行文本以确认连接工作正常,然后从网络上断开服务器主机,并再客户端上键入另一行文本。这样同时也模拟了当客户发送数据时服务器不可达的情形。(例如,建立连接后某些中间路由器不工作)。
步骤如下所述:
1) 当服务器主机崩溃时,已有的网络连接上不发送任何东西。这里我们假设的是主机崩溃,而不是由操作员执行命令关机
2) 我们在客户上键入一行文件,他由write写入到内核,在由客户端tcp作为一个数据分节送出。客户随后阻塞于read调用,等待回射的应答。
3) 如果我们用tcpdump观察网络就会发现,客户端tcp持续重传数据分节,试图从服务器接收一个ack。
源自Berkeley的实现重传该数据分节12次,共等待约9分钟才放弃重传。当客户tcp最后放弃时,给客户进程返回一个错误。既然客户阻塞在read调用上,该调用将返回一个错误。假设服务器主机已崩溃,从而对客户的数据分节根本没有响应,那么所返回的错误是ETIMEOUT。如果是中间某个路由器判断主机已不可达,从而响应一个destination unreachable 的icmp消息,那么所返回的错误是EHOSTUNREACH或者ENETUNREACH。
我们用tcpdump抓取数据包,在wireshark上观察一下,在Linux系统上数据重传7次
尽管我们的客户最终还是会发现对端主机已经崩溃或者不可达,不过由时侯我们需要比不得不等待9分钟更快的检测出这种情况。所用的方法就是对read调用设置一个超时。