鱼骨建站公司,网络营销公司排行,试剂网站建设,网站下方一般放什么原因文章目录读取和设置 socket 选项SO_REUSEADDRSO_RCVBUF 和 SO_SNDBUFSO_RCVLOWAT 和 SO_SNDLOWATSO_LINGER 选项网络信息APIgethostbyname 和 gethostbyaddrgetservbyname 和 getservbyportgetaddrinfogetnameinfo读取和设置 socket 选项
正如 fcntl 系统调用是控制文件描述符…
文章目录读取和设置 socket 选项SO_REUSEADDRSO_RCVBUF 和 SO_SNDBUFSO_RCVLOWAT 和 SO_SNDLOWATSO_LINGER 选项网络信息APIgethostbyname 和 gethostbyaddrgetservbyname 和 getservbyportgetaddrinfogetnameinfo读取和设置 socket 选项
正如 fcntl 系统调用是控制文件描述符属性的通用 POSIX 方法socket文件描述符的属性也有两个系统调用专门 读取 和 设置
#includesys/socket.h
int getsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
int setsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
// sockfd 指定被操作的socket。
// level 指定操作哪个协议的选项属性如IPv4、IPv6、TCP等。
// option_name 指定选项的名字。
// option_value 和 option_len 参数分别指定操作选项值和长度。
// 成功返回0失败返回-1并置errno。socket 选项leveloption数据类型说明必须在 listen/connect 调用之前设置SOL_SOCKETSO_DEBUGint打开调试信息YES通用 socket 选项与协议无关SO_REUSEADDRint重用本地地址。如可以令服务器处于 TIME_WAIT 的端口立刻被使用。SO_TYPEint获取 socket 类型SO_ERRORint获取并清除 socket 错误状态SO_DONTROUTEint不查看路由表直接将数据发送给本地局域网内的主机。类同 send 系统调用的 MSG_DONTROUTE。YESSO_RCVBUFintTCP接收缓冲区大小最小值是256字节YESSO_SNDBUFintTCP发送缓冲区大小最小值是2048字节YESSO_RCVLOWATintTCP接收缓冲区低水位标记YESSO_SNDLOWATintTCP发送缓冲区低水位标记YESSO_RCVLOWATint接收数据超时SO_SNDLOWATint发送数据超时SO_KEEPALIVEint发送周期性保活报文以维持连接YESSO_OBBINLINEint接收到的带外数据将 在线存留 在普通数据的输入队列中此时我们不能使用带 MSG_OOB 的读操作来读取带外数据而应该像读取普通数据那样读取带外数据。YESSO_LINGERintger若有数据待发送则延迟关闭。YESIPPROTO_IPIP_TOSint服务类型IPv4选项IP_TTLint存活时间IPPROTO_IPV6IPV6_NEXTHOPsockaddr_in6下一跳IP地址IPv6选项IPV6_RECVPKTINFOint接受分组信息IPV6_DONTFRAGint禁止分片IPV6_RECVTCLASSint接受通信类型IPPROTO_TCPTCP_MAXSEGintTCP最大报文段大小YESTCP选项TCP_NODELAYint禁止Nagle算法YES
对 服务器 而言部分socket选项 只能在调用 listen 系统调用前 针对 socket 设置才有效。这是因为连接 socket 只能由 accept 调用返回而 accept 从 listen 监听队列 中接受的连接至少是个 半连接 这说明 服务器 已经往 客户端 上发送出了 TCP同步报文段执行完了三次握手中的前两次。但 部分 socket 选项只能在 TCP同步报文中设置 如TCP最大报文段选项。对此有两种解决方案
对于服务器而言执行 listen系统调用 时设置这些 socket选项那么 accept 返回的 连接socket 将自动继承这些选项注1。对于客户端而言这些 socket选项 应该在调用 connect函数 之前设置因为 connect 调用成功之后三次握手已完成。 注1 这些选项包括SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OBBINLINE、SO_RCVBUF、SO_RCVLOWAT、TCP_MAXSEG、TCP_NODELAY 。 SO_REUSEADDR
服务器程序可以通过设置本选项来强制使用被处于 TIME_WAIT 状态的连接占用的 socket 地址。此外我们也可以通过修改内核参数 /proc/sys/net/ipv4/tcp_tw_recycle 来快速回收被关闭的 socket甚至使 TCP 连接根本就不进入 TIME_WAIT 状态。
int sock socket( PF_INET, SOCK_STREAM, 0); // TCP协议IPv4版本基于流服务0使用默认协议
assert( sock 0); // 检测创建sock是否成功
int reuse 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reuse, sizeof(reuse) );
// 操作描述符为sock的socket套接字操作的属性是通用socket选项
// 选项类型为SO_REUSEADDRSO_REUSEADDR的数据类型为int
// 1即为启用SO_REUSEADDR选项SO_RCVBUF 和 SO_SNDBUF
这两个选项分别用来表示 TCP 接收缓冲区大小和发送缓冲区大小。不过当我们 通过setsockopt 来设置 TCP 的接收、发送缓冲区大小时系统都会将其值加倍并且不小于某个值。
一般来讲TCP 接收缓冲区的最小值是 256 字节发送缓冲区的最小值是 2048 字节不同操作系统可能有差异。我们可以直接修改内核参数 /proc/sys/net/ipv4/tcp_rmem 和 /proc/sys/net/ipv4/tcp_wmem 来强制缓冲区没有最小值限制。
/* 先设置 TCP 接收缓冲区的大小然后立即读取 */
setsockopt( sock, SOL_SOCKET, SO_RCVBUF, recvbuf, sizeof(recvbuf) );
getsockopt( sock, SOL_SOCKET, SO_RCVBUF, recvbuf, ( socklen_t* )len );
// sock 为目标套接字的文件描述符
// recvbuf 为设定的缓冲区大小
// lensizeof( recvbuf );SO_RCVLOWAT 和 SO_SNDLOWAT
该两个选项分别表示接收、发送缓冲区的低水位标记。一般被 I/O复用系统调用 用来判断 socket 是否可读或可写
当 接收缓冲区中 可读数据的总数大于其低水位标记时I/O复用系统调用 将通知应用程序可以从对应的 socket 上读取数据当 发送缓冲区 中的空闲空间大于其低水位标记时I/O复用系统调用 将通知应用程序可以往对应的 socket 上写入数据。
默认情况下两者均为 1字节 。 SO_LINGER 选项
该选项用于控制 close系统调用 在关闭 TCP 连接时的行为。默认情况下当我们使用 close系统调用 来关闭一个 socket 时close 将立即返回TCP 模块负责把该 socket 对应的 TCP 发送缓冲区中残留的数据发送给对方。
设置获取SO_LINGER 选项的值时我们需要给 setsockoptgetsockopt系统调用传递一个 linger 类型的结构体
#include sys/socket.hstruct linger{int l_onoff; // 开启非0/关闭0该选项int l_linger; // 滞留时间
};根据 linger 结构体中两个成员变量的不同值close 系统调用可能产生如下行为
l_onoff 等于 0。 此时 SO_LINGER 选项不起作用close 用默认行为来关闭 socket。l_onoff 不为 0l_linger 等于 0。 此时 close系统调用 立即返回TCP模块 将丢弃被关闭的 socket 对应的 TCP发送缓冲区 中残留的数据同时给对方发送一个 复位报文段。这为服务器提供了异常终止一个连接的方法。l_onoff 不为 0l_linger 大于 0。 此时 close 的行为取决于两个条件 被关闭的 socket 对应的 发送缓冲区 中是否还有残留的数据该 socket 是阻塞的还是非阻塞的阻塞 close 将等待一段长为 l_linger 的时间直到 TCP模板 发送完所有残留数据并得到对方的确认。如果没发送完并得到确认则 close 返回 -1 并设置 errno 为 EWOULDBLOCK。非阻塞 close 将立即返回此时需要根据其 返回值 和 errno 来判断残留数据是否发送完毕。 网络信息API
gethostbyname 和 gethostbyaddr
gethostbyname 根据主机名称获取主机的完整信息。通常先在本地的 /etc/hosts 配置文件中查找主机没有找到再去访问 DNS 服务器。gethostbyaddr 根据IP地址获取主机的完整信息。
#includenetdb.h
struct hostent* gethostbyname( const char* name );
struct hostent* gethostbyaddr( const void* addr, size_t len, int type );
// len IIP地址长度
// type 指定addr所指IP地址的类型可以是AF_INET或AFINET6两者的返回类型都是 hostent 结构体类型的指针
#includenetdb.h
struct hostent{char* h_name; // 主机名char** h_aliases; // 主机别名列表可有多个int h_addrtype; // 地址类型地址族int h_length; // 地址长度char** h_addr_list; // 按网络字节序列出的主机IP地址列表
};getservbyname 和 getservbyport
根据名称/端口号获得某个服务的完整信息。实际上都是通过读取 /etc/services 文件来获取服务的信息的。
#includenetdb.h
struct servent* getservbyname( const char* name, const char* proto );
struct servent* getservbyport( int port, const char* proto );
// name 目标服务的名字
// port 目标服务对应的端口号
// proto 服务类型tcp表流服务、udp表数据报服务、NULL表获取所有类型的服务两者的返回类型都是 servent 结构体类型的指针
#includenetdb.h
struct servent{char* s_name; // 服务名称char** s_aliases; // 服务别名列表可有多个int s_port; // 端口号char* s_proto; // 服务类型通常是 tcp 或 udp
}; 不可重入 gethostbyname、gethostbyaddr、getservbyname 和 getservbyport 都是不可重入的即非线程安全的。但是 netdb.h 头文件给出了它们的可重入版本在原函数名尾部加上 _rre-entrant。 getaddrinfo
既能通过主机名获取 IP 地址内部使用的是 gethostbyname也能通过服务名获取端口号内部使用的是 getservbyname是否可重入取决于内部调用的函数 gethostbyname、 getservbyname 是否是它们的可重入版本。
#includenetdb.h
int getaddrinfo( const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result );
// hostname可以接收主机名服务名/字符串表示的IP地址【IPv4使用点分十进制字符串、IPv6使用十六进制字符串】
// service可以接收服务名/字符串表示的十进制端口号
// hints可以为NULL表示允许反馈任何可用的结果。
// result指向一个用于存储反馈结果的链表getaddrinfo 将隐式分配堆内存因此调用结束后必须释放这块内存
#include netdb.h
void freeaddrinfo( struct addrinfo* res );getaddrinfo 反馈的每一条结果都是 addinfo 结构体类型的对象
struct addrinfo
{int ai_flags; // 标志int ai_family; // 地址族int ai_socktype; // 服务类型,SOCK_STREAM或SOCK_DGRAMint ai_protocol; // 具体的网络协议等同于socket系统调用的第三个参数常被设为0以表自动匹配对应协议。socklen_t ai_addrlen; // socket地址ai_addr的长度char* ai_canonname; // 主机的别名struct sockaddr* ai_addr; // 指向socket地址struct addrinfo* ai_next; // 指向下一个 sockinfo 结构的对象
};ai_flags成员可以取下表中的标志的按位或
选项含义AI_PASSIVE套接字地址将用于调用 bind 函数服务器通常需要设置以表接受任何本地 socket 地址上的服务请求。客户端不能设置。AI_CANONNAME返回主机的别名AI_NUMERICHOSThostname 参数必须是IP地址字符串避免了DNS查询。AI_NUMERICSERV强制 service 参数必须是十进制端口号字符串不能是服务名。AI_V4MAPPED如果对 IPv6 地址的 getaddrinfo 请求失败则将 IPv4 映射为 IPv6 地址格式。AI_ALL必须和 AI_V4MAPPED 同时使用否则将被忽略。同时返回 符合条件 和 由IPv4转换而来 的 IPv6地址。AI_ADDRCONFIG只有至少配置了一个IPv4/IPv6地址除了回路地址后getaddrinfo 才会解析。和 AI_V4MAPPED 互斥。
当我们使用 hints 参数时可以设置 addrinfo 中前四个成员其他成员必须设置为 NULL。 getnameinfo
内部使用 gethostbyaddr 和 getservbyport是否可重入取决于内部调用的函数版本是否可重入
#includenetdb.h
int getnameinfo( const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags );
// 将返回的主机名服务名存储在 hostserv 参数指向的缓存中
// flags控制getnameinfo的行为getaddrinfo 和 getnameinfo 成功时返回0失败返回错误码。Linux下 strerror 函数能将数值错误码 error 转换为易读的字符串形式
#includenetdb.h
const char* gai_strerror( int error );