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++后台开发一个很 ...
随机推荐
- 综合第一篇文章(带钩Quora)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxNDc4MzAyNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- 百度音乐搜索API介绍
百度音乐搜索API的请求地址如下: [html] view plaincopy http://box.zhangmen.baidu.com/x?op=12&count=1&title= ...
- STL之容器适配器queue的实现框架
说明:本文仅供学习交流,转载请标明出处,欢迎转载! 上篇文章STL之容器适配器stack的实现框架已经介绍了STL是怎样借助基础容器实现一种经常使用的数据结构stack (栈),本文介绍下第二种STL ...
- Cygwin编译自己定义OpenCV库报错:opencv_contrib: LOCAL_SRC_FILES points to a missing file
今天受命帮师弟调个OpenCV4Android 识别银行卡的程序,版本号为OpenCV4Android2.4.9,使用方式为前文介绍的第一种方式,即通过jni调用opencv.如杂家前文所述,配套的N ...
- 关系数据库的基本概念和MySQL说明
关系数据库的基本概念 数据库: 大量的信息化解决方案的高效管理. 根据数据结构来组织.存储和管理数据的库. 数据库系统(DBS,DATABASE SYSTEM): 数据库(DB,DATABASE) + ...
- ftk学习记录(多形式的文章)
[声明:版权全部.欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 在上周末的博客中,我们谈到了list view,今天能够看看效果图怎样. 假设大家细心一点,能够 ...
- OpenWrt wireless通过配置uci生效
[转载请注明出处:钱正柱 http://blog.csdn.net/qianguozheng/article/details/24412673] 配置无线 vi /etc/config/wireles ...
- python 2.x 与3.x的区别
下载了点python的电子书,有基于3.x的有基于2.x的让我不知道看哪些好,BD一下差别着实很大有木有?print语句变成了函数有木有?之后都得加括号了有木有? 别人整理的成果,我就无耻地搬来借用啦 ...
- Java中的逆变与协变(转)
看下面一段代码 Number num = new Integer(1); ArrayList<Number> list = new ArrayList<Integer>(); ...
- leaflet开源地图库源码 浏览器&移动设备判断(browser.js)备份
<script> var isIe = !-[1,]; // alert('ie9 之前'+isIe); var ie = 'ActiveXObject' in window; //ale ...