Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
传输层

模块

 Effective__TCP__IP
 
 TCP
 
 UDP
 

变量

 测试udp打洞
 
 端口复用
 
 为什么对称性nat不能打洞成功
 
 为什么网络穿透使用udp,不使用tcp
 
 proc_sys_net_ipv4下各项的意义
 
 监听端口4个0
 

详细描述

变量说明

proc_sys_net_ipv4下各项的意义
/proc/sys/net/ipv4/icmp_timeexceed_rate
这个在traceroute时导致著名的“Solaris middle star”。这个文件控制发送ICMP Time Exceeded消息的比率。

/proc/sys/net/ipv4/igmp_max_memberships
主机上最多有多少个igmp (多播)套接字进行监听。

/proc/sys/net/ipv4/inet_peer_gc_maxtime
求 助: Add a little explanation about the inet peer storage? Minimum interval between garbage collection passes. This interval is in effect under low (or absent) memory pressure on the pool. Measured in jiffies.

/proc/sys/net/ipv4/inet_peer_gc_mintime
每一遍碎片收集之间的最小时间间隔。当内存压力比较大的时候,调整这个间隔很有效。以jiffies计。

/proc/sys/net/ipv4/inet_peer_maxttl
entries的最大生存期。在pool没有内存压力的情况下(比如,pool中entries的数量很少的时候),未使用的entries经过一段时间就会过期。以jiffies计。

/proc/sys/net/ipv4/inet_peer_minttl
entries的最小生存期。应该不小于汇聚端分片的生存期。当pool的大小不大于inet_peer_threshold时,这个最小生存期必须予以保证。以jiffies计。

/proc/sys/net/ipv4/inet_peer_threshold
The approximate size of the INET peer storage. Starting from this threshold entries will be thrown aggressively. This threshold also determines entries' time-to-live and time intervals between garbage collection passes. More entries, less time-to-live, less GC interval.

/proc/sys/net/ipv4/ip_autoconfig
这个文件里面写着一个数字,表示主机是否通过RARP、BOOTP、DHCP或者其它机制取得其IP配置。否则就是0。

/proc/sys/net/ipv4/ip_default_ttl
数据包的生存期。设置为64是安全的。如果你的网络规模巨大就提高这个值。不要因为好玩而这么做——那样会产生有害的路由环路。实际上,在很多情况下你要考虑能否减小这个值。

/proc/sys/net/ipv4/ip_dynaddr/proc/sys/net/ipv4/icmp_destunreach_rate
如果你有一个动态地址的自动拨号接口,就得设置它。当你的自动拨号接口激活的时候,本地所有没有收到答复的TCP套接字会重新绑定到正确的地址上。这可以解决引发拨号的套接字本身无法工作,重试一次却可以的问题。

/proc/sys/net/ipv4/ip_forward
内核是否转发数据包。缺省禁止。

/proc/sys/net/ipv4/ip_local_port_range
用于向外连接的端口范围。缺省情况下其实很小:1024到4999。

/proc/sys/net/ipv4/ip_no_pmtu_disc
如果你想禁止“沿途MTU发现”就设置它。“沿途MTU发现”是一种技术,可以在传输路径上检测出最大可能的MTU值。参见Cookbook一章中关于“沿途MTU发现”的内容。

/proc/sys/net/ipv4/ipfrag_high_thresh
用 于IP分片汇聚的最大内存用量。分配了这么多字节的内存后,一旦用尽,分片处理程序就会丢弃分片。When ipfrag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ipfrag_low_thresh is reached.

/proc/sys/net/ipv4/ip_nonlocal_bind
如果你希望你的应用程序能够绑定到不属于本地网卡的地址上时,设置这个选项。如果你的机器没有专线连接(甚至是动态连接)时非常有用,即使你的连接断开,你的服务也可以启动并绑定在一个指定的地址上。

/proc/sys/net/ipv4/ipfrag_low_thresh
用于IP分片汇聚的最小内存用量。

/proc/sys/net/ipv4/ipfrag_time
IP分片在内存中的保留时间(秒数)。

/proc/sys/net/ipv4/tcp_abort_on_overflow
一个布尔类型的标志,控制着当有很多的连接请求时内核的行为。启用的话,如果服务超载,内核将主动地发送RST包。

/proc/sys/net/ipv4/tcp_fin_timeout
如 果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。参见tcp_max_orphans。

/proc/sys/net/ipv4/tcp_keepalive_time
当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时。

/proc/sys/net/ipv4/tcp_keepalive_intvl
当探测没有确认时,重新发送探测的频度。缺省是75秒。

/proc/sys/net/ipv4/tcp_keepalive_probes
在认定连接失效之前,发送多少个TCP的keepalive探测包。缺省值是9。这个值乘以tcp_keepalive_intvl之后决定了,一个连接发送了keepalive之后可以有多少时间没有回应。

/proc/sys/net/ipv4/tcp_max_orphans
系 统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。This limit exists only to prevent simple DoS attacks, you _must_ not rely on this or lower the limit artificially, but rather increase it (probably, after increasing installed memory), if network conditions require more than default value, and tune network services to linger and kill such states more aggressively. 让我再次提醒你:每个孤儿套接字最多能够吃掉你64K不可交换的内存。

/proc/sys/net/ipv4/tcp_orphan_retries
本端试图关闭TCP连接之前重试多少次。缺省值是7,相当于50秒~16分钟(取决于RTO)。如果你的机器是一个重载的WEB服务器,你应该考虑减低这个值,因为这样的套接字会消耗很多重要的资源。参见tcp_max_orphans。

/proc/sys/net/ipv4/tcp_max_syn_backlog
记 录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。如果服务器不堪重负,试 试提高这个值。注意!如果你设置这个值大于1024,最好同时调整include/net/tcp.h中的TCP_SYNQ_HSIZE,以保证 TCP_SYNQ_HSIZE*16 ≤tcp_max_syn_backlo,然后重新编译内核。

/proc/sys/net/ipv4/tcp_max_tw_buckets
系 统同时保持timewait套接字的最大数量。如果超过这个数字,time-wait套接字将立刻被清除并打印警告信息。这个限制仅仅是为了防止简单的 DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,如果网络实际需要大于缺省值,更应该增加这个值(如果增加了内存之后)。

/proc/sys/net/ipv4/tcp_retrans_collapse
为兼容某些糟糕的打印机设置的“将错就错”选项。再次发送时,把数据包增大一些,来避免某些TCP协议栈的BUG。
/proc/sys/net/ipv4/tcp_retries1
在认定出错并向网络层提交错误报告之前,重试多少次。缺省设置为RFC规定的最小值:3,相当于3秒~8分钟(取决于RIO)。

/proc/sys/net/ipv4/tcp_retries2
在杀死一个活动的TCP连接之前重试多少次。RFC 1122规定这个限制应该长于100秒。这个值太小了。缺省值是15,相当于13~30分钟(取决于RIO)。

/proc/sys/net/ipv4/tcp_rfc1337
这个开关可以启动对于在RFC1337中描述的“tcp的time-wait暗杀危机”问题的修复。启用后,内核将丢弃那些发往time-wait状态TCP套接字的RST包。却省为0。

/proc/sys/net/ipv4/tcp_sack
特别针对丢失的数据包使用选择性ACK,这样有助于快速恢复。

/proc/sys/net/ipv4/tcp_stdurg
使用TCP紧急指针的主机需求解释。因为绝大多数主机采用BSD解释,所以如果你在Linux上打开它,可能会影响它与其它机器的正常通讯。缺省是FALSE。
/proc/sys/net/ipv4/tcp_syn_retries
在内核放弃建立连接之前发送SYN包的数量。

/proc/sys/net/ipv4/tcp_synack_retries
为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。

/proc/sys/net/ipv4/tcp_timestamps
时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。

/proc/sys/net/ipv4/tcp_tw_recycle
能够更快地回收TIME-WAIT套接字。缺省值是1。除非有技术专家的建议和要求,否则不应修改。

/proc/sys/net/ipv4/tcp_window_scaling
一般来说TCP/IP允许窗口尺寸达到65535字节。对于速度确实很高的网络而言这个值可能还是太小。这个选项允许设置上G字节的窗口大小,有利于在带宽*延迟很大的环境中使用。
一旦内核认为它无法发包,就会丢弃这个包,并向发包的主机发送ICMP通知。

/proc/sys/net/ipv4/icmp_echo_ignore_all
根本不要响应echo包。请不要设置为缺省,它可能在你正被利用成为DoS攻击的跳板时可能有用。
/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts [Useful]
如果你ping子网的子网地址,所有的机器都应该予以回应。这可能成为非常好用的拒绝服务攻击工具。设置为1来忽略这些子网广播消息。
/proc/sys/net/ipv4/icmp_echoreply_rate
设置了向任意主机回应echo请求的比率。

/proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
设置它之后,可以忽略由网络中的那些声称回应地址是广播地址的主机生成的ICMP错误。

/proc/sys/net/ipv4/icmp_paramprob_rate
一个相对不很明确的ICMP消息,用来回应IP头或TCP头损坏的异常数据包。你可以通过这个文件控制消息的发送比率。


tcp_syn_retries :INTEGER
默认值是5
对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1 决定的)

tcp_synack_retries :INTEGER
默认值是5
对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。(可以根据上面的 tcp_syn_retries 来决定这个值)

tcp_keepalive_time :INTEGER
默认值是7200(2小时)
当keepalive打开的情况下,TCP发送keepalive消息的频率。(由于目前网络攻击等因素,造成了利用这个进行的攻击很频繁,曾经也有cu的朋友提到过,说如果2边建立了连接,然后不发送任何数据或者rst/fin消息,那么持续的时间是不是就是2小时,空连接攻击? tcp_keepalive_time就是预防此情形的.我个人在做nat服务的时候的修改值为1800秒)

tcp_keepalive_probes:INTEGER
默认值是9
TCP发送keepalive探测以确定该连接已经断开的次数。(注意:保持连接仅在SO_KEEPALIVE套接字选项被打开是才发送.次数默认不需要修改,当然根据情形也可以适当地缩短此值.设置为5比较合适)

tcp_keepalive_intvl:INTEGER
默认值为75
探测消息发送的频率,乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。(对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值)

tcp_retries1 :INTEGER
默认值是3
放弃回应一个TCP连接请求前﹐需要进行多少次重试。RFC 规定最低的数值是3﹐这也是默认值﹐根据RTO的值大约在3秒 - 8分钟之间。(注意:这个值同时还决定进入的syn连接)

tcp_retries2 :INTEGER
默认值为15
在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒).(这个值根据目前的网络设置,可以适当地改小,我的网络内修改为了5)

tcp_orphan_retries :INTEGER
默认值是7
在近端丢弃TCP连接之前﹐要进行多少次重试。默认值是7个﹐相当于 50秒 - 16分钟﹐视 RTO 而定。如果您的系统是负载很大的web服务器﹐那么也许需要降低该值﹐这类 sockets 可能会耗费大量的资源。另外参的考 tcp_max_orphans 。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为3)

tcp_fin_timeout :INTEGER
默认值是 60
对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。默认值为 60 秒。过去在2.2版本的内核中是 180 秒。您可以设置该值﹐但需要注意﹐如果您的机器为负载很重的web服务器﹐您可能要冒内存被大量无效数据报填满的风险﹐FIN-WAIT-2 sockets 的危险性低于 FIN-WAIT-1 ﹐因为它们最多只吃 1.5K 的内存﹐但是它们存在时间更长。另外参考 tcp_max_orphans。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为30)

tcp_max_tw_buckets :INTEGER
默认值是180000
系 统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要人为的降低这个限制﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。(事实上做NAT的时候最好可以适当地增加该值)

tcp_tw_recycle :BOOLEAN
默认值是0
打开快速 TIME-WAIT sockets 回收。除非得到技术专家的建议或要求﹐请不要随意修改这个值。(做NAT的时候,建议打开它)
tcp_tw_reuse:BOOLEAN
默认值是0
该文件表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接(这个对快速重启动某些服务,而启动后提示端口已经被使用的情形非常有帮助)

tcp_max_orphans :INTEGER
缺省值是8192
系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制(这个值Redhat AS版本中设置为32768,但是很多防火墙修改的时候,建议该值修改为2000)

tcp_abort_on_overflow :BOOLEAN
缺省值是0
当守护进程太忙而不能接受新的连接,就象对方发送reset消息,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。(对待已经满载的sendmail,apache这类服务的时候,这个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它)

tcp_syncookies :BOOLEAN
默认值是0
只有在内核编译时选择了CONFIG_SYNCOOKIES时才会发生作用。当出现syn等候队列出现溢出时象对方发送syncookies。目的是为了防止syn flood攻击。
注意:该选项千万不能用于那些没有收到攻击的高负载服务器,如果在日志中出现synflood消息,但是调查发现没有收到synflood攻击,而是合法用户的连接负载过高的原因,你应该调整其它参数来提高服务器性能。参考:
tcp_max_syn_backlog
tcp_synack_retries
tcp_abort_on_overflow
syncookie严重的违背TCP协议,不允许使用TCP扩展,可能对某些服务导致严重的性能影响(如SMTP转发)。(注意,该实现与BSD上面使用的tcp proxy一样,是违反了RFC中关于tcp连接的三次握手实现的,但是对于防御syn-flood的确很有用.)

tcp_stdurg :BOOLEAN
默认值为0
使用 TCP urg pointer 字段中的主机请求解释功能。大部份的主机都使用老旧的 BSD解释,因此如果您在 Linux 打开它﹐或会导致不能和它们正确沟通。
tcp_max_syn_backlog :INTEGER
对于那些依然还未获得客户端确认的连接请求﹐需要保存在队列中最大数目。对于超过 128Mb 内存的系统﹐默认值是 1024 ﹐低于 128Mb 的则为 128。如果服务器经常出现过载﹐可以尝试增加这个数字。警告﹗假如您将此值设为大于 1024﹐最好修改 include/net/tcp.h 里面的 TCP_SYNQ_HSIZE ﹐以保持 TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog ﹐并且编进核心之内。(SYN Flood攻击利用TCP协议散布握手的缺陷,伪造虚假源IP地址发送大量TCP-SYN半打开连接到目标系统,最终导致目标系统Socket队列资源耗 尽而无法接受新的连接。为了应付这种攻击,现代Unix系统中普遍采用多连接队列处理的方式来缓冲(而不是解决)这种攻击,是用一个基本队列处理正常的完 全连接应用(Connect()和Accept() ),是用另一个队列单独存放半打开连接。这种双队列处理方式和其他一些系统内核措施(例如Syn-Cookies/Caches)联合应用时,能够比较有效的缓解小规模的SYN Flood攻击(事实证明<1000p/s)加大SYN队列长度可以容纳更多等待连接的网络连接数,所以对Server来说可以考虑增大该值.)

tcp_window_scaling :INTEGER
缺省值为1
该 文件表示设置tcp/ip会话的滑动窗口大小是否可变。参数值为布尔值,为1时表示可变,为0时表示不可变。tcp/ip通常使用的窗口最大可达到 65535 字节,对于高速网络,该值可能太小,这时候如果启用了该功能,可以使tcp/ip滑动窗口大小增大数个数量级,从而提高数据传输的能力(RFC 1323)。(对普通地百M网络而言,关闭会降低开销,所以如果不是高速网络,可以考虑设置为0)

tcp_timestamps :BOOLEAN
缺省值为1
Timestamps 用在其它一些东西中﹐可以防范那些伪造的 sequence 号码。一条1G的宽带线路或许会重遇到带 out-of-line数值的旧sequence 号码(假如它是由于上次产生的)。Timestamp 会让它知道这是个 '旧封包'。(该文件表示是否启用以一种比超时重发更精确的方法(RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。)

tcp_sack :BOOLEAN
缺省值为1
使 用 Selective ACK﹐它可以用来查找特定的遗失的数据报--- 因此有助于快速恢复状态。该文件表示是否启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段)。(对于广域网通信来说这个选项应该启用,但是这会增加对 CPU 的占用。)

tcp_fack :BOOLEAN
缺省值为1
打开FACK拥塞避免和快速重传功能。(注意,当tcp_sack设置为0的时候,这个值即使设置为1也无效)

tcp_dsack :BOOLEAN
缺省值为1
允许TCP发送"两个完全相同"的SACK。

tcp_ecn :BOOLEAN
缺省值为0
打开TCP的直接拥塞通告功能。

tcp_reordering :INTEGER
默认值是3
TCP流中重排序的数据报最大数量 。 (一般有看到推荐把这个数值略微调整大一些,比如5)

tcp_retrans_collapse :BOOLEAN
缺省值为1
对于某些有bug的打印机提供针对其bug的兼容性。(一般不需要这个支持,可以关闭它)

tcp_wmem(3个INTEGER变量): min, default, max
min:为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建议以后都可以使用它。默认值为4096(4K)。

default:为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它协议使用的net.core.wmem_default 值,一般要低于net.core.wmem_default的值。默认值为16384(16K)。

max: 用于TCP socket发送缓冲的内存最大值。该值不会影响net.core.wmem_max,"静态"选择参数SO_SNDBUF则不受该值影响。默认值为131072(128K)。(对于服务器而言,增加这个参数的值对于发送数据很有帮助,在我的网络环境中,修改为了51200 131072 204800)

tcp_rmem (3个INTEGER变量): min, default, max
min:为TCP socket预留用于接收缓冲的内存数量,即使在内存出现紧张情况下tcp socket都至少会有这么多数量的内存用于接收缓冲,默认值为8K。

default:为TCP socket预留用于接收缓冲的内存数量,默认情况下该值影响其它协议使用的 net.core.wmem_default 值。该值决定了在tcp_adv_win_scale、tcp_app_win和tcp_app_win=0默认值情况下,TCP窗口大小为65535。默认值为87380

max:用于TCP socket接收缓冲的内存最大值。该值不会影响 net.core.wmem_max,"静态"选择参数 SO_SNDBUF则不受该值影响。默认值为 128K。默认值为87380*2 bytes。(可以看出,.max的设置最好是default的两倍,对于NAT来说主要该增加它,我的网络里为 51200 131072 204800)

tcp_mem(3个INTEGER变量):low, pressure, high
low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。(理想情况下,这个值应与指定给 tcp_wmem 的第 2 个值相匹配 - 这第 2 个值表明,最大页面大小乘以最大并发请求数除以页大小 (131072 * 300 / 4096)。 )

pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。(理想情况下这个值应该是 TCP 可以使用的总缓冲区大小的最大值 (204800 * 300 / 4096)。 )

high:允许所有tcp sockets用于排队缓冲数据报的页面量。(如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守 (512000 * 300 / 4096) 的原因了。 在这种情况下,提供的价值很大,它能处理很多连接,是所预期的 2.5 倍;或者使现有连接能够传输 2.5 倍的数据。 我的网络里为192000 300000 732000)

一般情况下这些值是在系统启动时根据系统内存数量计算得到的。

tcp_app_win : INTEGER
默认值是31
保留max(window/2^tcp_app_win, mss)数量的窗口由于应用缓冲。当为0时表示不需要缓冲。

tcp_adv_win_scale : INTEGER
默认值为2
计算缓冲开销bytes/2^tcp_adv_win_scale(如果tcp_adv_win_scale > 0)或者bytes-bytes/2^(-tcp_adv_win_scale)(如果tcp_adv_win_scale <= 0)。
tcp_rfc1337 :BOOLEAN
缺省值为0
这个开关可以启动对于在RFC1337中描述的"tcp 的time-wait暗杀危机"问题的修复。启用后,内核将丢弃那些发往time-wait状态TCP套接字的RST 包.
tcp_low_latency : BOOLEAN
缺省值为0
允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项一般情形是的禁用。(但在构建Beowulf 集群的时候,打开它很有帮助)
tcp_westwood :BOOLEAN
缺省值为0
启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。
tcp_bic :BOOLEAN
缺省值为0
为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。

$ /proc/sys/net/core/wmem_max
最大socket写buffer,可参考的优化值:873200
$ /proc/sys/net/core/rmem_max
最大socket读buffer,可参考的优化值:873200
$ /proc/sys/net/ipv4/tcp_wmem
TCP写buffer,可参考的优化值: 8192 436600 873200
$ /proc/sys/net/ipv4/tcp_rmem
TCP 读buffer,可参考的优化值: 32768 436600 873200
$ /proc/sys/net/ipv4/tcp_mem
同样有3个值,意思是:
net.ipv4.tcp_mem[0]: 低于此值,TCP没有内存压力.
net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段.
net.ipv4.tcp_mem[2]: 高于此值,TCP拒绝分配socket.
上述内存单位是页,而不是字节.可参考的优化值是:786432 1048576 1572864
$ /proc/sys/net/core/netdev_max_backlog
进入包的最大设备队列.默认是300,对重负载服务器而言,该值太低,可调整到1000.
$ /proc/sys/net/core/somaxconn
listen()的默认参数,挂起请求的最大数量. 默认是128.对繁忙的服务器,增加该值有助于网络性能.可调整到256.
$ /proc/sys/net/core/optmem_max
socket buffer的最大初始化值,默认10K.
$ /proc/sys/net/ipv4/tcp_max_syn_backlog
进入SYN包的最大请求队列. 默认1024.对重负载服务器,增加该值显然有好处.可调整到2048.
$ /proc/sys/net/ipv4/tcp_retries2
TCP失败重传次数,默认值15,意味着重传 15次才彻底放弃.可减少到5,以尽早释放内核资源.
$ /proc/sys/net/ipv4/tcp_keepalive_time
$ /proc/sys/net/ipv4/tcp_keepalive_intvl
$ /proc/sys/net/ipv4/tcp_keepalive_probes
这3个参数与TCP KeepAlive有关.默认值是:
tcp_keepalive_time = 7200 seconds (2 hours)
tcp_keepalive_probes = 9
tcp_keepalive_intvl = 75 seconds
意思是如果某个TCP连接在idle 2个小时后,内核才发起probe.如果probe 9次(每次75秒)不成功,内核才彻底放弃,认为该连接已失效.对服务器而言,显然上述值太大. 可调整到:
/proc/sys/net/ipv4/tcp_keepalive_time 1800
/proc/sys/net/ipv4/tcp_keepalive_intvl 30
/proc/sys/net/ipv4/tcp_keepalive_probes 3
$ proc/sys/net/ipv4/ip_local_port_range
指定端口范围的一个配置,默认是32768 61000,已够大.
net.ipv4.tcp_syncookies = 1
表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1
表示开启重用。允许将 TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,1表示关闭。
net.ipv4.tcp_fin_timeout = 30
表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
net.ipv4.tcp_keepalive_time = 1200
表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 1024 65000
表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192
表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_tw_buckets = 5000
表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为 5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。

# 以下一段为抵抗syn flood攻击,平时建议关闭
sysctl -w net.ipv4.tcp_syncookies=1              # tcp syncookie,默认关闭
sysctl -w net.ipv4.tcp_max_syn_backlog=1280   # syn队列,默认1024,> 1280可能工作不稳定,需要修改内核源码参数
sysctl -w net.ipv4.tcp_synack_retries=2             # syn-ack握手状态重试次数,默认5,遭受syn-flood攻击时改为1或2
sysctl -w net.ipv4.tcp_syn_retries=2                  # 外向syn握手重试次数,默认4 

# 以下一段为应对tcp connect连接耗尽攻击,如果开启iptables connlimit模块可禁用
# 有严重连接性能影响和不稳定因素,慎用
sysctl -w tcp_tw_recycle=1                           # 默认0,tw快速回收
sysctl -w tcp_tw_reuse=1                             # 默认0,tw重用
sysctl -w tcp_keepalive_intvl=60                    # 默认75,tcp keeplive探测轮询时间
sysctl -w tcp_keepalive_probes=3                  # 默认9,tcp keeplive探测轮询次数
sysctl -w tcp_keepalive_time=1800                # 默认7200,tcp keeplive时间
sysctl -w tcp_fin_timeout=30                        # 默认60,tcp fin状态超时时间
#sysctl -w net.ipv4.tcp_retries1=2                     # tcp连接重传参数,慎用
#sysctl -w net.ipv4.tcp_retries2=8
sysctl -w net.ipv4.ip_conntrack_max=65535          # 增大iptables状态跟踪表
为什么对称性nat不能打洞成功
1、nat的类型分为:
    (1) 完全锥型NAT(FULL CONE NAT)
        完全锥型NAT在内网用户A(Private Endpoint)首次向外部主机发送数据包时创建地址映射会话,
        并为A分配一个公网地址和端口(Public Endpoint),以后任何A向外部发送的数据都将使用这个Public Endpoint。
        此后,任何外部主机想要与A通信,只要将数据包发送到Public Endpoint上,A就能够顺利的进行接收。
    (2) 限制锥型NAT (RESTRICT CONE NAT)
        限制锥型NAT在内网用户A(Private Endpoint)首次向外部主机发送数据包时创建地址映射会话,
        并为A分配一个公网地址和端口(Public Endpoint),以后任何A向外部发送的数据包都将使用这个Public Endpoint。
        此后,如果某个外部主机(Endpoint IP:PORT)想要与A通信,只要将数据包发送到Public Endpoint并且
        保证A曾用当前与NAT的会话向该外部主机的IP地址发送过数据,A就能够正常收到外部主机(Endpoint IP:PORT)发送来的数据包。
    (3) 端口限制锥型NAT(PORT RESTRICT CONE NAT)
        端口限制锥型在内网用户A(Private Endpoint)首次向外部主机发送数据包时创建地址映射会话,
        并为A分配一个公网地址和端口(Public Endpoint),以后任何A向外部发送的数据都将使用这个Public Endpoint。
        此后,如果某个外部主机(Endpoint IP:PORT)想要与A通信,只要将数据包发送到Public Endpoint并且
        保证A曾用当前与NAT的会话向该外部主机的Endpoint发送过数据,A就能够正常收到外部主机(Endpoint IP:PORT)发送来的数据包。
    (4) 对称型NAT(SYMMETRIC NAT)
        对称型NAT是一种比较特殊的NAT。内网用户A(Private Endpoint)首次向外部主机S1发送数据包时创建地址映射会话Session1,
        并为A分配一个公网地址和端口(Public Endpoint1),以后A所有发向S1的数据包都使用这个Public Endpoint1。
        如果之后A用同一个Socket向外部主机S2发送数据包,这时对称型NAT又为其分配一个地址映射会话,
        并为A分配一个新的公网地址和端口对(Public Endpoint2),以后A所有发向S2的数据包都使用这个Public Endpoint2。
        对称型NAT规定Public Endpoint1和Public Endpoint2一定不相同。
        此外,如果任何外部主机想要发送数据给A,那么它首先应该收到A发给他的数据,然后才能往回发送,
        否则即使他知道内网主机的Public Endpoint也不能发送数据给A。
        这种NAT可以通过端口猜测等方法进行穿透,但是效果并不是很好,很难实现UDP-P2P通信。
2、锥形和对称形的区别是:对于锥形,B使用端口5555连接S的端口6666,在NAT_B转换为6200到6666,
    B使用端口5555连接A的端口4444,在NAT_B转换为6200到4444,也就是转换后的端口是一样的。
    对于对称形,前一次是6200,后一次可能是6201,反正两次不一样。
3、连接都是udp,现在考虑内网主机A和另一个内网主机B都连接了外网服务器S,S知道A和B的内网和外网ip和port。
    内网是消息发过来的,外网通过udp连接可以获取。
4、现在A去连接B,A该如何连接B呢?
    A和S说,我要去连接B,于是S告诉A有关B的内网和外网ip和port,注意:这个外网ip和port就是B连接S时NAT_B转换后的。
    去连接B的内网,不能成功。去连接B的外网,因为没有在NAT_B设置端口映射,在NAT_B会被丢弃。
5、如何不让NAT_B丢弃呢?
    需要B从里面打个洞出来,这个洞怎么打呢?
    S告诉B,A要来连接你,你向A那边在NAT_B上打个洞,这是A的内网和外网的ip和port,于是B去连接A的外网。
    如果是锥形,在外网上的端口和之前B连接S是一样的,和S为6200->6666,和A为6200->4444。
    由于之前S告诉A,B的外网端口就是6200,那么从A 的内网去连接6200是可以的。
6、但是如果是对称形,B向A打洞,端口是6201,那么从A 的内网去连接6200是不行的。
    也就是说,A期望去连接B的6200(这个6200就是B连接S的端口),但是B打的洞却是6201,当然不行。
为什么网络穿透使用udp,不使用tcp
1、考虑外网访问内网主机A,内网网关为NAT_A,从外网访问不了内网。需要主机A从内网在NAT_A上打个洞出来。
    从内网出来的请求,经过NAT_A转换后,对外的端口是一样的,假定都是6200
2、根据从外面请求的传入能力,将NAT_A分为全锥型,限制性锥型,端口限制性锥型。
    全锥型:外网都能传输进来
    限制性锥型:打洞对应的的IP才能传输进来
    端口限制性锥型:打洞对应的IP和Port才能传输进来。
3、对于udp,同一个端口既可以向外连接,又能够接受来自外部的连接,多个socket可以绑定同一个端口。
    但是对于tcp,套接字通过调用connect()函数来建立向外的连接,或者通过listen()和 accept函数接受来自外部的连接。
    而且tcp的套接字通常仅允许建立1对1的响应。
    即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。
测试udp打洞
1、正常使用场景是:主机A位于NAT_A后面,主机B位于NAT_B后面,NAT_A、NAT_B以及中介服务器S通过交换机在外网。
    A向B进行UDP连接,需要B向A方向,在NAT_B上打个洞,要求NAT_B是锥型NAT。
2、这个场景不好搭建,搭建一个更简单的场景进行测试。如下:
    主机A是192.168.1.160,udp绑定端口4444
    NAT_A是10.22.4.201 192.168.1.164 
    服务S是10.22.4.145,udp绑定端口6666
    从A[192.168.1.160:4444]去连接S[10.22.4.145:6666]没有问题,因为在NAT_A设置SNAT就好了。
    但是如果从S[10.22.4.145:6666]去连接A,怎么办呢?
    连接A的内网4444,显然不行。连接A的外网4444,因为没有在NAT_A设置端口映射,连接失败。
    为什么不在NAT_A对4444设置端口映射,因为往往不知道A的程序绑定端口,端口是变化的。
3、从S[10.22.4.145:6666]去连接A[10.22.4.201:4444]
    在A进行udp绑定,如下:
    [root@localhost ~]# nc -uv -l -p4444
    listening on [any] 4444 ...
    
    在S去连接A,如下:
    [niu_zibin@localhost sbin]$ nc -uv -p6666 10.22.4.201 4444
    10.22.4.201: inverse host lookup failed:  : No such file or directory
    (UNKNOWN) [10.22.4.201] 4444 (?) open
    open
    [niu_zibin@localhost sbin]$ 
    输入open直接报错
4、解决办法是先从A到S打个洞。
    在S进行udp绑定,如下:
    [niu_zibin@localhost sbin]$ nc -uv -l -p6666
    listening on [any] 6666 ...
    
    在A去连接S,如下:
    [root@localhost ~]# nc -uv -p4444 10.22.4.145 6666
    10.22.4.145: inverse host lookup failed:  : No such file or directory
    (UNKNOWN) [10.22.4.145] 6666 (?) open
    open
    
    hhh
    可以与S[10.22.4.145 6666]交互
    注意:在A看到的是A的内网到S的连接,在S看到的是S到A的外网连接,在NAT_A看不到连接。
5、现在关闭A的S的主动连接,之前A的绑定4444端口,可以与S进行交互了。如下:
    也就是说,A和S都有绑定udp端口,S去连接A失败,A向S打个洞(socket绑定相同的ip和port),
    这个时候S与这个洞可以交互,然后把这个洞关闭,S就可以与A的绑定端口交互了。
监听端口4个0
1、tcp或者udp作为服务端,监听的时候,监听地址为 0.0.0.0,这是什么意思?
2、正常情况下,使用bind系统调用,监听的时候,使用本机ip地址和一个端口。
3、考虑下面的情况,本地机器上的ip地址是变化的,本地机器也可能有多个ip地址
    特别是在多个网卡的情况下。比如有两个ip地址,如下:
    10.36.65.80
    192.168.1.80
    我监听端口9820,期望连接10.36.65.80和192.168.1.80的9820端口,都能收到。
    那么ip地址该怎么填写呢?
4、指定地址为0.0.0.0,表示本机监听的地址不确定,因为有多个ip地址,或者ip地址是变化的。
端口复用
1、无论是udp还是tcp,一个socket不能绑定两个端口。测试代码如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[])
    {
        char server_ip[30] = "10.22.4.145";
    
        // 初始化本地网络信息
        struct sockaddr_in my_addr;
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = AF_INET;
        my_addr.sin_port   = htons(8000);
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        int sockfd;
        sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
        if(sockfd < 0)
        {
                perror("socket");
                exit(-1);
        }
    
        // 第一次绑定端口8000
        int err_log;
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 8000");
                close(sockfd);
                exit(-1);
        }
    
        // 又一次绑定别的端口9000, 会绑定失败
        my_addr.sin_port = htons(9000);
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 9000");
                close(sockfd);
                exit(-1);
        }
        close(sockfd);
        return 0;
    }
    
    [niu_zibin@localhost socket]$ g++ -o main main.cpp
    [niu_zibin@localhost socket]$ ./main
    bind 9000: Invalid argument
    
    改成tcp,也就是 sockfd = socket(AF_INET, SOCK_STREAM, 0); 测试结果一样。
2、udp和tcp是传输层的两个协议,他们之间的端口不搭嘎。也就是udp的socket绑定8000,tcp的socket也可以绑定8000,如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[])
    {
        char server_ip[30] = "10.22.4.145";
    
        // 初始化本地网络信息
        struct sockaddr_in my_addr;
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = AF_INET;
        my_addr.sin_port   = htons(8000);
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        int sockfd;
        sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
        if(sockfd < 0)
        {
                perror("socket");
                exit(-1);
        }
    
        // UDP绑定端口8000
        int err_log;
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 8000");
                close(sockfd);
                exit(-1);
        }
    
        // TCP绑定8000
        my_addr.sin_port = htons(8000);
        int sockfd_2;
        sockfd_2 = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
        if(sockfd < 0)
        {
                perror("socket");
                exit(-1);
        }
    
        err_log = bind(sockfd_2, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 8000");
                close(sockfd);
                exit(-1);
        }
        // 注意:TCP和UDP不同,TCP绑定之后,还必须要listen
        listen(sockfd_2,10);
    
        getchar();
        close(sockfd);
        close(sockfd_2);
        return 0;
    }
    
    运行
    [niu_zibin@localhost 2]$ g++ -o main main.cpp 
    [niu_zibin@localhost 2]$ ./main
    
    查看绑定信息
    [niu_zibin@localhost ~]$ netstat -anp|grep 8000
    (Not all processes could be identified, non-owned process info
    will not be shown, you would have to be root to see it all.)
    tcp        0      0 0.0.0.0:8000                0.0.0.0:*                   LISTEN      15692/main          
    udp        0      0 0.0.0.0:8000                0.0.0.0:*                               15692/main 
3、默认情况下,对于udp,两个socket不能绑定同一个端口,对于tcp也是一样。如何重复绑定同一个端口?
    对于udp,使用SO_REUSEADDR,两个socket可以重复绑定同一个端口,如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[])
    {
        char server_ip[30] = "10.22.4.145";
    
        // 初始化本地网络信息
        struct sockaddr_in my_addr;
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = AF_INET;
        my_addr.sin_port   = htons(8000);
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        int sockfd;
        sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
        if(sockfd < 0)
        {
                perror("socket");
                exit(-1);
        }
    
        int err_log;
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd绑定端口8000
        int opt = 1;
        setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 8000");
                close(sockfd);
                exit(-1);
        }
    
        int sockfd_2;
        sockfd_2 = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
        if(sockfd_2 < 0)
        {
                perror("socket");
                exit(-1);
        }
    
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd_2绑定端口8000
        setsockopt(sockfd_2,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));
        err_log = bind(sockfd_2, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
                perror("bind 8000");
                close(sockfd_2);
                exit(-1);
        }
        getchar();
        close(sockfd);
        close(sockfd_2);
        return 0;
    }
    注意:每个socket绑定之前,都要设置SO_REUSEADDR。
        如果第一个socket绑定端口8000之前,没有设置端口复用,第二个socket先设置端口复用,然后绑定8000,也会报错
        bind 8000: Address already in use

    可以看到进程对于udp的8000端口,绑定了两次,如下:
    [niu_zibin@localhost ~]$ netstat -anp|grep 8000
    (Not all processes could be identified, non-owned process info
    will not be shown, you would have to be root to see it all.)
    udp        0      0 0.0.0.0:8000                0.0.0.0:*                               28554/main          
    udp        0      0 0.0.0.0:8000                0.0.0.0:*                               28554/main 
4、但是对于tcp,即使设置了SO_REUSEADDR,也不能复用端口。
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[])
    {
        char server_ip[30] = "10.22.4.145";
    
        // 初始化本地网络信息
        struct sockaddr_in my_addr;
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = AF_INET;
        my_addr.sin_port   = htons(8000);
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        int sockfd;
        sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
        if(sockfd < 0)
        {
            perror("socket");
            exit(-1);
        }
        
        int err_log;
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd绑定端口8000
        int opt = 1;
        setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));    
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
            perror("bind 8000");
            close(sockfd);
            exit(-1);
        }
        listen(sockfd,10);
    
        int sockfd_2;
        sockfd_2 = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
        if(sockfd_2 < 0)
        {
            perror("socket");
            exit(-1);
        }
    
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd_2绑定端口8000
        setsockopt(sockfd_2,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt)); 
        err_log = bind(sockfd_2, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
            perror("bind 8000");
            close(sockfd_2);
            exit(-1);
        }
        listen(sockfd_2,10);
    
        getchar();
        close(sockfd);
        close(sockfd_2);
        return 0;
    }
    
    报错如下:
    [niu_zibin@localhost 4]$ g++ -o main main.cpp
    [niu_zibin@localhost 4]$ ./main
    bind 8000: Address already in use
5、需要注意的是,对于udp,绑定端口就好了。但是对于tcp,只是绑定没有用的。
    绑定之后,还必须要listen,才能看到tcp的监听。
6、对于udp,端口可以复用,绑定同一个端口。但是对于tcp,不能在同一个端口监听,或者建立相同的连接四元组。
    那么端口复用对于tcp有什么意义呢?
7、首先,对于相同的端口,可以在不同的ip地址上监听。它的意义在于主机上会有多个物理网卡,
    或者一个物理网卡配置了多个逻辑ip,或者表现形式不同.
    比如当前主机为10.22.4.145,tcp可以分别在10.22.4.145,127.0.0.1,0.0.0.0上监听同一个端口。
    使用网络助手NetAssist也可以测试。
    测试代码如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[])
    {
        char server_ip[30] = "10.22.4.145";
    
        // 初始化本地网络信息
        struct sockaddr_in my_addr;
        bzero(&my_addr, sizeof(my_addr));
        my_addr.sin_family = AF_INET;
        my_addr.sin_port   = htons(8000);
        my_addr.sin_addr.s_addr = inet_addr("10.22.4.145");
    
        int sockfd;
        sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建UDP套接字
        if(sockfd < 0)
        {
            perror("socket");
            exit(-1);
        }
        
        int err_log;
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd绑定端口8000
        int opt = 1;
        setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt));    
        err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
            perror("bind 8000");
            close(sockfd);
            exit(-1);
        }
        listen(sockfd,10);
    
        int sockfd_2;
        sockfd_2 = socket(AF_INET, SOCK_STREAM, 0); //创建UDP套接字
        if(sockfd_2 < 0)
        {
            perror("socket");
            exit(-1);
        }
    
        // 在sockfd绑定bind之前,设置其端口复用
        // sockfd_2绑定端口8000
        my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        setsockopt(sockfd_2,SOL_SOCKET,SO_REUSEADDR,(const void*)&opt,sizeof(opt)); 
        err_log = bind(sockfd_2, (struct sockaddr*)&my_addr, sizeof(my_addr));
        if(err_log != 0)
        {
            perror("bind 8000");
            close(sockfd_2);
            exit(-1);
        }
        listen(sockfd_2,10);
    
        getchar();
        close(sockfd);
        close(sockfd_2);
        return 0;
    }
    
    查看监听情况,如下:
    [niu_zibin@localhost ~]$ netstat -anp|grep 8000
    (Not all processes could be identified, non-owned process info
    will not be shown, you would have to be root to see it all.)
    tcp        0      0 127.0.0.1:8000              0.0.0.0:*                   LISTEN      28865/main          
    tcp        0      0 10.22.4.145:8000            0.0.0.0:*                   LISTEN      28865/main
    注意:这种情况,不设置SO_REUSEADDR,也可以在不同ip上的相同端口监听。
8、tcp还有一种情况,相同的tcp连接四元组,比如当前连接被动断开连接,处于time_wait状态。
    这时候设置SO_REUSEADDR,可以在这个端口上监听。
    当一个socketA被动断开连接,处于time_wait状态,另一个socketB可以在相同的ip和port上监听。
    注意:如果不想经历TIME_WAIT的过程,设置如下:
  BOOL bDontLinger = FALSE; 
  setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&amp;bDontLinger,sizeof(BOOL));
9、对于udp,设置了SO_REUSEADDR,相同IP和port,可以重复绑定。也就是说,两个套接字可以绑定相同的ip和port。
    如果外部来的数据报目的地址是单播,则只有最后一个socket能收到数据。
    如果外部来的数据报目的地址是多播,则多个socket都能收到相同的数据。
    也就是说,相同ip和port的绑定,用于udp的多播。对于udp,相同ip和port都能够重复绑定,有一个不同,更能够绑定。
10、linux上,tcp不能在相同的ip和port监听。对于freebsd(类unix操作系统),提供了SO_REUSEPORT,
    可以让tcp的socket绑定相同的ip和port,只有第一个socket能收到数据。
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.