Linux网络编程 | socket选项设定 及 网络信息API
文章目录
- 读取和设置 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
调用返回,而 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_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 的行为取决于两个条件:
- 被关闭的
socket
对应的发送缓冲区
中是否还有残留的数据; - 该
socket
是阻塞的还是非阻塞的,阻塞:close
将等待一段长为l_linger
的时间,直到TCP模板
发送完所有残留数据并得到对方的确认。如果没发送完并得到确认,则close
返回-1
并设置errno
为EWOULDBLOCK
。非阻塞: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
};
不可重入
gethostbyname
、gethostbyaddr
、getservbyname
和 getservbyport
都是不可重入的,即非线程安全的。但是 netdb.h
头文件给出了它们的可重入版本:在原函数名尾部加上 _r(re-entrant)
。
getaddrinfo
既能通过主机名获取 IP
地址(内部使用的是 gethostbyname
),也能通过服务名获取端口号(内部使用的是 getservbyname
),是否可重入取决于内部调用的函数( gethostbyname
、 getservbyname
)是否是它们的可重入版本。
#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
内部使用 gethostbyaddr
和 getservbyport
,是否可重入取决于内部调用的函数版本是否可重入:
#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的行为
getaddrinfo
和 getnameinfo
成功时返回0,失败返回错误码。Linux下 strerror
函数能将数值错误码 error
转换为易读的字符串形式:
#include<netdb.h>
const char* gai_strerror( int error );
Linux网络编程 | socket选项设定 及 网络信息API相关推荐
- Linux 网络编程——socket 网络编程
文章目录 一.网络基础 TCP/UDP对比 TCP/IP协议族体系 socket IP地址 IP地址转化API inet_addr() inet_aton() inet_ntoa() inet_pto ...
- Python网络编程socket
网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...
- linux下网络编程设置非阻塞,UNIX网络编程 非阻塞connect的实现
一.<UNIX网络编程>-非阻塞connect 在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未 ...
- 树莓派:入门(基础配置、GPIO、网络编程 Socket)
树莓派在创客中越来越发挥重要的作用,树莓派的强大兼用性和功能丰富,得到 DIY 朋友的青睐.带大家认识目前最新的树莓派 3B+,从零基础到入门,到动手做有趣的应用. 本场 Chat 首先会带领大家入门 ...
- java网络编程socket\server\TCP笔记(转)
java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04| 分类: Socket | 标签:java |举报|字号 订阅 1 TCP的开销 a 连接 ...
- Python网络编程(Socket)
Python网络编程(Socket) Python提供了两个访问级别的网络服务.在一个较低的水平,您可以访问底层操作系统的基本套接字支持,允许你实现面向连接和无连接协议的客户端和服务器 Python有 ...
- Java网络编程 Socket、ServerSocket 详解,方法介绍及完整代码示例
Java网络编程 Socket.ServerSocket 详解,方法介绍及完整代码示例 概念 什么是网络编程? 网络编程是指编写运行在多个设备(计算机)的程序,这些设备通过网络连接起来.当这些通过网络 ...
- java 中的网络编程(Socket、TCP三次握手四次挥手、TCP/UDP/URL)
文章目录 前言 一.网络编程概述 二.网络通信要素概述 1.如何实现网络中的主机互相通信 2.网络通信协议 3.IP和端口号 4.InetAddress类 5.网络协议 6.TCP/IP协议簇 7.T ...
- JAVA零学习之基础归纳(十八)--网络编程、协议、IP、基本API、URL、socket等
[Java程序可以非常方便的访问互联网上的 HTTP服务.FTP服务等,并可以直接取得互联网上的远程资源,还可以向远程资源发送 GET.POST请求] 一.网络编程 1.1 网络概念和分类 所谓计算机 ...
最新文章
- JavaScript新手学习笔记4——我记不住的几个坑:短路逻辑、按值传递、声明提前...
- linux创建文件怎么输入换行_Revit 怎么创建自定义的族文件?
- 图解操作系统研发与探索教程
- Spring Boot 参考指南(运行你的应用程序)
- python 支付宝个人账单_解析2018年度三大用户数据报告——网易云音乐、支付宝、微信...
- java接口配置文件_Java读取property配置文件,另接口的配置
- pandas 每一列相加_Python3 numpy amp; pandas 学习笔记
- android客户端日志,更新日志 - BugHD Android 客户端上线
- C++test对多变参数的函数打桩处理技巧
- 18个国外优质图片素材
- 30分钟!用Django做一个迷你的Todolist!下篇!
- java中var是什么意思_Java 10中的var是什么?
- java程序设计课后答案 刘慧宁_【单选题】建筑立面图中,室外地坪轮廓线应用( )。...
- 史上最美八大隶书,你都临过吗?
- Arcmap做地形地貌图流程
- C#练习题答案: 反恐精英系列【难度:1级】--景越C#经典编程题库,1000道C#基础练习题等你来挑战
- TP5的自动过滤方法
- 市场调研方法:焦点小组访谈法
- avr模拟串口通讯c语言,AVR的模拟串口的问题
- Python+Selenium爬取新浪微博评论数据
热门文章
- 华为什么时候更新鸿蒙os,华为鸿蒙系统升级时间表
- git 代码回滚_能提交到远程的Git回滚
- 学霸系统计算机天才,小欢喜:开局获得学霸系统
- Office web app server2013详细的安装和部署
- java面试宝典 多线程,《java面试宝典》之java多线程面试题
- 300plc与组态王mpi通讯_S7-300与S7-200之间的MPI通信
- usb接口供电不足_AMD RX 6000 系列显卡配备USB-C 接口,支持外接供电
- Python3 爬虫学习笔记 C12【验证码对抗系列 — 图形验证码】
- Mybatis四种分页方式
- 【ZOJ - 3870】Team Formation(异或,思维)