网络编程学习笔记(九)套接字的多种可选项

本文探讨了网络编程中套接字的多种可选项,包括getsockopt和setsockopt函数的使用,以及SO_SNDBUF、SO_RCVBUF、SO_REUSEADDR和TCP_NODELAY等关键选项。详细讲解了Nagle算法的作用和禁用场景,并提供了Windows平台下的实现示例。
摘要由CSDN通过智能技术生成

套接字多种可选项

我们之前写的程序都是创建好套接字后(未经特别操作)直接使用的,此时通过默认的套接字特性进行数据通信。之前的示例较为简单,无需特别操作套接字特性,但有时的确需要更改。

协议层选项名读取设置
SOL_SOCKETSO_SNDBUF
SOL_SOCKETSO_RCVBUF
SOL_SOCKETSO_REUSEADDR
SOL_SOCKETSO_KEEPALIVE
SOL_SOCKETSO_BROADCAST
SOL_SOCKETSO_DONTROUTE
SOL_SOCKETSO_OOBINLINE
SOL_SOCKETSO_ERROR
SOL_SOCKETSO_TYPE
协议层选项名读取设置
IPPROTO_IPIP_TOS
IPPROTO_IPIP_TTL
IPPROTO_IPIP_MULTICAST_TTL
IPPROTO_IPIP_MULTICAST_LOOP
IPPROTO_IPIP_MULTICAST_IF
协议层选项名读取设置
IPPROTO_TCPTCP_KEEPALIVE
IPPROTO_TCPTCP_NODELAY
IPPROTO_TCPTCP_MAXSEG

从表中可以看出,套接字可选项是分层的。IPPROTO_IP层可选项是IP协议相关事项,IPPROTO_TCP层可选项是TCP协议相关事项,SOL_SOCKET层是套接字相关的通用可选项。

getsockopt&setsockopt

我们几乎可以针对表中的所有可选项进行读取(Get)和设置(Set),可选项的读取和设置通过如下两个函数完成。

#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, 
                                                socklen_t*optlen);
//sock     用于查看选向套接字文件描述符
//level    要查看的可选项的协议层
//optname  要查看的可选项名
//optval   保存查看结果的缓冲地址值
//optlen   向第4个参数optval传递的缓冲大小。调用函数后,该变量中保存通过第四个参数返回的可选项信息的字节数
//成功时返回0,失败时返回-1                        

下面介绍更改可选项时调用的函数

#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void*optval, socklen_t optlen);
变量对应上面函数,不过这次是用于更改的值。

下面介绍这些函数的调用方法。
sock_type.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int tcp_sock, udp_sock;
    int sock_type;
    socklen_t optlen;
    int state;

    optlen = sizeof(sock_type);
    tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
    udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
    printf("SOCK_STREAM: %d \n", SOCK_STREAM);
    printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);

    state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    if(state)
        error_handling("getsockopt() error!");
    printf("Socket type one: %d \n",sock_type);

    state = getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    if(state)
        error_handling("getsockopt() error!");
    printf("Socket type two: %d \n",sock_type);

    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

套接字类型只能在创建时决定, 以后不能再更改。

SO_SNDBUF & SO_RCVBUF

前面介绍过,创建套接字将同时生成I/O缓冲。SO_REVBUF是输入缓冲大小相关可选项,也可以进行更改。通过下列示例读取创建套接字时默认的I/O缓冲大小。
get_buf.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    int snd_buf, rcv_buf, state;
    socklen_t len;

    sock = socket(PF_INET, SOCK_STREAM, 0);
    len = sizeof(snd_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
    if(state)
        error_handling("getsockopt() error");

    len = sizeof(rcv_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&rcv_buf, &len);
    if(state)
        error_handling("getsockopt() error");

    printf("Input buffer size: %d \n",rcv_buf);
    printf("Output buffer size: %d \n", snd_buf);

    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

接下来程序中将更改I/O缓冲大小
set_buf.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUF_SIZE 3096
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    int snd_buf = BUF_SIZE, rcv_buf = BUF_SIZE, state;
    socklen_t len;

    sock = socket(PF_INET, SOCK_STREAM, 0);
    state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
    if(state)
        error_handling("getsockopt() error1");


    state = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));
    if(state)
        error_handling("getsockopt() error2");

    len = sizeof(snd_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
    if(state)
        error_handling("getsockopt() error3");

    len = sizeof(rcv_buf);
    state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
    if(state)
        error_handling("getsockopt() error4");

    printf("Input buffer size: %d \n",rcv_buf);
    printf("Output buffer size: %d \n", snd_buf);

    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

缓冲大小的设置须谨慎处理,因此不会完全按照我们的要求进行。

SO_REUSEADDR

重点是可选项SO_REUSEADDR及其相关的Time-wait状态。
Time-wait状态,四次挥手过程中,先断开连接的主机经过Time-wait状态才结束。所以若服务器先断开连接,则无法立即重新运行。套接字处在Time-wait过程时,相应端口还是正在使用的状态。因此bind函数调用过程中当然会发生错误。
Time-wait也有很大的缺点,如果网络状态不理想,那么Time-wait状态有可能会持续较长一段时间。解决方案就是在套接字的可选项中更改SO_REUSEADDR的状态。适当调整参数,可将Time-wait状态下的套接字端口号重新分配给新的套接字。
代码实现实际上就是3句话:
SO_REUSEADDR默认值为0(假,此时无法重新分配),我们将其设置为真。

optlen = sizeof(option);
option = TRUE;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*) &option, optlen);

TCP_NODELAY

Nagle算法

这个算法是为防止数据包过多而发生网络过载而在1984就诞生了。应用于TCP层,很简单。
简单描述就是:只有收到前一数据的ACK消息时,Nagle算法才发送下一数据。
TCP套接字默认使用Nagle算法交换数据,因此最大限度地进行缓冲,直到收到ACK。
一般情况下,不使用Nagle算法可以提高传输速度。但如果无条件放弃使用Nagle算法,就会增加过多的网络流量,反而会影响传输。

禁用Nagle算法

禁用的典型情景就是“传输大文件数据”。Nagle算法使用与否在网络流量上差别不大时,使用Nagle算法的传输速度更慢。禁用方法非常简单,只需将套接字可选项TCP_NODELAY改为1(真)即可。

int optval = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val,sizeof(opt,val));

基于windows的实现

#include <winsock2.h>
int getsockopt(SOCKET sock, int level, int optname, char *optval, int *optlen);
//可以看到,除了optval类型变成char指针外,与Linux中的getsockopt函数相比并无太大区别。
//成功时返回0,失败时返回SOCKET_ERROR
#include <winsock2.h>
int setsockopt(SOCKET sock, int level, int optname, 
                                const char * optval, int optlen);
//成功时返回0,失败时返回SOCKET_ERROR
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值