融汇贯通实用的socket属性典型讲解

之前已经大概了解setsockopt的作用,但是没有贯通起来,下面这段转载,基本上让人对socket属性设置的理解,融汇贯通起来。

setsockopt设置SO_REUSEADDR。

socket关闭之后并不会立即收回,而是要经历一个TIME_WAIT的阶段。windows下最多可以达到4分钟。

所以在这个时候对这个端口进行重新绑定就会出错。所以需要先设置 SO_REUSEADDR.

或者在closesocket的时候,使用setsockopt设置SO_DONTLINGER。也不会有TIME_WAIT的阶段.

通常使用这个设置来加强网络程序的健壮性。

 

另外,这些属性还和socket是否为阻塞或非阻塞模式有关联...择适而用之

 

下面是别处补充的资料:

1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用
      closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
      BOOL bReuseaddr=TRUE;
      setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const 
      char*)&bReuseaddr,sizeof(BOOL));

      2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历
      TIME_WAIT的过程:
      BOOL bDontLinger = FALSE; 
      setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const 
      char*)&bDontLinger,sizeof(BOOL));

      3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:
      int nNetTimeout=1000;//1秒
      //发送时限
      setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char 
      *)&nNetTimeout,sizeof(int));
      //接收时限
      setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char 
      *)&nNetTimeout,sizeof(int));

      4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节
      (异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据
      和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:
      // 接收缓冲区
      int nRecvBuf=32*1024;//设置为32K
      setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
      //发送缓冲区
      int nSendBuf=32*1024;//设置为32K
      setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));

      5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响
      程序的性能:
      int nZero=0;
      setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));

      6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):
      int nZero=0;
      setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));

      7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
      BOOL bBroadcast=TRUE; 
      setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const 
      char*)&bBroadcast,sizeof(BOOL));

      8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可
      以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的
      作用,在阻塞的函数调用中作用不大)
      BOOL bConditionalAccept=TRUE;
      setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const 
      char*)&bConditionalAccept,sizeof(BOOL));

      9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们
      一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体
      应用的要求(即让没发完的数据发送出去后在关闭socket)?
      struct linger {
      u_short l_onoff;
      u_short l_linger;
      };
      linger m_sLinger;
      m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
      // 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
      m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
      setsockopt(s,SOL_SOCKET,SO_LINGER,(const 
      char*)&m_sLinger,sizeof(linger));
      Note:1.在设置了逗留延时,用于一个非阻塞的socket是作用不大的,最好不用;2.如果想要程序不经历SO_LINGER需要设置SO_DONTLINGER,或者设置l_onoff=0;

      10.还一个用的比较少的是在SDI或者是Dialog的程序中,可以记录socket的调试信息:
      (前不久做过这个函数的测试,调式信息可以保存,包括socket建立时候的参数,采用的
      具体协议,以及出错的代码都可以记录下来)
      BOOL bDebug=TRUE;
      setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&bDebug,sizeof(BOOL));

 

 

==============

SO_REUSEADDR是用在服务器端socket关闭后处在TIME_WAIT阶段时,socket所对应的地址(IP+PORT)可以被重新绑定。常用在服务器程序中,服务器使用固定端口,当服务器产生异常并重启后,有可能服务器所使用的socket所占用的地址尚未彻底关闭,即处在TIME_WAIT阶段,这时候,默认情况下此地址是不可以被重新使用的,直到TIME_WAIT结束。但假如设置了此选项,此地址便可以新启动的服务器socket绑定并在此地址上监听连接。当然如果想重用地址和端口,则旧的socket和新的socket都要已经被设置了SO_REUSEADDR特性,只有两者之一有这个特性会产生问题。还有就是使用此选项最好是在创建socket成功之后立即设定,不要在bind等操作后设定,因为如果这此操作失败返回,此选项相当于没有设置,下次再使用时就不能生效。

       一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。 每个连接都是通过它的本地及远程地址的组合“独一无二”地标识出来的,针对我们想要连接的地址,只要能用极其细微的差异(比如TCP/IP中采用不同的端口号),来维持这种“独一无二”或者“唯一”的特点,所以我们需要绑定机制。但默认情况下两个独立的套接字不可与同一个本地接口(在TCP/IP的情况下,则是端口)绑定到一起,以等待进入的连接。 假定两个套接字都在同一个端口上进行监听,那么到底由谁来接收一个进入连接通知呢?对于这个问题,尚无一种正式规范提出了解决方案。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到对方发送的非期望数据。从而造成服务程序反应混乱,所以必须慎重使用SO_REUSEADDR 选项我们常用的解决方法是对服务器刚起来时收到的数据不予理睬,使用select将不希望的数据全部接收,然后扔掉。

       这个问题在Richard Stevens的《Unix网络编程指南》卷一里有很详细的解答(中文版P166-168页)。下面的几个基本的例子验证了这个问题。

SO_REUSEADDR可以用在以下四种情况下。

(摘自《Unix网络编程》卷一,即UNPv1)

1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。

3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。

4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

========

更对内容参考:

http://blog.163.com/tianle_han/blog/static/6617826200822031512575/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值