套接字中可以设置和获取多种可选项,包括套接字的类型(TCP套接字还是UDP套接字)、接收和发送缓冲区的大小等等。下图就是套接字中支持的可选项:
level是可选项的分层,SOL_SOCKE是socket级别(对应应用层)的可选项,其中我们常用的设置输入/输出缓冲区的大小(SO_RCVBUF和SO_SNDBUF)就在该层;PPROTO_TCP是TCP/UDP级别(对应传输层)的可选项的设置,我们可以进行Nagle算法的开启或者关闭等等;IPPROTO_IP、IPPROTO_IPV6和IPPROTO_ICMPV6对应于网络层的选项的设置,只不过分别用于ip4、ip6、icmpv6等协议相关的设置。
Optname是设置的可选项的名称。而get和set是表明该可选项是否支持可读或是可写。
level | Optname | get | set | 说明 | 标志 | 数据类型 |
SOL_SOCKET | SO_BROADCAST | y | y | 允许发送广播数据报 | y | int |
SO_DEBUG | y | y | 使能调试跟踪 | y | int | |
SO_DONTROUTE | y | y | 旁路路由表查询 | y | int | |
SO_ERROR | y | 获取待处理错误并消除 | int | |||
SO_KEEPALIVE | y | y | 周期性测试连接是否存活 | y | int | |
SO_LINGER | y | y | 若有数据待发送则延迟关闭 | linger{} | ||
SO_OOBINLINE | y | y | 让接收到的带外数据继续在线存放 | y | int | |
SO_RCVBUF | y | y | 接收缓冲区大小 | int | ||
SO_SNDBUF | y | y | 发送缓冲区大小 | int | ||
SO_RCVLOWAT | y | y | 接收缓冲区低潮限度 | int | ||
SO_SNDLOWAT | y | y | 发送缓冲区低潮限度 | int | ||
SO_RCVTIMEO | y | y | 接收超时 | timeval{} | ||
SO_SNDTIMEO | y | y | 发送超时 | timeval{} | ||
SO_REUSEADDR | y | y | 允许重用本地地址 | y | int | |
SO_REUSEPORT | y | y | 允许重用本地地址 | y | int | |
SO_TYPE | y | 取得套接口类型 | int | |||
SO_USELOOPBACK | y | y | 路由套接口取得所发送数据的拷贝 | y | int | |
IPPROTO_IP | IP_HDRINCL | y | y | IP头部包括数据 | y | int |
IP_OPTIONS | y | y | IP头部选项 | 见后面说明 | ||
IP_RECVDSTADDR | y | y | 返回目的IP地址 | y | int | |
IP_RECVIF | y | y | 返回接收到的接口索引 | y | int | |
IP_TOS | y | y | 服务类型和优先权 | int | ||
IP_TTL | y | y | 存活时间 | int | ||
IP_MULTICAST_IF | y | y | 指定外出接口 | in_addr{} | ||
IP_MULTICAST_TTL | y | y | 指定外出TTL | u_char | ||
IP_MULTICAST_LOOP | y | y | 指定是否回馈 | u_char | ||
IP_ADD_MEMBERSHIP | y | 加入多播组 | ip_mreq{} | |||
IP_DROP_MEMBERSHIP | y | 离开多播组 | ip_mreq{} | |||
IPPROTO_ICMPV6 | ICMP6_FILTER | y | y | 指定传递的ICMPv6消息类型 | icmp6_filter{} | |
IPPROTO_IPV6 | IPV6_ADDRFORM | y | y | 改变套接口的地址结构 | int | |
IPV6_CHECKSUM | y | y | 原始套接口的校验和字段偏移 | int | ||
IPV6_DSTOPTS | y | y | 接收目标选项 | y | int | |
IPV6_HOPLIMIT | y | y | 接收单播跳限 | y | int | |
IPV6_HOPOPTS | y | y | 接收步跳选项 | y | int | |
IPV6_NEXTHOP | y | y | 指定下一跳地址 | y | sockaddr{} | |
IPV6_PKTINFO | y | y | 接收分组信息 | y | int | |
IPV6_PKTOPTIONS | y | y | 指定分组选项 | 见后面说明 | ||
IPV6_RTHDR | y | y | 接收原路径 | y | int | |
IPV6_UNICAST_HOPS | y | y | 缺省单播跳限 | int | ||
IPV6_MULTICAST_IF | y | y | 指定外出接口 | in6_addr{} | ||
IPV6_MULTICAST_HOPS | y | y | 指定外出跳限 | u_int | ||
IPV6_MULTICAST_LOOP | y | y | 指定是否回馈 | y | u_int | |
IPV6_ADD_MEMBERSHIP | y | 加入多播组 | ipv6_mreq{} | |||
IPV6_DROP_MEMBERSHIP | y | 离开多播组 | ipv6_mreq{} | |||
IPPROTO_TCP | TCP_KEEPALIVE | y | y | 控测对方是否存活前连接闲置秒数 | int | |
TCP_MAXRT | y | y | TCP最大重传时间 | int | ||
TCP_MAXSEG | y | y | TCP最大分节大小 | int | ||
TCP_NODELAY | y | y | 禁止Nagle算法 | y | int | |
TCP_STDURG | y | y | 紧急指针的解释 | y | int |
我们可以利用getsockopt和setsockopt进行套接字的可选项信息的获取和设置。他们的原型如下:
#include
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
sockfd,socket描述符。
#include
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
下面是一个使用getsockopt和setsockopt的例子,例子中先获取了套接字的类型(是SOCK_STREAM还是SOCK_DGRAM),然后设置接收和发送缓冲区的大小,最后显示这些大小:
#include
#include
#include
#include
void error_handling(char* message);
int main(int argc,char* argv)
{
//声明socket
int tcp_sock,udp_sock;
//socket类型
int sock_type;
//socket缓冲区大小
int snd_buf,rev_buf;
//可选项字节数
socklen_t optlen;
//getsockopt返回的状态,0表示获取成功
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,&sock_type,&optlen);
if(state)
error_handling("getsockopt one error");
printf("socket type one: %d\n",sock_type);
state=getsockopt(udp_sock,SOL_SOCKET,SO_TYPE,&sock_type,&optlen);
if(state)
error_handling("getsockopt two error");
printf("socket type two: %d\n",sock_type);
optlen=sizeof(snd_buf);
state=getsockopt(tcp_sock,SOL_SOCKET,SO_SNDBUF,&snd_buf,&optlen);
if(state==0)
printf("socket输出缓冲区大小是: %d\n",snd_buf);
optlen=sizeof(rev_buf);
state=getsockopt(tcp_sock,SOL_SOCKET,SO_RCVBUF,&rev_buf,&optlen);
if(state==0)
printf("socket输入缓冲区大小是: %d\n",rev_buf);
printf("更改输入和输出缓冲区...\n");
snd_buf=1024*3;
rev_buf=1024*6;
optlen=sizeof(snd_buf);
state=setsockopt(tcp_sock,SOL_SOCKET,SO_SNDBUF,&snd_buf,optlen);
if(state==0)
{
state=getsockopt(tcp_sock,SOL_SOCKET,SO_SNDBUF,&snd_buf,&optlen);
printf("更改成功!\n");
printf("更改后的输出缓冲区大小为:%d\n",snd_buf);
}
optlen=sizeof(rev_buf);
state=setsockopt(tcp_sock,SOL_SOCKET,SO_RCVBUF,&rev_buf,optlen);
if(state==0)
{
state=getsockopt(tcp_sock,SOL_SOCKET,SO_RCVBUF,&rev_buf,&optlen);
printf("更改后的输入缓冲区大小为:%d\n",rev_buf);
}
return 0;
}
void error_handling(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
运行结果:
[Hyman@Hyman-PC csdn]$ ./a.out
SOCK_STREAM: 1
SOCK_DGRAM: 2
socket type one: 1
socket type two: 2
socket输出缓冲区大小是: 16384
socket输入缓冲区大小是: 87380
更改输入和输出缓冲区...
更改成功!
更改后的输出缓冲区大小为:6144
更改后的输入缓冲区大小为:12288
“为什么我设置的输入和输出缓冲区大小为1024*6和1024*3,实际设置成功的结果却是12288和6144?”
其实我们设置的值是操作系统设置socket输入和输出缓冲区的一个参考值,因为操作系统还要进行一些额外的操作:比如错误重传机制等等都需要一定的缓存,此时实际设置的值往往比我们预期的要大。这里只是为了演示,在实际的开发中socket默认的输出和输入缓冲已经足够我们使用了。。
Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone [email protected]:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL09