一直想对套接字选项的资料做个整理。该篇主要包括选项的各个列表,和最终的一个代码,打印出本机是否支持这些选项。 资料摘自http://blog.chinaunix.net/u1/38994/showart.php?id=726708 ,代码从<unp>中摘录
详细列表请点击以上连接查看(排版难排)
getsockopt和setsockopt
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
返回:0—OK,-1—出错。
sockfd必须是一个打开的套接口描述字;level(级别)指定系统中解释选项的代码:普通套接口代码或特定于协议的代码);optval是一个指向变量的指针;此变量的大小由最后一个参数决定。
对于某些套接口选项,什么时候进行设置或获取是有差别的。下面的套接口选项是由TCP已连接套接口从监听套接口继承来的:
* SO_DEBUG;
* SO_DONTROUTE;
* SO_KEEPALIVE;
* SO_LINGER;
* SO_OOBINLINE;
* SO_RCVBUF;
* SO_SNDBUF。
详细说明:
SO_BROADCAST
使能或禁止进程发送广播消息的能力。只有数据报套接口支持广播,并且还必须在支持广播消息的网络上(如以太网、令牌环网等)。
如果目的地址是广播地址但此选项未设,则返回EACCES错误。
SO_DEBUG
仅仅TCP支持。当打开此选项时,内核对TCP在此套接口所发送和接收的所有分组跟踪详细信息。这些信息保存在内核的环形缓冲区内,可由程序trpt进行检查。
SO_DONTROUTE
此选项规定发出的分组将旁路底层协议的正常路由机制。
该选项经常由路由守护进程(routed和gated)用来旁路路由表(路由表不正确的情况下),强制一个分组从某个特定接口发出。
SO_ERROR
当套接口上发生错误时,源自Berkeley的内核中的协议模块将此套接口的名为so_error的变量设为标准的UNIX Exxx值中的一个,它称为此套接口的待处理错误(pending error)。内核可立即以以下两种方式通知进程:
1. 如果进程阻塞于次套接口的select调用,则无论是检查可读条件还是可写条件,select都返回并设置其中一个或所有两个条件。
2. 如果进程使用信号驱动I/O模型,则给进程或进程组生成信号SIGIO。
进程然后可以通过获取SO_ERROR套接口选项来得到so_error的值。由getsockopt返回的整数值就是此套接口的待处理错误。so_error随后由内核复位为0。
当进程调用read且没有数据返回时,如果so_error为非0值,则read返回-1且errno设为so_error的值,接着so_error的值被复位为0。如果此套接口上有数据在排队,则read返回那些数据而不是返回错误条件。
如果进程调用write时so_error为非0值,则write返回-1且errno设为so_error的值,随后so_error也被复位。
SO_KEEPALIVE
打开此选项后,如果2小时内在此套接口上没有任何数据交换,TCP就会自动给对方发一个保持存活探测分节,结果如下:
1. 对方以期望的ACK响应,则一切正常,应用程序得不到通知;
2. 对方以RST响应,套接口的待处理错误被置为ECONNRESET,套接口本身则被关闭;
3. 对方对探测分节无任何响应,经过重试都没有任何响应,套接口的待处理错误被置为ETIMEOUT,套接口本身被关闭;若接收到一个ICMP错误作为某个探测分节的响应,则返回相应错误。
此选项一般由服务器使用。服务器使用它是为了检测出半开连接并终止他们。
SO_LINGER
此选项指定函数close对面向连接的协议如何操作(如TCP)。缺省close操作是立即返回,如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
SO_LINGER选项用来改变此缺省设置。使用如下结构:
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三种情况:
1. l_onoff为0,则该选项关闭,l_linger的值被忽略,等于缺省情况,close立即返回;
2. l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态;
3. l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完成。
l_linger的单位依赖于实现,4.4BSD假设其单位是时钟滴答(百分之一秒),但Posix.1g规定单位为秒。
让客户知道服务器已经读其数据的一个方法时:调用shutdown(SHUT_WR)而不是调用close,并等待对方close连接的本地(服务器)端。
SO_OOBINLINE
此选项打开时,带外数据将被保留在正常的输入队列中(即在线存放)。当发生这种情况时,接收函数的MSG_OOB标志不能用来读带外数据。
SO_RCVBUF和SO_SNDBUF
每个套接口都有一个发送缓冲区和一个接收缓冲区,使用这两个套接口选项可以改变缺省缓冲区大小。
当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,SO_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。
TCP套接口缓冲区的大小至少是连接的MSS的三倍,而必须是连接的MSS的偶数倍。
SO_RCVLOWAT和SO_SNDLOWAT
每个套接口有一个接收低潮限度和一个发送低潮限度,他们由函数select使用。这两个选项可以修改他们。
接收低潮限度是让select返回“可读”而在套接口接收缓冲区中必须有的数据量,对于一个TCP或UDP套接口,此值缺省为1。发送低潮限度是让select返回“可写”而在套接口发送缓冲区中必须有的可用空间,对于TCP套接口,此值常为2048。
SO_RCVTIMEO和SO_SNDTIMEO
使用这两个选项可以给套接口设置一个接收和发送超时。通过设置参数的值为0秒和0微秒来禁止超时。缺省时两个超时都是禁止的。
接收超时影响5个输入函数:read、readv、recv、recvfrom和recvmsg;发送超时影响5个输出函数:write、writev、send、sendto和sendmsg。
SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR提供如下四个功能:
1. SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
2. SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
3. SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
4. SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。
SO_REUSEPORT选项有如下语义:
1. 此选项允许完全重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才性。
2. 如果被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。
使用这两个套接口选项的建议:
1. 在所有TCP服务器中,在调用bind之前设置SO_REUSEADDR套接口选项;
2. 当编写一个同一时刻在同一主机上可运行多次的多播应用程序时,设置SO_REUSEADDR选项,并将本组的多播地址作为本地IP地址捆绑。
SO_TYPE
该选项返回套接口的类型,返回的整数值是一个诸如SOCK_STREAM或SOCK_DGRAM这样的值。
SO_USELOOPBACK
该选项仅用于路由域(AF_ROUTE)的套接口,它对这些套接口的缺省设置为打开(这是唯一一个缺省为打开而不是关闭的SO_xxx套接口选项)。当此套接口打开时,套接口接收在其上发送的任何数据的一个拷贝。
禁止这些回馈拷贝的另一个方法是shutdown,第二个参数应设为SHUT_RD。
IP_HDRINCL
如果一个原始套接口设置该选项,则我们必须为所有发送到此原始套接口上的数据报构造自己的IP头部。
IP_OPTIONS
设置此选项允许我们在IPv4头部中设置IP选项。这要求掌握IP头部中IP选项的格式信息。
IP_RECVDSTADDR
该选项导致所接收到的UDP数据报的目的IP地址由函数recvmsg作为辅助数据返回。
IP_RECVIF
该选项导致所接收到的UDP数据报的接口索引由函数recvmsg作为辅助数据返回。
IP_TOS
该选项使我们可以给TCP或UDP套接口在IP头部中设置服务类型字段。如果我们给此选项调用getsockopt,则放到外出IP数据报头部的TOS字段中的当前值将返回(缺省为0)。还没有办法从接收到的IP数据报中取此值。
可以将TOS设置为如下的值:
* IPTOS_LOWDELAY:最小化延迟
* IPTOS_THROUGHPUT:最大化吞吐量
* IPTOS_RELIABILITY:最大化可靠性
* IPTOS_LOWCOST:最小化成本
IP_TTL
用次选项,可以设置和获取系统用于某个给定套接口的缺省TTL值(存活时间字段)。与TOS一样,没有办法从接收到的数据报中得到此值。
ICMP6_FILTER
可获取和设置一个icmp6_filter结构,他指明256个可能的ICMPv6消息类型中哪一个传递给在原始套接口上的进程。
IPV6_ADDRFORM
允许套接口从IPv4转换到IPv6,反之亦可。
IPV6_CHECKSUM
指定用户数据中校验和所处位置的字节偏移。如果此值为非负,则内核将(1)给所有外出分组计算并存储校验和;(2)输入时检查所收到的分组的校验和,丢弃带有无效校验和的分组。此选项影响出ICMPv6原始套接口外的所有IPv6套接口。如果指定的值为-1(缺省值),内核在此原始套接口上将不给外出的分组计算并存储校验和,也不检查所收到的分组的校验和。
IPV6_DSTOPTS
设置此选项指明:任何接收到的IPv6目标选项都将由recvmsg作为辅助数据返回。此选项缺省为关闭。
IPV6_HOPLIMIT
设置此选项指明:接收到的跳限字段将由recvmsg作为辅助数据返回。
IPV6_HOPOPTS
设置此选项指明:任何接收到的步跳选项都将由recvmsg作为辅助数据返回。
IPV6_NEXTHOP
这不是一个套接口选项,而是一个可指定个sendmsg的辅助数据对象的类型。此对象以一个套接口地址结构指定某个数据报的下一跳地址。
IPV6_PKTINFO
设置此选项指明:下面关于接收到的IPv6数据报的两条信息将由recvmsg作为辅助数据返回:目的IPv6地址和到达接口索引。
IPV6_PKTOPTIONS
大多数IPv6套接口选项假设UDP套接口使用recvmsg和sendmsg所用的辅助数据在内核与应用进程间传递信息。TCP套接口使用IPV6_PKTOPTIONS来获取和存储这些值。
IPV6_RTHDR
设置此选项指明:接收到的IPv6路由头部将由recvmsg作为辅助数据返回。
IPV6_UNICAST_HOPS
类似于IPv4的IP_TTL,它的设置指定发送到套接口上的外出数据报的缺省跳限,而它的获取则返回内核将用于套接口的跳限值。为了从接收到的IPv6数据报中得到真实的跳限字段,要求使用IPV6_HOPLIMIT套接口选项。
TCP_KEEPALIVE
它指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间。缺省值至少为7200秒,即2小时。该选项仅在SO_KEEPALIVE套接口选项打开时才有效。
TCP_MAXRT
它指定一旦TCP开始重传数据,在连接断开之前需经历的以秒为单位的时间总量。值0意味着使用系统缺省值,值-1意味着永远重传数据。
TCP_MAXSEG
允许获取或设置TCP连接的最大分节大小(MSS)。返回值是我们的TCP发送给另一端的最大数据量,他常常就是由另一端用SYN分节通告的MSS,除非我们的TCP选择使用一个比对方通告的MSS小的值。如果此选项在套接口连接之前取得,则返回值为未从另一端收到的MSS选项的情况下所用的缺省值。
TCP_NODELAY
如果设置,此选项禁止TCP的Nagle算法。缺省时,该算法是使能的。
Nagle算法的目的是减少WAN上小分组的数目。
Nagle算法常常与另一个TCP算法联合使用:延迟ACK(delayed ACK)算法。
解决多次写导致Nagle算法和延迟ACK算法负面影响的方法:
1. 使用writev而不是多次write;
2. 合并缓冲区,对此缓冲区使用一次write;
3. 设置TCP_NODELAY选项,继续调用write多次,这是最不可取的解决方法。
TCP_STDURG
它影响对TCP紧急指针的解释。
/*
代码如下:
@环境 gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
@CPU intel
*/
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
union val {
int i_val;
long l_val;
char c_val[10];
struct linger linger_val;
struct timeval timeval_val;
} val;
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);
struct sock_opts {
char *opt_str;
int opt_level;
int opt_name;
char *(*opt_val_str)(union val *, int);
} sock_opts[] = {
"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, sock_str_flag,
"SO_DEBUG", SOL_SOCKET, SO_DEBUG, sock_str_flag,
"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, sock_str_flag,
"SO_ERROR", SOL_SOCKET, SO_ERROR, sock_str_int,
"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, sock_str_flag,
"SO_LINGER", SOL_SOCKET, SO_LINGER, sock_str_linger,
"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, sock_str_flag,
"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, sock_str_int,
"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, sock_str_int,
"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, sock_str_int,
"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, sock_str_int,
"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, sock_str_timeval,
"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, sock_str_timeval,
"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, sock_str_flag,
#ifdef SO_REUSEPORT
"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, sock_str_flag,
#else
"SO_REUSEPORT", 0, 0, NULL,
#endif
"SO_TYPE", SOL_SOCKET, SO_TYPE, sock_str_int,
//"SO_USELOOPBACK", SOL_SOCKET, SO_USELOOPBACK, sock_str_flag,
"IP_TOS", IPPROTO_IP, IP_TOS, sock_str_int,
"IP_TTL", IPPROTO_IP, IP_TTL, sock_str_int,
"TCP_MAXSEG", IPPROTO_TCP,TCP_MAXSEG, sock_str_int,
"TCP_NODELAY", IPPROTO_TCP,TCP_NODELAY, sock_str_flag,
NULL, 0, 0, NULL
};
int main(int argc, char **argv)
{
int fd ;
socklen_t 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) {
printf("getsockopt error\n");
exit(0);
} else {
printf("default = %s\n", (*ptr->opt_val_str)(&val, len));
}
}
}
exit(0);
}
/* end checkopts2 */
/* include checkopts3 */
static char strres[128];
static char *
sock_str_flag(union val *ptr, int len)
{
/* *INDENT-OFF* */
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);
/* *INDENT-ON* */
}
/* end checkopts3 */
static char *
sock_str_int(union val *ptr, int len)
{
if (len != sizeof(int))
snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len);
else
snprintf(strres, sizeof(strres), "%d", ptr->i_val);
return(strres);
}
static char *
sock_str_linger(union val *ptr, int len)
{
struct linger *lptr = &ptr->linger_val;
if (len != sizeof(struct linger))
snprintf(strres, sizeof(strres),
"size (%d) not sizeof(struct linger)", len);
else
snprintf(strres, sizeof(strres), "l_onoff = %d, l_linger = %d",
lptr->l_onoff, lptr->l_linger);
return(strres);
}
static char *
sock_str_timeval(union val *ptr, int len)
{
struct timeval *tvptr = &ptr->timeval_val;
if (len != sizeof(struct timeval))
snprintf(strres, sizeof(strres),
"size (%d) not sizeof(struct timeval)", len);
else
snprintf(strres, sizeof(strres), "%d sec, %d usec",
tvptr->tv_sec, tvptr->tv_usec);
return(strres);
}
/*
本机打印结果
SO_BROADCAST: default = off
SO_DEBUG: default = off
SO_DONTROUTE: default = off
SO_ERROR: default = 0
SO_KEEPALIVE: default = off
SO_LINGER: default = l_onoff = 0, l_linger = 0
SO_OOBINLINE: default = off
SO_RCVBUF: default = 87380
SO_SNDBUF: default = 16384
SO_RCVLOWAT: default = 1
SO_SNDLOWAT: default = 1
SO_RCVTIMEO: default = 0 sec, 0 usec
SO_SNDTIMEO: default = 0 sec, 0 usec
SO_REUSEADDR: default = off
SO_REUSEPORT: (undefined)
SO_TYPE: default = 1
IP_TOS: default = 0
IP_TTL: default = 64
TCP_MAXSEG: default = 536
TCP_NODELAY: default = off
*/
分享到:
相关推荐
1. **创建套接字**:使用`socket()`函数创建一个新的套接字,指定协议类型(如TCP或UDP)、地址族(通常为AF_INET)和套接字类型(如SOCK_STREAM或SOCK_DGRAM)。 2. **绑定地址**:使用`bind()`函数将套接字与本地...
标题中的“利用原始套接字实现PING功能的源代码”是指使用计算机网络编程中的原始套接字(Raw Sockets)来构建一个简单的网络诊断工具,类似于操作系统内置的PING命令。这个工具可以用来测试网络连通性,测量数据包...
Redis提供了支持UNIX域套接字的选项,可以通过配置文件启用,并指定一个`.sock`文件,例如`redis.sock`。 在Java中,我们通常使用第三方库如Lettuce来操作Redis。Lettuce是一个强大的、线程安全的Redis客户端,支持...
它包括创建套接字、设置套接字选项和绑定套接字地址等步骤。 套接字通信函数: 套接字通信函数是指套接字通信中使用的函数,例如send函数和recv函数等。这些函数提供了基本的套接字通信功能。 三次握手、四次挥手...
在使用原始套接字时,一个重要的步骤是设置IP头部包含选项(IP_HDRINCL)。通过`setsockopt()`函数,我们可以告诉内核IP数据包的头部将由我们的应用程序填充,而不是由内核生成。在示例中,这个选项通过设置`IPPROTO...
总的来说,这个C#项目提供了一个数据包嗅探和转发的工具,利用原始套接字技术,可以直接操作网络层的数据包,从而实现更灵活的网络监控和管理。这在网络安全分析、网络调试、性能测试等方面都有广泛的应用。不过,...
接下来,我们需要设置套接字选项以允许非特权用户捕获所有网络流量。这通常涉及到`setsockopt()`函数和`SO_BINDTODEVICE`选项。这个选项允许我们将套接字绑定到特定的网络接口,从而只抓取该接口的数据包。 ```c ...
基于套接字用C实现的Ping程序,是一个深入探索网络编程和协议栈处理的经典案例。在计算机网络领域,Ping命令是测试网络连通性的一种基本工具,它通过发送Internet Control Message Protocol (ICMP) Echo请求报文并...
总的来说,这个项目是一个使用Winsock原始套接字在win2k系统下实现网络数据包捕获的工具,具备线程处理、进度条显示和批量存盘功能。通过分析源代码,我们可以学习到如何利用Winsock API进行网络编程,以及如何在C++...
Linux网络编程中的Socket选项是控制套接字行为的关键机制,它们允许程序员调整网络通信的各种特性。本文将深入探讨Socket选项的实现,以及如何通过`getsockopt`和`setsockopt`函数来读取和设置这些选项。 `...
值得注意的是,实际的网络应用程序可能需要处理更多的细节,例如错误检查、多线程处理多个客户端连接、使用套接字选项、超时控制等。但这个基础的Winsock服务器和客户端示例为你提供了一个良好的起点,让你能够逐步...
- **套接字选项**:`setsockopt`和`getsockopt`函数用于设置和查询套接字的特定选项,例如超时时间、重传次数等。 - **套接字控制消息**:`WSASend`和`WSARecv`可以传递控制信息,如TCP窗口大小、流量控制等。 7....
1. 创建套接字:首先,我们需要创建一个TCP套接字,这是与远程服务器建立连接的基础。 2. 连接服务器:使用套接字的connect函数,指定服务器的IP地址和端口号,建立起客户端到服务器的连接。 3. 发送请求:向服务器...
10. **套接字选项(Socket Options)**:可以使用`setsockopt()`和`getsockopt()`函数来设置或查询套接字的特定选项,如超时设置、重传策略等。 这些是Windows Socket编程的一些核心概念。压缩包中的文件可能包含...
10. **套接字选项**:了解如何使用setsockopt()和getsockopt()设置和查询套接字选项,如SO_REUSEADDR、SO_LINGER等。 11. **套接字编程高级主题**:包括UNIX域套接字、TCP的连接管理、性能优化、安全套接字层(SSL/...
本程序定义了三个关键结构体,用于存储和处理网络通信所需的数据: 1. **IP报头结构体**:存储IP报头信息,如版本、长度、类型等。 2. **ICMP报头结构体**:包含ICMP报文的类型、代码、校验和等字段。 3. **IP路由...
5. **设置套接字选项**:使用`setsockopt`函数设置`SO_REUSEADDR`选项,以便在服务器重启时可以立即重用地址和端口,避免因系统保持一段时间的TIME_WAIT状态而导致的问题。 6. **绑定套接字到地址**:通过`bind`...
这包括创建套接字、设置套接字选项和将套接字与网络接口绑定。 2. 发送 TCP 数据包:在初始化原始套接字后,可以开始发送 TCP 数据包。发送 TCP 数据包需要将数据封装成 TCP 报文,然后将其发送到目的主机。 数据...