浅谈 Linux 下常用 Socket 选项设置

更新日期: 2019-12-01阅读: 2.3k标签: Linux

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选项:


注:可通过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层和应用层在网络编程领域的中间层之后,进一步明确读写套接字选项的函数,以及常见的套接字选项的设置方法以及设置原因,从而对整个套接字选项有一个基本认识。

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

原文 https://mp.weixin.qq.com/s/lPQQp5z81RJQhGLBFQweww

链接: https://fly63.com/article/detial/7116

putty对Linux上传下载文件或文件夹

putty是一个开源软件,目前为止最新版本为0.70。对于文件或文件夹的上传下载,在Windows下它提供了pscp和psftp两个命令。pscp在命令提示符中使用,只要putty(ssh)能够远程,就能使用该命令。

sed 和 awk,所有的 Linux 管理员都应该会的技能!

我曾经有一次在 Reddit 看到一个帖子。这是一个很简单的需求,就像我们常用 Unix 的人每天遇到的一样。他的问题是,如何删除文件中的重复行,只保留不重复的。 这听起来似乎很简单,但是当文件足够大时,就会有些复杂。

在linux上部署自己开发的web项目

相信有很多做开发的小伙伴和我之前一样,只会在windows环境下,利用开发工具开发运行web项目,但是却不知道怎么把开发好的项目部署到linux服务器上去,并能够外网访问,这里是我自己摸索总结的过程

w3m浏览网页_linux在命令符界面如何浏览网页

w3m是个开放源代码的命令行下面的网页浏览器。 它支持表格、框架、SSL连线、颜色。如果是在适当的terminal上,支持多种操作系统,在命令行终端可以很好的支持中文。即使在没有鼠标支持的情况下也可以检查网页的输出。本文列出常用的快捷键。

Linux运维:mysql数据库的备份与恢复

运维工程师的日常工作需要对各种数据进行备份,其中数据库数据的备份当属重点之一,为了方便管理,选择哪种备份方案是很重要的。全量备份就是指对某一个时间点上的所有数据或应用进行的一个完全拷贝

linux系统启动过程

首先计算机之中在主板上,有一个东西叫ROM(Read Only Memor),在ROM上固话了一些程序,被称为BIOS(基本输入输出系统),由于系统刚刚启动时处于实模式,关于什么是实模式,以及保护模式

Linux下4种禁用Root登陆的方法,你掌握了哪几种呢?

我们都知道 Linux 下 Root 用户的权限是最大的,因此一般不推荐直接使用 Root 用户操作。通常都是使用普通用户,在必要时通过 Sudo 命令来提权。在 Ubuntu 中,更是直接把 Root 用户直接禁用了。那么如何在 Linux 中禁止Root 登陆呢?今天,我们就来介绍几种常用的方法。

安全强化你的Linux服务器的七个步骤

这篇入门文章将向你介绍基本的 Linux 服务器安全知识。虽然主要针对 Debian/Ubuntu,但是你可以将此处介绍的所有内容应用于其他 Linux 发行版。我也鼓励你研究这份材料,并在适用的情况下进行扩展。

处理Linux文件的3个技巧

Linux 提供了许多用于查找、计数和重命名文件的命令。这有一些有用的选择。Linux 提供了多种用于处理文件的命令,这些命令可以节省你的时间

如何查看Linux哪些用户拥有sudo权限

本教程将讲解如何查看Linux用户是否有sudo权限。您还将学习如何查看Linux系统上的所有sudo用户。如何查看Linux哪些用户拥有sudo权限

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!