REUSEADDR 选项
一般而言,对于处理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 选项的更多相关文章
- TCP回射客户服务器模型(02 设置套接字选项、处理多并发)
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); //设置套接字选项 ...
- Java Socket 学习笔记
TCP协议的Socket编程 Socket:英文中的意思是插座.两个Java应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket.Java中所有关于网络编程的类都 ...
- 服务器上的Git
前面的话 如果想与他人使用,除了使用Git来完成日常工作之外,还需要一个远程的Git仓库.尽管从技术上可以从个人的仓库里推送和拉取修改内容,但并不鼓励这样做,因为一不留心就很容易弄混其他人的进度.因此 ...
- Git详解之四:服务器上的Git
服务器上的 Git 到目前为止,你应该已经学会了使用 Git 来完成日常工作.然而,如果想与他人合作,还需要一个远程的 Git 仓库.尽管技术上可以从个人的仓库里推送和拉取修改内容,但我们不鼓励这样做 ...
- tcp/ip通信第5期之服务器端程序
/* 此程序是tcp/ip通信服务器端程序,测试运行在redhat5上 重构readline函数,解决粘包问题——利用“\n”识别一个消息边界 */ #include<stdio.h> # ...
- Git详解之四 服务器上的Git
以下内容转载自:http://www.open-open.com/lib/view/open1328069988843.html 服务器上的 Git 到目前为止,你应该已经学会了使用 Git 来完成日 ...
- 在Android手机上学习socket程序
我们都知道Android手机是基于Linux系统的,在没有Linux环境,但是想学习socket编程的同学可以在Android手机中试试,利用ndk编译可执行文件在Android手机中运行.不同于动态 ...
- 【Linux 网络编程】REUSADDR
(1)服务器端尽可能使用REUSEADDR.(2)在绑定之前尽可能调用setsockopt来设置REUSEADDR套接字选项.(3)使用REUSEADDR选项可以使得不必等待TIME_WAIT状态消失 ...
- 第08课:【实战】Redis网络通信模块源码分析(1)
我们这里先研究redis-server端的网络通信模块.除去Redis本身的业务功能以外,Redis的网络通信模块实现思路和细节非常有代表性.由于网络通信模块的设计也是Linux C++后台开发一个很 ...
随机推荐
- Linux查找多个类似,但不同的名称和重命名文件
受试者被认为是百度侧面问题,Linux称号:寻找core.1.core.2....形式命名的文件,然后改变这些文件的名称bak.core.1.bak.core.2,...... 首先,你应该找到这些文 ...
- bootstrap使用汇总
//大多数功能都能够指定data属性来指定 但是,有必要的情况下使用jquery由于事件是不同的过程 //活动通常有两种状态show shown 通用和过去的 <!DOCTYPE html> ...
- ehcache历史变迁及常用API的使用(转)
ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案.同时ehcache作为开放源 ...
- hdu1115(重力算法的多边形中心)
标题的含义: 给定一个n刚n顶点.这是获得n分众协调多边形. http://acm.hdu.edu.cn/showproblem.php? pid=1115 题目分析: /** *出处:http:// ...
- HttpWebRequest BeginGetResponse EndGetResponse
private void Button_Click_4(object sender, RoutedEventArgs e) { HttpWebRequest request = HttpWebRequ ...
- 8年,属于 HTML 5 春天的到来悄悄!
[核心提示] 在 8 年时间中,HTML 5 为整个行业都带来了什么.标准终于确定后又会产生什么样的变革呢? 微博微信Twitter对于非常多人来说,非常有可能在微信的朋友圈里玩过「围住神经猫」,也非 ...
- Eclipse—怎样为Eclipse开发工具中创建的JavaWebproject创建Servlet
在博客<在Eclipse中怎样创建JavaWebproject>中图文并茂的说明了Eclipse中创建JavaWebproject的方法,本篇博客将告诉大家怎样为Eclipse开发工具中创 ...
- 定制XP引导屏幕背景图像和替换windows这句话
(一)带滚动条界面变化 1.制作640*480位图,4位置16肤色,尺寸小于200Kb. 2.位图名boot.bmp,并将其复制到C:/windows根文件夹 3.更改boot.ini档.我的电脑/属 ...
- net网站运行在自定义的Web服务器上
ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上 一.前言 大家都知道,在之前,我们Asp.net 的网站都只能部署在IIS上,并且IIS也只存在于Win ...
- 如何将linux用在开发环境中的
如何将linux用在开发环境中的 1.我为什么要写这篇文章 一直想深入学习一下linux的使用,于是将家里的笔记本装了linux系统,但是要将自己的系统打造一个适合开发的环境确实是一件费心费力的事,而 ...