socket可选项编程

套接字有多种可选项,有时候是需要更改套接字可选项的,下面是一部分套接字可选项


        套接字可选项是分层的。IPPROTO_IP层可选项是IP协议相关事项,IPPROTO_TCP层可选项是TCP协议相关事项,SOL_SOCKET层是套接字相关的通用可选项。
下面是读取和设置可选项的函数

#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。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值