套接字的多种可选项(Linux + GCC)

套接字的多种可选项

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

levelOptnamegetset说明标志数据类型







SOL_SOCKETSO_BROADCASTyy允许发送广播数据报yint

SO_DEBUGyy使能调试跟踪yint

SO_DONTROUTEyy旁路路由表查询yint

SO_ERRORy
获取待处理错误并消除
int

SO_KEEPALIVEyy周期性测试连接是否存活yint

SO_LINGERyy若有数据待发送则延迟关闭
linger{}

SO_OOBINLINEyy让接收到的带外数据继续在线存放yint

SO_RCVBUFyy接收缓冲区大小
int

SO_SNDBUFyy发送缓冲区大小
int

SO_RCVLOWATyy接收缓冲区低潮限度
int

SO_SNDLOWATyy发送缓冲区低潮限度
int

SO_RCVTIMEOyy接收超时
timeval{}

SO_SNDTIMEOyy发送超时
timeval{}

SO_REUSEADDRyy允许重用本地地址yint

SO_REUSEPORTyy允许重用本地地址yint

SO_TYPEy
取得套接口类型
int

SO_USELOOPBACKyy路由套接口取得所发送数据的拷贝yint







IPPROTO_IPIP_HDRINCLyyIP头部包括数据yint

IP_OPTIONSyyIP头部选项
见后面说明

IP_RECVDSTADDRyy返回目的IP地址yint

IP_RECVIFyy返回接收到的接口索引yint

IP_TOSyy服务类型和优先权
int

IP_TTLyy存活时间
int

IP_MULTICAST_IFyy指定外出接口
in_addr{}

IP_MULTICAST_TTLyy指定外出TTL
u_char

IP_MULTICAST_LOOPyy指定是否回馈
u_char

IP_ADD_MEMBERSHIP
y加入多播组
ip_mreq{}

IP_DROP_MEMBERSHIP
y离开多播组
ip_mreq{}







IPPROTO_ICMPV6ICMP6_FILTERyy指定传递的ICMPv6消息类型
icmp6_filter{}







IPPROTO_IPV6IPV6_ADDRFORMyy改变套接口的地址结构
int

IPV6_CHECKSUMyy原始套接口的校验和字段偏移
int

IPV6_DSTOPTSyy接收目标选项yint

IPV6_HOPLIMITyy接收单播跳限yint

IPV6_HOPOPTSyy接收步跳选项yint

IPV6_NEXTHOPyy指定下一跳地址ysockaddr{}

IPV6_PKTINFOyy接收分组信息yint

IPV6_PKTOPTIONSyy指定分组选项
见后面说明

IPV6_RTHDRyy接收原路径yint

IPV6_UNICAST_HOPSyy缺省单播跳限
int

IPV6_MULTICAST_IFyy指定外出接口
in6_addr{}

IPV6_MULTICAST_HOPSyy指定外出跳限
u_int

IPV6_MULTICAST_LOOPyy指定是否回馈yu_int

IPV6_ADD_MEMBERSHIP
y加入多播组
ipv6_mreq{}

IPV6_DROP_MEMBERSHIP
y离开多播组
ipv6_mreq{}







IPPROTO_TCPTCP_KEEPALIVEyy控测对方是否存活前连接闲置秒数
int

TCP_MAXRTyyTCP最大重传时间
int

TCP_MAXSEGyyTCP最大分节大小
int

TCP_NODELAYyy禁止Nagle算法yint

TCP_STDURGyy紧急指针的解释yint

            

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


getsockopt & setsockopt

        我们几乎可以针对上表中的所有可选项进行读取(Get)和设置(Set)(当然,有些可选项只能进行一种操作)。可选项的读取和设置通过如下2个函数完成。


#include <sys/socket.h>

int getsockopt(int sock,int level,int optname,void* optval,socklen_t *optlen);


-------sock              用于查看选项套接字文件描述符。

-------level             要查看的可选项的协议层。

-------optname        要查看的可选项名。

-------optval           保存查看结果的缓冲地址值。

-------optlen           向第四个参数optval传递的缓冲大小。调用函数后,该变量中保存通过第四个参数返回的

                                         选项信息的字节数目。



   上述函数用于读取套接字可选项,并不难。接下来介绍更改可选项时调用的函数

#include <sys/socket.h>

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


-------sock              用于更改可选项的套接字文件描述符。

-------level             要更改的可选项协议层。

-------optname        要更改的可选项名。

-------optval           保存要更改的选项信息的缓冲地址值。

-------optlen           向第四个参数optval传递的可选项信息的字节数。


        接下来介绍这些函数的调用方法。关于setsockopt函数的调用方法在其他示例中给出,先介绍getsockopt函数的调用方法。下列示例用协议层为SOL_SOCKET,名为SO_TYPE的可选项查看套接字类型(TCP或UDP)。


sock_type.c

#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);
	printf("SOCK_DGRAM: %d\n",SOCK_DGRAM);

	state = getsockopt(tcp_sock,SOL_SOCKET,SO_TYPE,(void*)&sock_type,&optlen);
	if(state)
		error_handling("getsocket() 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);
}

运行结果:sock_type.c

ycz@debian8470p:~/program/TCP&&IP/chapter9$ gcc sock_type.c -o socktype

ycz@debian8470p:~/program/TCP&&IP/chapter9$ ./socktype
SOCK_STREAM: 1
SOCK_DGRAM: 2
Socket type one: 1
Socket type two: 2


       上述示例给出了调用getsockopt函数查看套接字信息的方法。另外,用于验证套接字类型的SO_TYPE是典型的只读可选项,这一点可以通过下面这句话解释:

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


SO_SNDBUF & SO_RCVBUF

           SO_RCVBUF是输入缓冲大小相关可选项,SO_SNDBUF是输出缓冲大小相关可选项。用这2个可选项既可以读取当前I/O缓冲大小,也可以进行更改。通过下列示例读取创建套接字时默认的I/O缓冲大小。


get_buf.c

#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;
	int 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("setsockopt() 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("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);
}

运行结果:get_buf.c

ycz@debian8470p:~/program/TCP&&IP/chapter9$ gcc get_buf.c -o getbuf
ycz@debian8470p:~/program/TCP&&IP/chapter9$ ./getbuf
Input buffer size: 87380
Output buffer size: 16384


      这是我系统中的运行结果,与各位的运行结果相比可能有较大差异。接下来的程序中将更改I/O缓冲大小。

set_buf.c

#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;
	int 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("setsockopt() 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("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);
}

运行结果:set_buf.c

ycz@debian8470p:~/program/TCP&&IP/chapter9$ gcc set_buf.c -o setbuf
ycz@debian8470p:~/program/TCP&&IP/chapter9$ ./setbuf
Input buffer size: 6144
Output buffer size: 6144

    输出结果跟我们预想的完全不同,但也算合理。缓冲大小的设置需谨慎处理,因此不会完全按照我们的要求进行,只是通过调用setsockopt 函数向系统传递我们的请求。如果把输出缓冲设置为0并如实反映这种设置,TCP协议将如何进行?如果要实现流控制和错误发生时的重传机制,至少要有一些缓冲空间吧?上述示例虽没有100%按照我们的请求设置缓冲大小,但也大致反映出了通过setsockopt函数设置的缓冲大小。


SO_REUSEADDR

发生地址分配错误(Bindling Error)

        学习SO_REUSEADDR可选项之前,应理解好Time-wait状态。


reuseadr_eserver.c

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

#define TRUE 1
#define FALSE 0
void error_handling(char *message);
int main(int argc,char *argv[]){
	int serv_sock,clnt_sock;
	char message[30];
	int option,str_len;
	socklen_t optlen,clnt_adr_sz;
	struct sockaddr_in serv_adr,clnt_adr;
	if(argc!=2){
		printf("Usage : %s<port>\n",argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET,SOCK_STREAM,0);
	if(serv_sock==-1)
		error_handling("socket() error!");

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

	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)))
		error_handling("bind() error");
	if(listen(serv_sock,5)==1)
		error_handling("listen error");
	clnt_adr_sz = sizeof(clnt_adr);
	clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);

	while((str_len = read(clnt_sock,message,sizeof(message)))!=0){
		write(clnt_sock,message,str_len);
		write(1,message,str_len);
	}

	close(clnt_sock);
	close(serv_sock);
	return 0;
}

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


           此示例是之前已经实现过多次的回声服务器端,可以结合第4章介绍过的回声客户端运行。



运行结果:reuseadr_eserver.c

ycz@debian8470p:~/program/TCP&&IP/chapter9$gcc reuseadr_eserver.c -o reuseadr_eserver

ycz@debian8470p:~/program/TCP&&IP/chapter9$ ./reuseadr_eserver 9190
wait
what the message
success
copy that
CET-6


运行结果:eclient.c

ycz@debian8470p:~/program/TCP&&IP/chapter4$ gcc eclient.c -o eclient

ycz@debian8470p:~/program/TCP&&IP/chapter4$ ./eclient 127.0.0.1 9190
Connected............
Inputs message(Q to quit): wait
Message from server: wait
Inputs message(Q to quit): what the message
Message from server: what the message
Inputs message(Q to quit): success
Message from server: success
Inputs message(Q to quit): copy that
Message from server: copy that
Inputs message(Q to quit): CET-6
Message from server: CET-6
Inputs message(Q to quit): Q


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值