文章目录

  • 读取和设置 socket 选项
    • SO_REUSEADDR
    • SO_RCVBUF 和 SO_SNDBUF
    • SO_RCVLOWAT 和 SO_SNDLOWAT
    • SO_LINGER 选项
  • 网络信息API
    • gethostbyname 和 gethostbyaddr
    • getservbyname 和 getservbyport
    • getaddrinfo
    • getnameinfo

读取和设置 socket 选项

正如 fcntl 系统调用是控制文件描述符属性的通用 POSIX 方法;socket文件描述符的属性也有两个系统调用专门 读取设置

#include<sys/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 选项

level option 数据类型 说明 必须在 listen/connect 调用之前设置
SOL_SOCKET SO_DEBUG int 打开调试信息 YES
(通用 socket 选项,与协议无关) SO_REUSEADDR int 重用本地地址。如:可以令服务器处于 TIME_WAIT 的端口立刻被使用。
SO_TYPE int 获取 socket 类型
SO_ERROR int 获取并清除 socket 错误状态
SO_DONTROUTE int 不查看路由表,直接将数据发送给本地局域网内的主机。类同 send 系统调用的 MSG_DONTROUTE。 YES
SO_RCVBUF int TCP接收缓冲区大小(最小值是256字节) YES
SO_SNDBUF int TCP发送缓冲区大小(最小值是2048字节) YES
SO_RCVLOWAT int TCP接收缓冲区低水位标记 YES
SO_SNDLOWAT int TCP发送缓冲区低水位标记 YES
SO_RCVLOWAT int 接收数据超时
SO_SNDLOWAT int 发送数据超时
SO_KEEPALIVE int 发送周期性保活报文以维持连接 YES
SO_OBBINLINE int 接收到的带外数据将 在线存留 在普通数据的输入队列中,此时我们不能使用带 MSG_OOB 的读操作来读取带外数据,而应该像读取普通数据那样读取带外数据。 YES
SO_LINGER intger 若有数据待发送,则延迟关闭。 YES
IPPROTO_IP IP_TOS int 服务类型
(IPv4选项) IP_TTL int 存活时间
IPPROTO_IPV6 IPV6_NEXTHOP sockaddr_in6 下一跳IP地址
(IPv6选项) IPV6_RECVPKTINFO int 接受分组信息
IPV6_DONTFRAG int 禁止分片
IPV6_RECVTCLASS int 接受通信类型
IPPROTO_TCP TCP_MAXSEG int TCP最大报文段大小 YES
(TCP选项) TCP_NODELAY int 禁止Nagle算法 YES

服务器 而言,部分socket选项 只能在调用 listen 系统调用前 针对 socket 设置才有效。这是因为连接 socket 只能由 accept 调用返回,而 acceptlisten 监听队列 中接受的连接至少是个 半连接 ,这说明 服务器 已经往 客户端 上发送出了 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_REUSEADDR,SO_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 为设定的缓冲区大小
// len=sizeof( 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 选项的值时,我们需要给 setsockopt(getsockopt)系统调用传递一个 linger 类型的结构体:

#include <sys/socket.h>struct linger{int l_onoff; // 开启(非0)/关闭(0)该选项int l_linger; // 滞留时间
};

根据 linger 结构体中两个成员变量的不同值,close 系统调用可能产生如下行为:

  • l_onoff 等于 0。 此时 SO_LINGER 选项不起作用,close 用默认行为来关闭 socket
  • l_onoff 不为 0,l_linger 等于 0。 此时 close系统调用 立即返回,TCP模块 将丢弃被关闭的 socket 对应的 TCP发送缓冲区 中残留的数据,同时给对方发送一个 复位报文段。这为服务器提供了异常终止一个连接的方法。
  • l_onoff 不为 0,l_linger 大于 0。 此时 close 的行为取决于两个条件:
    1. 被关闭的 socket 对应的 发送缓冲区 中是否还有残留的数据;
    2. socket 是阻塞的还是非阻塞的,阻塞: close 将等待一段长为 l_linger 的时间,直到 TCP模板 发送完所有残留数据并得到对方的确认。如果没发送完并得到确认,则 close 返回 -1 并设置 errnoEWOULDBLOCK非阻塞: close 将立即返回,此时需要根据其 返回值errno 来判断残留数据是否发送完毕。

网络信息API

gethostbyname 和 gethostbyaddr

  • gethostbyname: 根据主机名称获取主机的完整信息。通常先在本地的 /etc/hosts 配置文件中查找主机,没有找到再去访问 DNS 服务器。
  • gethostbyaddr: 根据IP地址获取主机的完整信息。
#include<netdb.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 结构体类型的指针:

#include<netdb.h>
struct hostent{char* h_name; // 主机名char** h_aliases; // 主机别名列表,可有多个int h_addrtype; // 地址类型(地址族)int h_length; // 地址长度char** h_addr_list; // 按网络字节序列出的主机IP地址列表
};

getservbyname 和 getservbyport

根据名称/端口号获得某个服务的完整信息。实际上都是通过读取 /etc/services 文件来获取服务的信息的。

#include<netdb.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 结构体类型的指针:

#include<netdb.h>
struct servent{char* s_name; // 服务名称char** s_aliases; // 服务别名列表,可有多个int s_port; // 端口号char* s_proto; // 服务类型,通常是 tcp 或 udp
};

不可重入

gethostbynamegethostbyaddrgetservbynamegetservbyport 都是不可重入的,即非线程安全的。但是 netdb.h 头文件给出了它们的可重入版本:在原函数名尾部加上 _r(re-entrant)


getaddrinfo

既能通过主机名获取 IP 地址(内部使用的是 gethostbyname),也能通过服务名获取端口号(内部使用的是 getservbyname),是否可重入取决于内部调用的函数( gethostbynamegetservbyname )是否是它们的可重入版本。

#include<netdb.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_NUMERICHOST hostname 参数必须是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

内部使用 gethostbyaddrgetservbyport,是否可重入取决于内部调用的函数版本是否可重入:

#include<netdb.h>
int getnameinfo( const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags );
// 将返回的主机名(服务名)存储在 host(serv) 参数指向的缓存中
// flags控制getnameinfo的行为

getaddrinfogetnameinfo 成功时返回0,失败返回错误码。Linux下 strerror 函数能将数值错误码 error 转换为易读的字符串形式:

#include<netdb.h>
const char* gai_strerror( int error );

Linux网络编程 | socket选项设定 及 网络信息API相关推荐

  1. Linux 网络编程——socket 网络编程

    文章目录 一.网络基础 TCP/UDP对比 TCP/IP协议族体系 socket IP地址 IP地址转化API inet_addr() inet_aton() inet_ntoa() inet_pto ...

  2. Python网络编程socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

  3. linux下网络编程设置非阻塞,UNIX网络编程 非阻塞connect的实现

    一.<UNIX网络编程>-非阻塞connect 在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未 ...

  4. 树莓派:入门(基础配置、GPIO、网络编程 Socket)

    树莓派在创客中越来越发挥重要的作用,树莓派的强大兼用性和功能丰富,得到 DIY 朋友的青睐.带大家认识目前最新的树莓派 3B+,从零基础到入门,到动手做有趣的应用. 本场 Chat 首先会带领大家入门 ...

  5. java网络编程socket\server\TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅 1 TCP的开销 a  连接 ...

  6. Python网络编程(Socket)

    Python网络编程(Socket) Python提供了两个访问级别的网络服务.在一个较低的水平,您可以访问底层操作系统的基本套接字支持,允许你实现面向连接和无连接协议的客户端和服务器 Python有 ...

  7. Java网络编程 Socket、ServerSocket 详解,方法介绍及完整代码示例

    Java网络编程 Socket.ServerSocket 详解,方法介绍及完整代码示例 概念 什么是网络编程? 网络编程是指编写运行在多个设备(计算机)的程序,这些设备通过网络连接起来.当这些通过网络 ...

  8. java 中的网络编程(Socket、TCP三次握手四次挥手、TCP/UDP/URL)

    文章目录 前言 一.网络编程概述 二.网络通信要素概述 1.如何实现网络中的主机互相通信 2.网络通信协议 3.IP和端口号 4.InetAddress类 5.网络协议 6.TCP/IP协议簇 7.T ...

  9. JAVA零学习之基础归纳(十八)--网络编程、协议、IP、基本API、URL、socket等

    [Java程序可以非常方便的访问互联网上的 HTTP服务.FTP服务等,并可以直接取得互联网上的远程资源,还可以向远程资源发送 GET.POST请求] 一.网络编程 1.1 网络概念和分类 所谓计算机 ...

最新文章

  1. JavaScript新手学习笔记4——我记不住的几个坑:短路逻辑、按值传递、声明提前...
  2. linux创建文件怎么输入换行_Revit 怎么创建自定义的族文件?
  3. 图解操作系统研发与探索教程
  4. Spring Boot 参考指南(运行你的应用程序)
  5. python 支付宝个人账单_解析2018年度三大用户数据报告——网易云音乐、支付宝、微信...
  6. java接口配置文件_Java读取property配置文件,另接口的配置
  7. pandas 每一列相加_Python3 numpy amp; pandas 学习笔记
  8. android客户端日志,更新日志 - BugHD Android 客户端上线
  9. C++test对多变参数的函数打桩处理技巧
  10. 18个国外优质图片素材
  11. 30分钟!用Django做一个迷你的Todolist!下篇!
  12. java中var是什么意思_Java 10中的var是什么?
  13. java程序设计课后答案 刘慧宁_【单选题】建筑立面图中,室外地坪轮廓线应用( )。...
  14. 史上最美八大隶书,你都临过吗?
  15. Arcmap做地形地貌图流程
  16. C#练习题答案: 反恐精英系列【难度:1级】--景越C#经典编程题库,1000道C#基础练习题等你来挑战
  17. TP5的自动过滤方法
  18. 市场调研方法:焦点小组访谈法
  19. avr模拟串口通讯c语言,AVR的模拟串口的问题
  20. Python+Selenium爬取新浪微博评论数据

热门文章

  1. 华为什么时候更新鸿蒙os,华为鸿蒙系统升级时间表
  2. git 代码回滚_能提交到远程的Git回滚
  3. 学霸系统计算机天才,小欢喜:开局获得学霸系统
  4. Office web app server2013详细的安装和部署
  5. java面试宝典 多线程,《java面试宝典》之java多线程面试题
  6. 300plc与组态王mpi通讯_S7-300与S7-200之间的MPI通信
  7. usb接口供电不足_AMD RX 6000 系列显卡配备USB-C 接口,支持外接供电
  8. Python3 爬虫学习笔记 C12【验证码对抗系列 — 图形验证码】
  9. Mybatis四种分页方式
  10. 【ZOJ - 3870】Team Formation(异或,思维)