tcp套接字选项设置及解析

  我们在使用套接字的时候,经常需要设置socket的属性,下面就在ubuntu环境下,使用C++简要的描述一下获取套接字属性,以及设置套接字属性的简单步骤。

同时针对其中的4个套接字选项进行稍微深入的分析。

  在linux中,获取套接字属性有多种方法,其中比较常用的是getsockopt,设置套接字常用的是setsockopt,他们的原型如下。

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

  其中的level分为SOL_SOCKET,是针对套接字的一般通用选项,还有IPPROTO_TCP,这个主要是针对TCP的套接字选项。还有针对IPv6之类,不是这里主要讨论的。

在SOL_SOCKET的level下,我们挑选SO_LINGER, SO_RCVBUF, SO_SNDBUF 3个进行一下说明, IPPROTO_TCP选择TCP_NODELAY。

  需要注意的是,不同的选项(optname),在get和set属性的时候,使用的数据类型是不一样的,SO_LINGER使用的是一种特定的结构体,SO_RCVBUF, SO_SNDBUF使用的是int类型。获取属性和设置属性的简单代码如下所示:

void GetOptInfo(int sock)
{
    char szInfo[128]= {0};
    int nVal = 0;
    struct linger lng;
    socklen_t nLen = sizeof(lng);
    int nRet = getsockopt(sock, SOL_SOCKET, SO_LINGER, &lng, &nLen);
   
    nLen = sizeof(nVal);
    snprintf(szInfo, sizeof(szInfo)-1, "onoff:%d,linger:%d", lng.l_onoff, lng.l_linger);
    printf("SO_LINGER %s\n", szInfo);

    getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &nVal, &nLen);
    snprintf(szInfo, sizeof(szInfo)-1, "SO_RCVBUF val:%d", nVal);
    printf("%s\n", szInfo);
    
    getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &nVal, &nLen);
    snprintf(szInfo, sizeof(szInfo)-1, "SO_SNDBUF val:%d", nVal);
    printf("%s\n", szInfo);

    getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &nVal, &nLen);
    snprintf(szInfo, sizeof(szInfo)-1, "TCP_MAXSEG val:%d", nVal);
    printf("%s\n", szInfo);

    getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &nVal, &nLen);
    snprintf(szInfo, sizeof(szInfo)-1, "TCP_NODELAY val:%d", nVal);
    printf("%s\n", szInfo);
}

void SetOpt(int sock)
{
    struct linger lng;
    socklen_t nSize = sizeof(lng);
    lng.l_onoff = 1;
    lng.l_linger = 1;
    int nRet = setsockopt(sock, SOL_SOCKET, SO_LINGER, &lng, nSize);
    if (nRet != 0)
        cout << "set so_linger error" << endl;

    nSize = sizeof(int);
    int nRcvBuf = 4096;
    nRet = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &nRcvBuf, nSize);
    if (nRet != 0)
        cout << "set so_rcvbuf error" << endl;
int nSndBuf = 200; nRet = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &nSndBuf, nSize); if (nRet != 0) cout << "set so_sndbuf error" << endl; int nNodelay = 1; nRet = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &nNodelay, nSize); if (nRet != 0) cout << "set tcp_nodelay error" << endl; }

  获取和设置套接字选项都很简单,不过对于每个选项的理解就需要深入一点。

1、SO_LINGER用来设置当套接字关闭的时候,不需要进行等待,直接关闭,也就是说不在TIME_WAIT阶段进行停留。由于只有TCP连接才会有关闭连接的4次过程,所以这个选项虽然是通用的套接字选项,不过只对tcp有用。

2、SO_RCVBUF,SO_SNDBUF用来设置接收缓冲区,和发送缓冲区的。如果我们再set的时候将他们分别设置成2000,1000(例如这2个值),在get的时候会发现,返回的不是2000,1000,而是4000和2000. 至于为什么这样,在使用man setsockopt的时候也没有描述。这个时候,需要使用man 7 socket入口来进行查看。通过文档,我们可以知道这是因为内核对于我们设置的缓冲区大小会自动调整为2倍,所以在获取的时候,也就变成了2倍。而且针对发送缓冲区,是和MSS有关的,如果我们设置的发送缓冲区比MSS小,系统会自动扩展为MSS2倍以上的空间,在我的系统测试中,会变成2048,而MSS是536。

3、TCP_NODELAY是针对tcp套接字专用的选项,目的是为了禁止nagle算法。nagle算法为了防止网络传输中的小分组太多,影响网络性能,会自动将收到的多个数据包的返回包,整合成一个比较大的分组返回给对方,这种情况下,请求方在发送前几个小分组的时候可能不能立即收到对方的响应,在某些实时性要求比较高的应用中,需要屏蔽掉nagle算法,好让请求离开得到响应。

posted @ 2012-09-27 20:03  lovemychobits  阅读(2686)  评论(0)    收藏  举报