- #include<sys/types.h>
- #include<sys/socket.h>
-
- int sock = ::socket(PF_INET, SOCK_STREAM, 0);
原型:int socket(int domain, int type, int protocol);
domain: 协议族,可以是PF_INET,PF_INET6,PF_UNIX
type: socke类型,可以是SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,可以或(|)上SOCK_NONBLOCK(非阻塞), SOCK_CLOEXEC(close on exec: fork的子进程在执行exec的时候自动关闭已经打开的sock)
其中:SOCK_STREAM:传输层,TCP
SOCK_DGRAM:传输层,UDP
SOCK_RAW:网络层(处理ICMP、IGMP等网络报文、特殊的IPv4报文、可以通过IP_HDRINCL套接字选项由用户构造IP头)
protocol: 在对应domain中对应的协议类型,通常为一一对应,设为0
参考:
SOCK_RAW 与 SOCK_STREAM 、SOCK_DGRAM 的区别_zhu2695的博客-CSDN博客
- struct linger opt = {1, 1};
- setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&opt, sizeof(opt));
原型:int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
sockfd:创建的fd
level:需要设置option所在的协议层,可以是SOL_SOCKET(套接字选项),IPPROTO_IP(IP选项),IPPROTO_TCP(TCP选项), IPPROTO_UDP(UDP选项)
其中一些SOL_SOCKET选项有:man 7 socket
SO_REUSEADDR: bind()时,即使需要绑定的地址被其他处于TIME_WAIT状态的socket占用,也可以立刻重用(也可以改/proc/sys/net/ipv4/tcp_tw_recycle来快速回收TIME_WAIT的socket,不建议)
SO_KEEPALIVE: tcp keepalive,设置后,如果tcp连接在7200s内没有数据交互,会触发tcp keepalive机制,(通常服务器端)会给对端主机发送keep-alive probe的tcp数据包(包含1个字节或者不含数据):
如果对方回复ack,则再次等7200s发送keep-alive;
如果对方崩溃以重启会回复RST,此时socket待处理错误被置为ECONNRESET,socket本身则被关闭;
如果对方没有回复,会每隔75s发送一次,发送9次,如果仍然没有应答,会断掉tcp连接,socket待处理错误被置为ETIMEOUT并且被关闭,如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。
SO_LINGER: 控制socket在close或shutdown时,是否需要在缓冲区的数据发送出去后再返回
默认情况,在调用close()后,close会立即返回,内核的TCP模块会负责把socket对应缓冲区的数据发给对方。
如果l_onoff为0,则SO_LINGER不起作用,close用默认的行为关闭socket
如果l_onoff为1,l_linger为0,则close立即返回,TCP模块会丢弃发送缓冲区的数据,并且给对方发一个RST报文(可避免TIME_WAIT,但是可能会丢失数据)
如果l_onoff为1,l_linger大于0:
如果close是阻塞的,close会等待l_linger的时间,直到TCP模块把缓冲区的数据发完并收到ACK,如果在l_linger时间内,没有发送并确认完,close会返回-1,并且errno置为EWOULDBLOCK。
如果close是非阻塞的,close立即返回并且丢弃缓冲区数据。
struct linger { int l_onoff; /* linger active */ int l_linger; /* how many seconds to linger for */ };SO_OOBINLINE:在常规数据流中接收带外数据
SO_SNDBUF:发送缓冲区大小(udp没用,因为直接发到网络中去了)
SO_RCVBUF:接收缓冲区大小(socket级别,所以可以tcp/udp)
SO_SNDLOWAT:发送缓冲区的低水平标记位 (performance优化措施,防止cpu一直占用)
只对non-blocking的socket起作用,对于non-blocking的socket,send只能发送全部的数据或者SO_SNDLOWAT以上的数据(如果SO_SNDLOWAT为100,如果发送50字节,send会成功发送50字节或者返回失败,如果发送200字节,send可能成功发送100到200字节之间大小的数据,或者返回失败。默认SO_SNDLOWAT为1,send能发送1字节或者返回EWOULDBLOCK)
如果socket是block的,如果send不能发送全部的数据,会block,一直到有足够的缓冲区空间或者timeout(SO_SNDTIMEO)。
如果是select/poll/epoll,只有在缓冲区至少SO_SNDLOWAT的数据可写之后,才会返回socket可写。
SO_RCVLOWAT:接收缓冲区的低水平标记位
(非阻塞如何?值得验证) 对于阻塞recv会返回request的字节数或者超过SO_RCVLOWAT字节的数据
select/poll/epoll,只有在缓冲区至少有SO_RCVLOWAT的数据可读之后,才会返回socket可读
SO_RCVTIMEO:接收数据的超时时间。如果设置了该选项,recv/recvfrom函数会有一个block的时限,如果超过该时间就仍没有接收到更多的数据,会返回部分数据或者没有收到数据,返回失败(errno置为EWOULDBLOCK或EAGAIN),默认值为0(一直不会超时)。使用的结构体为struct timeval
#include <sys/time.h> struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ };SO_SNDTIMEO:发送数据的超时时间。类似SO_RCVTIMEO
optname:上面提到的SO_SOCKET选项
optval:选项对应的结构体,通常为int/bool
SO_LINGER/SO_SNDTIMEO/SO_RCVTIMEO分别有专门的结构体
optlen:optval的字节大小
参考:
Network Sockets and Communication (Guile Reference Manual)
setsockopt 详解_张忠琳的博客-CSDN博客_linux setsockopt
10.2. Changing Network Kernel Settings Red Hat Enterprise Linux 5 | Red Hat Customer Portal
linux - What's the purpose of the socket option SO_SNDLOWAT - Stack Overflow
TCP keepalive的详解(解惑) - 翔云123456 - 博客园
方法1 - 整个系统范围:
7200秒, 75秒, 9次这3个值可以通过更改这几个值
/proc/sys/net/ipv4/tcp_keepalive_time /proc/sys/net/ipv4/tcp_keepalive_intvl /proc/sys/net/ipv4/tcp_keepalive_probes或者在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置, 添加完下面的配置后输入
sysctl -p
使其生效,并使用命令sysctl -a | grep keepalive 来查看当前的默认配置
net.ipv4.tcp_keepalive_time=7200 net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9方法2 - TCP单个连接的:man 7 tcp
使用tcp options:
TCP_KEEPCNT
、TCP_KEEPIDLE
、TCP_KEEPINTVL
#include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> int keepalive = 1; int count = 5; setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive)); setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void *)&count, sizeof(count));
面试官:TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗?_小林coding的技术博客_51CTO博客
两者实现在不同的层次,作用也不同:
TCP keep-alive是TCP层(内核态)实现的,称为 TCP 保活机制,用于确保TCP连接的有效性,检测连接错误;
HTTP keep-alive是在应用层(用户态)实现的,称为 HTTP 长连接,用于HTTP连接复用,同一个连接上串行方式传递请求-响应数据,减少HTTP短连接需要多次建立/释放TCP连接消耗的资源;
TCP中已有SO_KEEPALIVE选项,为什么还要在应用层加入心跳包机制?? - 知乎
- tcp keepalive只能检测连接是否存活,不能检测连接是否可用。比如服务器因为负载过高导致无法响应请求但是连接仍然存在,或者出现进程死锁或阻塞,此时keepalive可以正常收发,但检测出连接出错。
- 客户端与服务器之间有四层代理或负载均衡,即在传输层之上的代理,只有传输层以上的数据才被转发,例如socks5等
- 如果TCP连接中的另一方因为停电突然断网,我们并不知道连接断开,此时发送数据失败会进行重传,由于重传包的优先级要高于keepalive的数据包,因此keepalive的数据包无法发送出去。只有在长时间的重传失败之后我们才能判断此连接断开了。
通过sysctl -a | grep tcp_rmem 和 tcp_wmem
net.ipv4.tcp_rmem = 4096 131072 6291456 net.ipv4.tcp_wmem = 4096 16384 4194304或者cat /proc/sys/net/ipv4/tcp_rmem cat /proc/sys/net/ipv4/tcp_wmem
# cat /proc/sys/net/ipv4/tcp_rmem 4096 131072 6291456 # cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 4194304这三个数值左中右分别是系统中tcp接收和发送缓冲区的最小值,默认值,最大值。用getsockopt可以得到默认值。
int wmem = 0; socklen_t wmem_len = sizeof(wmem); getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&wmem, &wmem_len);可以通过设置SO_SNDBUF/SO_RCVBUF值设置单个socket的tcp发送/接收缓冲区。验证后,在一定范围内,系统会将设置值加倍,比如设置10240后,通过getsockopt得到的为20480
int wmem = 10240; setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&wmem, sizeof(wmem));
通过sysctl -a | grep rmem
net.core.rmem_default = 212992 net.core.rmem_max = 212992可以通过设置SO_RCVBUF值设置单个udp socket(SOCK_DGRAM)的接收缓冲区,和tcp缓冲区一样,系统会将设置值加倍
int rmem = 10240; socklen_t rmem_len = sizeof(rmem); setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rmem, sizeof(rmem)); getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rmem, &rmem_len);
SO_SNDBUF和SO_RCVBUF_库昊天的博客-CSDN博客_so_rcvbuf 设置上限
TCP滑动窗口(发送窗口和接受窗口) - hongdada - 博客园
- //总长度16字节
- struct sockaddr {
- sa_family_t sa_family; // unsigned short int,16bit,2字节,AF_xxx
- char data[14];
- };
-
- //总长度128字节
- struct sockaddr_storage {
- sa_family_t sa_family;
- char __ss_padding[_SS_PADSIZE];
- __ss_aligntype __ss_align; /* Force desired alignment. */
- };
- // IP协议族 IPv4
- // 总长度16字节
- struct sockaddr_in {
- sa_family_t sin_family; // AF_INET
- uint16_t sin_port; // e.g. htons(3490)
- struct in_addr sin_addr;
-
- /* Pad to size of `struct sockaddr'. 8 字节*/
- unsigned char sin_zero[sizeof (struct sockaddr)
- - sizeof (sa_family_t)
- - sizeof (uint16_t)
- - sizeof (struct in_addr)];
- };
- struct in_addr {
- uint32_t s_addr;
- };
-
- // IP协议族 IPv6
- // 总长度28字节
- struct sockaddr_in6 {
- sa_family_t sin6_family; // AF_INET6
- uint16_t sin6_port; /* Transport layer port # */
- uint32_t sin6_flowinfo; /* IPv6 flow information */
- struct in6_addr sin6_addr; /* IPv6 address */
- uint32_t sin6_scope_id; /* IPv6 scope-id */
- };
- struct in6_addr {
- union {
- uint8_t __u6_addr8[16];
- uint16_t __u6_addr16[8];
- uint32_t __u6_addr32[4];
- } __in6_u;
- };
-
- //UNIX本地协议族
- struct sockaddr_un {
- sa_family_t sun_family; // AF_LOCAL/AF_UNIX
- char sun_path[108];
- };
PF_INET: 协议族,AF_INET:地址族
illumos: manual page: sockaddr_storage.3socket
Implementing AF-independent application
recvfrom 辅助数据
recvmsg(2) - OpenBSD manual pages
UNIX网络编程读书笔记:辅助数据 - ITtecman - 博客园