19

浅谈 Linux 下常用 Socket 选项设置

 4 years ago
source link: https://mp.weixin.qq.com/s/lPQQp5z81RJQhGLBFQweww
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

0.前言

TCP/IP协议栈是Linux内核的重要组成部分和网络编程的基石,虽然Linux和BSD有很大的联系,但是对于某些Socket选项和内核操作仍然存在差异,因此文中适用场景均为仅Linux。

《UNIX网络编程》是已故UNIX网络专家W. Richard Stevens博士(1951-1999)、世界著名网络专家Bill Fenner和Andrew M. Rudoff完成的权威著作,该书对网络编程进行全面而深入的阐述,是提高网络编程功力的不二之选。

1.Socket和TCP/IP的关系

"All problems in computer science can be solved by another level of indirection."

为满足应用层需求,系统对TCP/IP层进行细节屏蔽和抽象,Socket层就相当于TCP/IP和应用层之间的中间层。

常用的socket/bind/accept/connect就是抽象出来的接口,使用它们可以快速进行网络程序开发,可见Socket中间层的重要性。

Socket选项就是为满足用户的定制化需求而生的。我们经常遇到的情况包括地址复用、端口复用、读写超时时间、读写缓冲区大小等。

在Linux的TCP/IP协议栈中包括很多Socket选项,它们会出现在TCP层、IP层、Socket层等,为此在读取和设置socket选项时需要指定level。

如图可以看到Socket层作为中间层以及各层支持的部分Socket选项:

M7JVFni.png!web

注:可通过man 7 tcp/man 7 ip查看tcp/ip各层Socket选项详细定义和添加内核版本等信息。

2.操作Socket选项的API

读取和设置Socket选项的API包括:

getsockopt、setsockopt、fcntl、ioctl等;

其中fcntl和ioctl用来设置socket的阻塞和非阻塞状态。

通过man获得的函数定义:

//ioctl函数定义

#include <sys/ioctl.h>

int ioctl(int d, int request, ...);


//fcntl函数定义

#include <unistd.h>

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );


//get/setsockopt函数定义

#include <sys/types.h>

#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,const void *optval, socklen_t optlen);

3.get/setsockopt使用说明

使用时需要按照函数要求的形参格式进行传递,显式指明其所在的level以及选项名称optname、optval类型和长度optlen。

  • level参数说明

从sys/socket.h的源码中可以看到对于level的说明如下:

/* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */

#define SOL_IP 0

#define SOL_IPX 256

#define SOL_AX25 257

#define SOL_ATALK 258

#define SOL_NETROM 259

#define SOL_TCP 6

#define SOL_UDP 17

#define SOL_SOCKET 0xffff

  • optval和optlen参数说明

optval和optlen均为指针类型,这两个参数与当前操作的option有直接关系,可以看到optval使用void*类型,optlen使用socklen_t*类型。

socklen_t类型说明:socklen_t和int应该具有相同的长度,否则会破坏 BSD套接字层的填充,POSIX开始时候用的是size_t。

Linus Torvalds 向他们解释使用size_t是完全错误的,因为在64位结构中 size_t和int的长度是不一样的,而这个参数的长度必须和int一致,最终POSIX的那帮家伙找到了解决的办法,创造了 一个新的类型socklen_t。

Linux Torvalds说这是由于他们发现了自己的错误但又不好意思承认,所以另外创造了一个新的数据类型。

指针使用:optval和optlen两个指针类型是缺一不可的,optval为void*类型如果没有长度说明,系统函数在调用时就无法获取边界,optlen为底层调用指明内存起始地址对应的偏移量,这是C中常用的指针操作模式。

Socket选项多是int和bool类型 但是也有一些复合类型比如linger,因此在读写选项是对于optval和optlen的编写要根据实际而定。

4. SO_REUSEADDR选项

典型场景:在《Unix网络编程》卷一中指出了SO_REUSEADDR的重要使用场景:当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

TIME_WAIT :如何优雅关闭Socket是个值得思考的问题, TIME_WAIT状态是TCP协议为了保证全双工连接可靠性设置的,感兴趣可以查阅TIME_WAIT的作用,并不要一味的谈TIME_WAIT色变,这里就不展开了。

设置方法:未设置SO_REUSEADDR,在重启时就会绑定失败显示资源被占用,需要等待该IP+Port被释放才可以重启成功,该问题对于线上服务不可接受。

因此需要将服务端的socket设置为地址复用:

int enable = 1;

setsocketopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&enable,sizeof(enable));

5. SO_REUSEPORT选项

作用效果:端口复用选项SO_REUSEPORT是在SO_REUSEADDR之后于Linux3.9版本加入的,并不是所有系统都支持该选项。 SO_REUSEPORT 允许多个进程监听相同的IP和Port,但是为了防止端口劫持增加了对进程所属用户的限制。

内核支持:端口复用选项是个非常大的进步,有利于服务端程序扩展、提高并发能力。值得一提的是SO_REUSEPORT在内核层面实现了简单的负载均衡,为监听的多个进程进行流量分发。

Nginx应用:Nginx的1.9.1版本引入了SO_REUSEPORT套接字选项,对于Nginx而言,启用该选项可以减少在某些场景下的锁竞争而改善性能。

Linux 3.9版本和Nginx1.9.1版本(含)之后的版本,Nginx已经无需再使用 互斥锁 ngx_use_accept_mutex,引入SO_REUSEPORT选项由内核层面实现负责均衡来解决惊群问题。

设置方法:

int enable = 1;

setsocketopt(sockfd,SOL_SOCKET,SO_REUSEPORT,(void*)&enable,sizeof(enable));

6. TCP_NODELAY选项

简单背景 : 为解决福特公司局域网拥塞问题 ,Nagle算法由福特公司的John Nagle 在1984年提出。同时代的其他网络也存在这种情况,因此Naggle算法被引入到协议栈。

算法原则:尽可能发送大块数据,避免网络中充斥着许多小数据块,任意时刻最多只能有一个未被确认的小段。未被确认是指一个数据块发送出去后,没有收到对方发送的ACK确认。

通俗解释:就是在两座城市的高速路上之前充斥着非常多的货车,货车的车厢中可能是一根羽毛、一个玩具熊或者一台机器等,造成了高速路的拥堵。为此要求每次最多只有一辆未被授权的货车行驶且每个货车装载尽可能多的东西,从而提高单次运输效率和降低货车数量,缓解高速路的拥堵。

算法弊端:上世纪80年代网络带宽有限,Nagle算法有效改善了网络拥塞情况,但是随着网络带宽的增加和通信基础设施水平的提高,最多只能有一个未被确认的小段的限制导致了无意义的等待,无法有效利用当前的网络带宽。

算法禁用:TCP_NODELAY可以解决Nagle算法带来的问题,开启TCP_NODELAY意味着允许小包的发送且不强制等待,对时效高且数据量小的应用非常实用。从应用程序的角度来说应该尽量避免写小包,从而实现数据包大小和数据包数量的效率最大化。

设置方法 :

int enable = 1;

setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable,sizeof(enable));

注:CORK 算法与 Nagle 算法非常类似,感兴趣可自行查阅。

7.  小结

在了解了Socket作为TCP/IP层和应用层在网络编程领域的中间层之后,进一步明确读写套接字选项的函数,以及常见的套接字选项的设置方法以及设置原因,从而对整个套接字选项有一个基本认识。

套接字选项本身很多,但是我们常用的并不多,需要根据自己的实际情况和该选项的作用来进行调整,不理解背后机理的调整多半会留坑。


Recommend

  • 41
    • developer.51cto.com 5 years ago
    • Cache

    浅谈几种常用负载均衡架构

    在网站创立初期,我们一般都使用单台机器对台提供集中式服务,但随着业务量越来越大,无论性能还是稳定性上都有了更大的挑战。这时候我们就会想到通过扩容的方式来提供更好的服务。

  • 29
    • www.tuicool.com 5 years ago
    • Cache

    浅谈几种常用的编码方式

    浅谈几种常用编码方式 最近在做加解密这块的开发,使用频率最高的就是 Nodejs 的 crypto 模块了,中间出现了很多种不同的编码方式。一直对这些编码方式处于熟练拼写的状态,但是到底有什么区别处于...

  • 19
    • 微信 mp.weixin.qq.com 4 years ago
    • Cache

    Linux Socket 收发Json

    点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,麻烦点个在看或点个赞,感谢~ 不管是Qt开发还是 linux 嵌入式应用开发,一个人的核心竞争力还是不断思考,也就是不断琢磨。 下面的程序主要是Linux C Socket 读取...

  • 13
    • 微信 mp.weixin.qq.com 4 years ago
    • Cache

    Linux C Socket Api详解

    点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,麻烦点个在看或点个赞,感谢~ 文章主要梳理于《UNIX 环境高级编程第二版》 第十六章 网络 IPC: 套接字 以前都只是在网上搜的能用的例子,对一些参数不是很清楚,这次...

  • 35
    • studygolang.com 4 years ago
    • Cache

    设置各种选项的最佳套路

    背景 在 Go 里面写一个 struct 时,经常会遇到要给 struct 里面的各个字段提供设置功能。这个问题看起来很简单很容易,实际上困扰了不少人,连 Go 的三巨头之一 Rob Pike 都曾经为之苦恼了一段时间,后来找到了最佳实践后还为...

  • 9
    • blog.csdn.net 3 years ago
    • Cache

    随想录(常用gdb选项)

    随想录(常用gdb选项)

  • 3

    Android开发实践:如何设置NDK的编译选项 – Android开发中文站你的位置:Android开发中文站 > Android开发 >

  • 7

    2.5.1  General选项卡 General选项卡包含Altium De...

  • 6
    • www.yimiaotui.com 1 year ago
    • Cache

    微信的朋友设置有哪些选项?

    微信的朋友设置有哪些选项? 微信推广 发布日...

  • 6

    win11怎么进入高级启动选项设置?我们在使用电脑的过程中,可以使用高级启动模式进行故障排除,诊断,修复等系统修复服务。各位如果感觉电脑系统出现了问题,就可以选择进入这个模式进行系统修复,不过高级启动选项设置的进入方法非...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK