套接字有多种可选项,有时候是需要更改套接字可选项的,下面是一部分套接字可选项
下面是读取和设置可选项的函数
#include<sys/socket.h>
int getsockopt(int sock,int level,int optname,void *optval,socklen *optlen);
//成功时返回0,失败时返回-1
sock--用于查看选项套接字文件描述符level--要查看的可选项的协议层
optname--要查看的可选项名
optvl--保存查看结果的缓冲地址值
optlen--向第四个参数optval传递的缓冲大小。
上述函数用于读取套接字可选项,接下来介绍更改可选项时调用的函数。
#include<sys/socket.h>
int setsockopt(int sock,int level,int optname,const void *optval,socklen *optlen);
//成功时返回0,失败时返回-1
sock--用于更改选项套接字文件描述符level--要更改的可选项的协议层
optname--要更改的可选项名
optvl--保存更改结果的缓冲地址值
optlen--向第四个参数optval传递的缓冲大小。
下面是getsockopt函数的调用方法
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
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); //输出SOCK_STREAM的值
printf("SOCK_DGRAM:%%d \n",SOCK_DGRAM); //输出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); //输出获得的SOCK_STREAM常数值为1
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); //输出获得的SOCK_DGRAM常数值为2
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
一、I/O缓冲相关选项
SO_RCVBUF是输入缓冲大小相关可选项,SO_SNDBUF是输出缓冲大小相关选项。既可以读取当前缓冲I/O缓冲大小,也可以进行更改。下列示例读取创建套接字时默认的I/O缓冲大小。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
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_RCVBUF,(void*)&rcv_buf,&len);
if(state)
error_handling("getsocket() 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缓冲大小:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
void error_handling(char *message);
int main(int argc,char *argv[])
{
int sock;
int snd_buf=1024*3,rcv_buf=1024*3,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("setsockopt() error");
state=setsockopt(sock,SOL_SOCKET,SO_SNDBUF,(void*)&snd_buf,sizeof(snd_buf));
if(state)
error_handling("setsocket() error");
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_RCVBUF,(void*)&rcv_buf,&len);
if(state)
error_handling("getsocket() 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);
}
二、SO_REUSEADDR选项和Time-wait状态
Time-wait状态
套接字经过四次握手过程后并非立即消除,而是经过一段时间的Time-wait状态。只有先断开连接的(先发送FIN消息的)主机才经过Time-wait状态。因此,若服务器端先断开连接,则无法立即重新运行。套接字处在Time-wait过程时,相应端口是正在使用的状态。因此此时bind()函数的调用过程中会发生错误。
Time-wait状态发生的原因:假设有两个主机A和B。假设主机A向B传输ACK消息后立即消除套接字。但最后这条ACK消息在传递途中丢失,未能传给主机B。这时主机B会认为之前自己发送的FIN消息未能抵达主机A,继而试图重传。但此时主机A已是完全终止的状态,因此主机B永远无法收到从A最后传来的ACK消息。相反,若主机A的套接字处在Time-wait状态,则会向主机B重传最后的ACK消息,主机B也可以正常终止。基于以上,先传输FIN消息的主机应经过Time-wait过程。
地址再分配
Time-wait状态似乎很重要,但是如果发生系统故障而紧急停止的情况,这时需要尽快重启服务器端以提供服务,但因处于Time-wait状态而必须等待几分钟。
解决方案就是在套接字的可选项中更改SO_REUSEADDR的状态。适当调整该参数,可将Time-wait状态下的套接字端口号重新分配给新的套接字。SO_REUSEADDR的默认值为0,意味着无法分配Time-wait状态下的套接字端口号。因此将这个值改为1,以下代码
optlen=sizeof(option);
option=TRUE;
setsockopt(serv_sock,SOL_SOCKET,SO_REUSEADDR,(void*)&option,optlen);
三、TCP_NODELAY选项和Nagle算法
关于Nagle算法
该算法为防止数据包过多而发生网络过载而诞生,应用于TCP层。
只有收到前一数据的ACK消息时,Nagle算法才发送下一数据。TCP套接字默认使用Nagle算法交换数据,因此最大限度地进行缓冲,直到收到ACK。
一般情况下,不使用Nagle算法可以提高传输速度,但如果无条件放弃使用Nagle算法,就会增加过多的网络流量,反而影响传输。因此,未准确判断数据特性时不应禁用Nagle算法。
禁用Nagle算法
禁用Nagle算法只需将套接字可选项TCP_NODELAY改为1,下面是代码实现
int opt_val=1;
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(void*)&opt_val,sizeof(opt_val));
可以通过TCP_NODELAY的值查看Nagle算法的设置状态。
int opt_val;
socklen_t opt_len;
opt_len=sizeof(opt_val);
getsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(void*)&opt_val,&opt_len);
如果正在使用Nagle算法,opt_val变量中会保存0;如果已禁用Nagle算法,则保存1。