一般而言,对于处理2MSL状态的套接字(一般为服务端套接字)是不允许接受从同一客户端重新发起一个新的连接的,但是套接字编程系统接口允许应用程序通过设置一个REUSEADDR选项,使处于2MSL状态的套接字重新接受从相同客户端发起的新的请求。很多教科书上都是这么说,但是其中有一个最为关键的问题大家都避而不谈,那就是在客户端请求连接时,服务器是新创建一个套接字用于通信的,侦听套接字还处于侦听状态,其不进行任何实际的数据交换。那么现在双方关闭连接,实际上从服务器的角度而言,处于2MSL等待状态也就是先前那个用于通信的套接字,这时候侦听套接字依然处于侦听状态,而且所谓接不接受新的连接请求,只能对侦听套接字而言,对于那个新创建的用于数据交换的套接字是不存在所谓的接不接受请求的情况的。那么此处就涉及一个底层sock数据结构的存储和查找问题。

get_sock函数,改函数根据套接字四元素(双方的IP地址和端口号),从系统队列中寻找满足条件的sock结构。实际上对于侦听套接字sock结构只有本地IP地址本地端口号进行标识,其不与任何远端IP地址端口号进行绑定,只有在处理一个连接请求时新创建的那个用于通信的套接字才进行四元素的绑定,而且这个新创建的套接字的本地IP地址和端口号大多数情况下是和侦听套接字相同的。

由于sock结构查找过程中首先是通过本地端口号进行数组元素队列的寻址,所以对于一个侦听套接字而言,其在调用tcp_conn_request函数处理一个连接请求时创建的新的套接字对应的sock结构都与该侦听sock结构处于同一个队列中,只不过在查找具体sock结构时,是查找最佳匹配的sock结构。由于侦听套接字只绑定了本地IP地址和端口号,而负责通信的套接字sock结构绑定了本地和远端的IP和端口号,在查找时匹配度叫侦听套接字高,所以通常在通信套接字处于非TCP_CLOSE状态其sock结构依然处于系统队列中,返回的是通信套接字的sock结构,而非侦听套接字sock结构。

那么对于新连接的请求,为何返回侦听套接字sock结构?这时候,侦听套接字以前创建的所有的新的套接字本地IP和端口号都符合条件,原因是:其一在查找对应sock结构时,远端IP地址和端口号都没有匹配项;其二侦听套接字处于队列的最前端,虽然由侦听套接字所创建的sock结构都满足本地IP地址和端口号的匹配,但查找规则是除非找到更合适的否则返回第一个满足条件的sock结构。

另外一个查找策略是如果套接字状态为TCP_CLOSE,则将该套接字排除在查找对象之外。综上所述,虽然在检查服务器端套接字状态为2MSL时使用的是通信套接字,但在接受一个远端的连接请求时,使用的确是侦听套接字。

 /*
4140 * BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is
4141 * a more complex suggestion for fixing these reuse issues in RFC1644
4142 * but not yet ready for general use. Also see RFC1379.
4143 */
#define BSD_TIME_WAIT
#ifdef BSD_TIME_WAIT
if (sk->state == TCP_TIME_WAIT && th->syn && sk->dead &&
after(th->seq, sk->acked_seq) && !th->rst)

4146 行 if 语句是对处于 2MSL 状态的套接字是否接受到一个连接请求进行判断, 如果条件都满足,则表示接收到一个具有相同远端地址,远端端口号的连接请求(这一点是由 4540 行代码保证的)。在处理上是将听任原来的这个通信套接字释放,而将请求转移给侦听套接字,通过调用tcp_conn_request 函数重新创建一个套接字用于通信。下面我们逐行分析这时如何完成的。

 long seq=sk->write_seq;

保存原来这个通信套接字本地发送序列号最后值,下面( 4165 行)将这个序列号加上 128000作为新创建套接字的初始序列号。

 if(sk->debug)
printk("Doing a BSD time wait\n");
tcp_statistics.TcpEstabResets++;
sk->rmem_alloc -= skb->mem_len;
skb->sk = NULL;
sk->err=ECONNRESET;
tcp_set_state(sk, TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
release_sock(sk);
- 行代码将原来的这个通信套接字完全置于关闭状态,首先将这个新的连接请求数据包
与这个通信套接字脱离干系( , 行),并设置套接字状态为完全关闭( , 行)。
最后调用 release_sock 函数进行释放(注意 release_sock 函数除了对缓存与 sock 结构中 back_log
队列中数据包调用 tcp_rcv 进行重新处理外, 对于处于 TCP_CLOSE 状态以及 sk->dead 设置为
的套接字进行释放操作(具体的通过设置一个定时器完成,定时器到期后方才进行释放)。换句
话说,以上这段代码是将原先的通信套接字完全置于“毁灭”。下面对于相同远端的连接请求通
过转移给侦听套接字, 而侦听套接字通过调用 tcp_conn_request 函数创建一个新的通信套接字儿
完成。
sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
当 行代码将原来的处理那个相同远端的套接字被设置为 TCP_CLOSE 状态后, 其就不具备
被查找的资格,所以 行代码调用 get_sock 函数返回的就是侦听套接字 sock 结构!即从
行开始,如下的 sk 变量指向的都是侦听套接字的 sock 结构,那么当然如果服务应用程序仍然
运行的话, 行为真,从进行 行 tcp_conn_request 函数的调用处理连接请求(即又创建
一个新的通信套接字进行数据交互)。当然如果服务应用程序被关闭,简单丢弃此次连接请求。
如果远端再次发送连接请求,在对其下一个连接请求数据包进行处理时,本地会“毫不客气”
的回复一个 RST 数据包的。
if (sk && sk->state==TCP_LISTEN)
{
sk->inuse=;
第二章 网络协议实现文件分析 LINUX1.2.13 内核网络栈实现代码分析
skb->sk = sk;
sk->rmem_alloc += skb->mem_len;
tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+);
release_sock(sk);
return ;
}
kfree_skb(skb, FREE_READ);
return ;
}
#endif
}// if(sk->state!=TCP_ESTABLISHED)
通过 - 行代码的分析,读者现在应该明白通常我们所说的使用 REUSEADDR 选项的侦
听套接字究竟是如何处理的(注意 REUSEADDR 选项只可被用于侦听套接字)。在处理上,原
来的通信套接字仍然进行释放,只不过侦听套接字又创建了一个新的套接字用于同一远端的通
信。本质上,这与平常的处理连接请求的方式并无区别,仅仅在于现在这个请求发生在原来的
套接字还没有被释放(那么在处理上就加速了释放过程,从而为创建新的套接字扫清道路),而
且原来通信中可能被延迟的数据包会被发送到这个新创建的连接通道中。当然对于使用
REUSEADDR 选项的应用而言,如果真的发生这种情况,这也是自找的。对于以上代码的分析,
读者需要结合 get_sock 函数进行理解。

REUSEADDR 选项的更多相关文章

  1. TCP回射客户服务器模型(02 设置套接字选项、处理多并发)

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);  //设置套接字选项 ...

  2. Java Socket 学习笔记

    TCP协议的Socket编程 Socket:英文中的意思是插座.两个Java应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket.Java中所有关于网络编程的类都 ...

  3. 服务器上的Git

    前面的话 如果想与他人使用,除了使用Git来完成日常工作之外,还需要一个远程的Git仓库.尽管从技术上可以从个人的仓库里推送和拉取修改内容,但并不鼓励这样做,因为一不留心就很容易弄混其他人的进度.因此 ...

  4. Git详解之四:服务器上的Git

    服务器上的 Git 到目前为止,你应该已经学会了使用 Git 来完成日常工作.然而,如果想与他人合作,还需要一个远程的 Git 仓库.尽管技术上可以从个人的仓库里推送和拉取修改内容,但我们不鼓励这样做 ...

  5. tcp/ip通信第5期之服务器端程序

    /* 此程序是tcp/ip通信服务器端程序,测试运行在redhat5上 重构readline函数,解决粘包问题——利用“\n”识别一个消息边界 */ #include<stdio.h> # ...

  6. Git详解之四 服务器上的Git

    以下内容转载自:http://www.open-open.com/lib/view/open1328069988843.html 服务器上的 Git 到目前为止,你应该已经学会了使用 Git 来完成日 ...

  7. 在Android手机上学习socket程序

    我们都知道Android手机是基于Linux系统的,在没有Linux环境,但是想学习socket编程的同学可以在Android手机中试试,利用ndk编译可执行文件在Android手机中运行.不同于动态 ...

  8. 【Linux 网络编程】REUSADDR

    (1)服务器端尽可能使用REUSEADDR.(2)在绑定之前尽可能调用setsockopt来设置REUSEADDR套接字选项.(3)使用REUSEADDR选项可以使得不必等待TIME_WAIT状态消失 ...

  9. 第08课:【实战】Redis网络通信模块源码分析(1)

    我们这里先研究redis-server端的网络通信模块.除去Redis本身的业务功能以外,Redis的网络通信模块实现思路和细节非常有代表性.由于网络通信模块的设计也是Linux C++后台开发一个很 ...

随机推荐

  1. sqlserver 无法初始化via支持库[QLVIPL.DLL]

    安装数据库后,在sqlserver configuration manager, sqlserver的网络配置,有将协议 shared memory,named pipes,tcp/ip,via全部启 ...

  2. 【solr这四个主题】大约VelocityResponseWriter

    一个.大约Velocity基本配置 在Solr在,可以以多种方式返回搜索结果,作为一个简单的文字回复(XML.JSON.CSV等待),能够返回velocity.js等格式.而VelocityRespo ...

  3. SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue性能测试(转)

    听说JDK6对SynchronousQueue做了性能优化,避免对竞争资源加锁,所以想试试到底平时是选择SynchronousQueue还是其他BlockingQueue. 对于容器类在并发环境下的比 ...

  4. 七天来学习ASP.NET MVC (两)——ASP.NET MVC 数据传输

    通过第一天的学习之后,我们相信您已经对MVC有一些基本了解. 本节所讲的内容是在上节的基础之上.因此须要确保您是否掌握了上一节的内容. 本章的目标是在今天学习结束时利用最佳实践解决方式创建一个小型的M ...

  5. SGU 200. Cracking RSA(高斯消元+高精度)

    标题效果:鉴于m整数,之前存在的所有因素t素数.问:有多少子集.他们的产品是数量的平方. 解题思路: 全然平方数就是要求每一个质因子的指数是偶数次. 对每一个质因子建立一个方程. 变成模2的线性方程组 ...

  6. .NET应用架构设计—再次了解分层架构(现代企业应用分层架构核心设计元素)

    阅读文件夹: 1.背景介绍 2.简要回想下传统三层架构 3.企业级应用分层架构(现代分层架构的基本演变过程) 3.1.服务层中应用契约式设计来解决动态条件不匹配错误(通过契约式设计模式来将问题在线下暴 ...

  7. andriod 在windows配置环境

    andriod开发环境配置 个人信息:就读于燕大本科软件project专业 眼下大四; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构和算法,希望将 ...

  8. struts1吊牌&lt;logic:iterate&gt;

    <logic:iterate>主要用于处理网页上的输出集合,集合是其中一般下列之一: 1. java对象的数组 2. ArrayList.Vector.HashMap等 具体使用方法请參考 ...

  9. javascript小白学习指南0---1

    引言: 做为一名程序猿.都是真心的想把自己的东西分享出来,供大家一起学习探讨.一起提高技能.一起涨工资,呵   这一系列的文章都是关于Javascript 基础的 当然文章其中穿插了些我自己的理解.希 ...

  10. ash

    查看当前用户使用的Shell,echo $SHELL BusyBox ('01-current) The BusyBox distribution is aiming for small implem ...