获取和设置影响套接口选项的函数:
getsockopt :获取套接口选项
setsockopt: 获取套接口选项
fcntl: 设置套接口为非阻塞I/O型信号驱动I/O型等
oictl
套接口选项
SO-KEEPALIVE
SO-LINGER
SE-RCVBUF 和 SO-SNDBUF
SO-RCVWAT和 SO-SNDLOWAT
SO-RCVTIMEO和 SO-SNDTIMEO
SO-REUSEADDR和 SO-REUSPORT
IP-TTL
TCP-KEEPALIVE
getsockopt 和 setsockopt
获得套接口选项:
int getsockopt ( int sockfd, int level, int optname,
void * optval, socklen_t *opteln )
设置套接口选项:
int setsockopt ( int sockfd, int level, int optname,
const void * optval, socklen_t *opteln )
sockfd(套接字): 指向一个打开的套接口描述字
level:(级别): 指定选项代码的类型。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname(选项名): 选项名称
optval(选项值): 是一个指向变量的指针
类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
optlen(选项长度) :optval 的大小
返回值:标志打开或关闭某个特征的二进制选项
检查套接口选项的程序
输出套接口的选项:
定义感兴趣的套接口选项
调用getsockopt
输出套接口选项
定义联合:不同的套接口选项有不同类型
union val { //套接口选项可能有的5个类型分别作为一个成员:
int i_val;
long l_val;
char c_val[10];
struct linger linger_val;
struct timeval timeval_val; //struct {int S; int uS}
} val;
//函数原型(prototype),这些函数用于输出套接口选项的值
static char *sock_str_flag(union val *, int); //静态函数,只可在本文件中被调用
static char *sock_str_int(union val *, int);
static char *sock_str_linger(union val *, int);
static char *sock_str_timeval(union val *, int);
//定义结构sock_opts, 其中包含了获得或输出套接口选项的所有信息
struct sock_opts {
char *opt_str; //字符名称
int opt_level; //级别
int opt_name; //名称
char *(*opt_val_str)(union val *, int); //函数指针,用于输出,
}
//定义结构数组并初始化
struct sock_opts sock_opts[ ] = { //全局变量数组才可以初始化
"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, sock_str_flag,
"SO_DEBUG", SOL_SOCKET, SO_DEBUG, sock_str_flag,
#ifdef SO_REUSEPORT //编译时用的宏定义
"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, sock_str_flag,
#else //没有这个选项
"SO_REUSEPORT", 0, 0, NULL, //NULL表示没有定义
#endif
"SO_TYPE", SOL_SOCKET, SO_TYPE, sock_str_int,
"IP_TTL", IPPROTO_IP, IP_TTL, sock_str_int,
"TCP_MAXSEG", IPPROTO_TCP,TCP_MAXSEG, sock_str_int,
NULL, { /*结束标志 */} 0, 0, NULL
};
源程序:
int main(int argc, char **argv)
{
int fd, len;
struct sock_opts *ptr; //结构类型
fd = Socket(AF_INET, SOCK_STREAM, 0); //获得套接字
for (ptr = sock_opts; ptr->opt_str != NULL; ptr++) { //从第一个选项到最后一个
printf(“%s: ”, ptr->opt_str); //输出字符名
if (ptr->opt_val_str == NULL) //没有定义的情况
printf("(undefined)\n");
else {
len = sizeof(val);
//获得套接口选项
if (getsockopt(fd, ptr->opt_level, ptr->opt_name, val, len) == -1) {
//返回值为1,函数调用失败
err_ret("getsockopt error");}
//输出选项的缺省值
else printf("default = %s\n", (*ptr->opt_val_str)(&val, len));
}
} //End of for loop
exit(0);
}
static char strres[128]; //静态变量,在函数调用后保留原值
static char * sock_str_flag(union val *ptr, int len)
{
if (len != sizeof(int)) //长度不相符
snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len);
else
//转换字符串
snprintf(strres, sizeof(strres),"%s", (ptr->i_val == 0) ? "off" : "on");
return(strres);
}
基本套接口选项
SO_KEEPALIVE
检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。
设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方
发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导
致以下三种情况:
对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。
对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接
口本身则被关闭。
对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到
一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错
误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不
可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为
EHOSTUNREACH。
SO_RCVBUF和SO_SNDBUF
每个套接口都有一个发送缓冲区和一个接收缓冲区。
接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由应用进程来读。
TCP:TCP通告另一端的窗口大小。
TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。
这就是TCP的流量控制,如果对方无视窗口大小而发出了超过宙口大小的数据,则接
收方TCP将丢弃它。
UDP:当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP是没有
流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。
SO_LINGER
指定函数CLOSE对面相连接的协议如何操作——当由数据残留在套接口发送缓冲区时的处理
LINGER结构
struct linger {
int l_onoff; // 0=off, nonzero=on
int l_linger; //linger time in seconds
};
SO_RCVLOWAT 和SO_SNDLOWAT
每个套接口都有一个接收低潮限度和一个发送低潮限度。它们是函数selectt使用的,
接收低潮限度是让select返回“可读”而在套接口接收缓冲区中必须有的数据总量。
——对于一个TCP或UDP套接口,此值缺省为1。发送低潮限度是让select返回“可写”
而在套接口发送缓冲区中必须有的可用空间。对于TCP套接口,此值常缺省为2048。
对于UDP使用低潮限度, 由于其发送缓冲区中可用空间的字节数是从不变化的,只要
UDP套接口发送缓冲区大小大于套接口的低潮限度,这样的UDP套接口就总是可写的。
UDP没有发送缓冲区,只有发送缓冲区的大小。
TCP 套接口选项
TCP_KEEPALIVE
指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间。缺省值至少必须
为7200秒,即2小时。此选项仅在SO_KEPALIVEE套接口选项打开时才有效。
TCP_MAXSEG
获取或设置TCP连接的最大分节大小(MSS)。返回值是我们的TCP发送给另一端的最大
数据量,它常常就是由另一端用SYN分节通告的MSS,除非我们的TCP选择使用一个比
对方通告的MSS小些的值。如果此值在套接口连接之前取得,则返回值为未从另·—端
收到Mss选项的情况下所用的缺省值。小于此返回值的信可能真正用在连接上,因为譬
如说使用时间戳选项的话,它在每个分节上占用12字节的TCP选项容量。我们的TcP将
发送的每个分节的最大数据量也可在连接存活期内改变,但前提是TCP要支持路径MTU
发现功能。如果到对方的路径改变了,此值可上下调整。
例程序:
//获得发送缓冲区大小和MSS大小,设置发送缓冲区大小
//获得发送缓冲区大小和MSS大小
#include "unp.h"
#include /* for TCP_MAXSEG */
int main(int argc, char **argv)
{ int sockfd, rcvbuf, mss;
socklen_t len;
struct sockaddr_in servaddr;
if (argc != 2) err_quit("usage: rcvbuf ");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
len = sizeof(rcvbuf);
Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, rcvbuf, len);
len = sizeof(mss);
Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);
printf("defaults: SO_RCVBUF = %d, MSS = %d\n", rcvbuf, mss);
bzero(servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); /* daytime server */
Inet_pton(AF_INET, argv[1], servaddr.sin_addr);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
len = sizeof(rcvbuf);
Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);
len = sizeof(mss);
Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);
printf("after connect: SO_RCVBUF = %d, MSS = %d\n", rcvbuf, mss);
exit(0);
}
//设置发送缓冲区大小
#include "unp.h"
#include /* for TCP_MAXSEG value */
int
main(int argc, char **argv)
{
int sockfd, mss, sendbuff;
socklen_t optlen;
float kk;
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
/* Fetch and print the TCP maximum segment size. */
optlen = sizeof(mss);
sendbuff =2048;
Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, sizeof(sendbuff));
optlen = sizeof(sendbuff);
Getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, &optlen);
printf("After send buffer size = %d\n", sendbuff);
exit(0);
}
(http://www.fanqiang.com)
进入【