socket选项总结(setsocketopt)

功能描述:

获取或者设置与某个套接字关联的选项。选项可能存在于多层协议中,他们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议号TCP。

用法:

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

参数:

sock: 将要被设置或者选项的套接字。

level:选项所在的协议层。

optname: 想要访问的选项名。

optval: 对于getsockopt(), 指向返回选项值的缓冲。对于setsockopt(), 指向包含新选项值的缓冲。

optlen: 对于getsockopt(), 作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现现象的长度。

返回说明:

成功执行时,返回0.失败返回-1.errno被设为以下的某个值

EBADF:sock不是有效的文件描述词。

EFAULT: optval指向的内存并非有效的进程空间

EINVAL:在调用setsockopt()时,optlen无效

ENOPROTOPT:指定的协议层不能识别选项

ENOTSOCK:sock描述的不是套接字。


参数详细说明:

level指定控制套接字的层次,可以取三种值:

1)SOL_SOCKET:通用套接字选项

2)IPPROTO_IP:IP选项

3)IPPROTO_TCP:TCP选项

optname指定控制的方式(选项的名称),我们下面详细解释:

optval获得或者是设置套接字选项,根据选项名称的数据类型类型进行转换

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

选项名称                                                           说明                                                    数据类型

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

                                                               SOL_SOCKET

-----------------------------------------------------------------------------------------------------------------------

SO_BROADCAST                                     允许发送广播数据                                      int

SO_DEBUG                                                允许调试                                                      int

SO_DONETROUTE                                  不查找路由                                                  int

SO_ERROR                                                 获得套接字错误                                        int

SO_KEEPALIVE                                          保持连接                                                    int

SO_LINGER                                                 延迟关闭连接                                            struct linger

SO_OOBINLINE                                        带外数据放入正常数据流                         int

SO_RCVBUF                                              接受缓冲区大小                                         int

SO_SNDBUF                                              发送缓冲区大小                                        int

SO_RCVLOWAT                                         接受缓冲区下限                                       int

SO_SNDLOWAT                                         发送缓冲区下限                                      int

SO_RCVTIMEO                                          接受超时                                                 struct timeval

SO_SNDTIMEO                                          发送超时                                                 struct timeval

SO_REUSEADDR                                     允许重用本地地址和端口                      int

SO_TYPE                                                     获得套接字类型                                     int

SO_BSDCOMPAT                                      与BSD系统兼容                                     int

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

                                                                      IPPROTO_IP

-------------------------------------------------------------------------------------------------------------------------

IP_HDRINCL                                                 在数据包中包含IP首部                       int

IP_OPTIONS                                                 IP首部选项                                           int

IP_TOS                                                           服务类型                                  

IP_TTL                                                            生存时间                                             int

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

                                                                  IPPRO_TCP

-------------------------------------------------------------------------------------------------------------------------

TCP_MAXSEG                                              TCP最大数据段的大小                      int

TCP_NODELAY                                             不使用Nagle算法                              int

=====================================================================
SO_RCVBUF和SO_SNDBUF每个套接口都有一个发送缓冲区和一个接受缓冲区,使用这两个套接字选项可以改变缺省缓冲区大小。

//接受缓冲区

int nRecvBuf = 32 * 1024;

setsockopt(fd, 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));

注意:

当设置TCP套接口接受缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,O_RCVBUF选项必须在connet之前设置;对于服务器端,SO_RCVBUF选项必须在listen前设置。

结合原理说明:

1.每个套接口都有一个发送缓冲区和一个接收缓冲区。接收缓冲区被TCP和UDP用来将接收到的数据一致保存到应用进程来读。TCP:TCP通告另一端的窗口大小。TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。这就是TCP的流量控制,如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP将丢弃它。UDP:当接收到的数据报装不进套接口缓冲区时,此数据报就被丢弃。UDP是没有流量控制的;快的发送者可以很容易地就淹没慢的接受者,导致接收方的UDP丢弃数据报。

2.我们经常听说tcp协议的三次握手,但三次握手到底是什么,其细节是什么,为什么要这么做呢?

第一次:客户端发送连接请求给服务器,服务器接收;

第二次:服务器返回客户端一个确认码,附带一个从服务器到客户端的连接请求,客户机接收,确认客户端到服务器的连接。

第三次:客户端返回服务器上次发送请求的确认码,服务器接收,确认服务器到客户端的连接。

我们可以看到:

1.tcp的每个连接都需要确认。

2.客户端到服务器和服务器到客户端的连接是独立的。

我们再想想tcp协议的特点:连接的,可靠的,全双工的,实际上TCP的三次握手正是为了保证这些特性的实现。

 

3.setsockopt的用法

1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想重用该socket。

BOOL BReuseaddr=True

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&BReuseaddr, sizeof(BOOL));

2.如果要已经处于连接状态的socket在调用closescoket后强制关闭,不经历TIME_WAIT的过程:

BOOL bDontLinger = FALSE;

setsockopt(fd, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(BOOL));

3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:

int nNetTimeOut = 1000;//1秒

//发送时限

setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeOut, sizeof(int));

//接收时限

setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeOut, sizeof(int));

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

//接收缓冲区

int nRecvBuf = 32 * 1024; // 设置为32K

setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));

//发送缓冲区

int nSendBuf = 32 * 1024; //设置为32K

setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBuf, sizeof(int));

5.如果在发送数据时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

int nZero = 0;

setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&nZero, sizeof(nZero));

6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

int nZero = 0;

setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&nZero, sizeof(int));

7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

BOOL bBroadcast = TRUE;

setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(BOOL));

8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connet()的过程中可以设置connet()延时,直到accept()被呼叫(本函数设置只有在非阻塞的过程中有明显的作用,在非阻塞的函数调用中作用不大)

BOOL bConditionAccept = TRUE;

setsockopt(fd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char*)&bConditionAccept, 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; //(容许逗留的时间的时间)

setsockopt(s, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(linger));

注意:两种套接口的选项:一种是布尔型选项,允许或禁止一种特性;另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。对于布尔型选项,optlen应等于sizeof(int);而其他选项,optval指向包含所需选项的整形数或结构,而optlen则时整形数或结构的长度。SO_LINGER选项用于控制下述情况的行为:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相关的操作特性:

struct linger{

int l_onoff;

int l_linger;
}

为了允许SO_LINGER,应用程序应将l_onoff设为非零,将l_linger设为零或需要的超时值(以秒为单位),然后调用setsockopt()。为了允许SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff应设为零,然后调用setsockopt()。

缺省条件下,一个套接口不能与一个已有使用中的本地地址绑定(参见bind())。但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址绑定并无大碍。为了通知套接口实现不要因为一个地址已被一个套接口使用就不让与另一个套接口绑定,应用程序可与bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会公用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这个选项。

一个应用程序可以通过打开SO_KEEPALIVE选项,使得套接口实现在TCP间接情况下允许使用“保持活动”包。一个套接口实现并不是必须支持“保持活动”,但是如果支持的话,具体的语义将与实现有关。

 

TCP_NODELAY选项禁止Nagle算法。Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据报的数目。但对于某些应用来说,这种算法将降低系统性能。所以TCP_NODELAY可用来将此算法关闭。应用程序编写者只有在确切了解它的效果并确实需要的情况下,才设置TCP_NODELAY选项,因为设置后对网络性能有明显的负面影响。TCP_NODELAY是唯一使用OPPRO_TCP层的选项,其他所有选项都使用SOL_SOCKET层。

如果设置了SO_DEBUG选项,套接口供应商被鼓励(但不是必须)提供输出相应的调试信息。但产生调试信息的机制以及调试信息的形式已超出本规范的讨论范围。

 

在TCP连接中,recv等函数默认为阻塞模式(block),即直到有数据到来之前函数不会返回,而我们有时则需要一种超时机制使其在一定时间后返回而不管是否有数据到来,这里我们就会用到setsockopt()函数:

int setsockopt(int s, int level, int optname, (void *)optval, socklen_t* optlen);

这里我们要涉及到一个结构

struct timeval

{

      time_t tv_sec;

      time_t tv_usec;

}

这里第一个域的单位是秒,第二个域的单位是微妙。

struct timeval tv_out;

tv_out.tv_sec = 1;

tv_out.tc_usec = 0;

填充这个结构后,我们就可以以如下的方式调用这个函数:

setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

这样我们就设定了recv()函数的超时机制,当超过tv_out设定的时间而没有数据到来时recv()就会返回0值。

 

第二个我们要介绍的是多路复用机制,也就是同时监听多个套接字连接。

int select(int n, fd_set *readfds, fd_set* writefds, fd_set* exceptds, struct timeval *timeout);

这里涉及到了fd_set结构:

typedef struct fd_set

{

      u_int fd_count;

      int fd_array[FD_SETSIZE[;

}

fd_count为fd_set结构中包含的套接字个数,fd_array唯一个int数组,包含了我们要监听的套接字。

首先我们需要使用FD_SET将我们要监听的套接字添加到fd_set结构中:

fd_set readfd;

FD_SET(fd, &readfd);

然后我们这样调用select函数:

select(max_fd + 1, &readfd, NULL,NULL,NULL)

FD_ISSET(fd, &readfd);

其中max_fd为我们要监听的套接字中值最大的一个,同时在调用select是要将其加1,readfd即为我们监听的要进行读操作的套接字连接,第三个参数是我们监听的要进行写操作的套接字连接,第四个参数用于异常,而最后一个参数用来设定超时,这里同时使用了struct timeval结构,可以实现与前面介绍相同的效果。这里如果连接进来的话select即select即返回一个大于0的值,然后我们调用FD_ISSET宏来检测具体是哪一个套接字由数据进来(FD_ISSET返回非零值)。

最后介绍的是另一种实现非阻塞的方式,这种方法在有些应用中会起到一定作用,尤其在select函数监听的套接字个数超过1024个时(因为fd_set结构在大部分UNIX系统中都对其可以监听的套接字个数作了1024的限制,如果要突破这个限制,必须修改头文件并重新编译内核),我们就不能使用select多路复用机制。

拿recv()函数来说,我们可以这样进行调用;

recv(fd, buf, sizeof(buf, MSG_DONTWAIT);

注意到我们这里采用了MSG_DONTWAIT标志,它的作用是告诉recv函数如果有数据到来的话就接收全部数据立即返回,没有数据的话也是立即返回,而不进行任何的等待。采用这个机制就可以在多于1024个套接字连接时使用for()循环对全部的连接进行监听。

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值