golang 安全的tcp server_Go 语言使用 TCP_NODELAY 控制发包流量

编写健壮且高性能的网络服务需要付出大量的努力。提高服务性能的方式有很多种,比如优化应用层的代码,更进一步,还可以看看垃圾回收器,操作系统,网络传输,以及部署我们服务的硬件是否有优化空间。

TCP/IP 协议栈中的一些算法会影响到服务性能。本文将简单介绍其中的 Nagle 算法,与 Nagle 算法相关的 socket 选项TCP_NODELAY,以及在 Go 语言中如何使用它。

理论

大部分平台上的 TCP 实现都提供了 socket 选项,用于控制连接生命周期,流量控制等算法。

其中一个会对网络传输性能造成影响的算法是 Nagle 算法,它在 Linux,macOS,Windows 平台默认都是打开的。

Nagle 算法的做法是:将要发送的小包合并,并延缓发送。延缓后的发送策略是,收到前一个发送出去的包的 ACK 确认包,或者一定时间后,收集了足够数量的小数据包。

Nagle 算法的目的是减少发送小包的数量,从而减小带宽,并提高网络吞吐量,付出的代价是有时会增加服务的延时。(译者 yoko 注:补充解释一下为什么减少小包的数量可以减小带宽。因为每个 TCP 包,除了包体中包含的应用层数据外,外层还要套上 TCP 包头和 IP 包头。由于应用层要发送的业务数据量是固定的,所以包数量越多,包头占用的带宽也越多)

引入的延时通常在毫秒级别,但是对于延迟敏感的服务来说,减少一些毫秒数的延迟也是值得的。

Nagle 算法所对应的 TCP socket 选项是TCP_NODELAY。开启TCP_NODELAY可以禁用 Nagle 算法。禁用 Nagle 算法后,数据将尽可能快的被发送出去。

另外,我们也可以在应用层对数据进行缓存合并发送来达到 Nagle 算法的目的(译者 yoko 注:在 Go 语言中即使用bufio.Writer。个人认为,使用bufio.Writer还有一个好处,就是减少了调用 write 系统调用的次数,但是相应的,增加了数据拷贝的开销)。

在 Go 语言中,TCP_NODELAY默认是开启的,并且标准库提供了net.SetNodelay(bool)方法来控制它。

实验

我们通过一个小实验来观察TCP_NODELAY打开和关闭时底层 TCP 包的变化。

代码逻辑十分简单,client 端连续调用 5 次conn.Write函数向 server 端发送相同的字符串GOPHER

服务端代码(server.go):

package main

import (
"bufio"
"fmt"
"log"
"net"
"strings"
)

func main() {
port := ":" + "8000"

// 创建监听
l, err := net.Listen("tcp", port)
if err != nil {
log.Fatal(err)
}
defer l.Close()

for {
// 接收新的连接
c, err := l.Accept()
if err != nil {
log.Println(err)
return
}

// 处理新的连接
go handleConnection(c)
}
}
func handleConnection(c net.Conn) {
fmt.Printf("Serving %s\n", c.RemoteAddr().String())

for {
// 读取数据
netData, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
log.Println(err)
return
}

cdata := strings.TrimSpace(netData)
if cdata == "GOPHER" {
c.Write([]byte("GopherAcademy Advent 2019!"))
}

if cdata == "EXIT" {
break
}
}
c.Close()
}

客户端代码(client.go):

package main

import (
"fmt"
"log"
"net"
)

func main() {
target := "localhost:8000"

raddr, err := net.ResolveTCPAddr("tcp", target)
if err != nil {
log.Fatal(err)
}

// 和服务端建立连接
conn, err := net.DialTCP("tcp", nil, raddr)
if err != nil {
log.Fatal(err)
}

// conn.SetNoDelay(false) // 如果打开这行代码,则禁用TCP_NODELAY,打开Nagle算法

fmt.Println("Sending Gophers down the pipe...")

for i := 0; i < 5; i++ {
// 发送数据
_, err = conn.Write([]byte("GOPHER\n"))
if err != nil {
log.Fatal(err)
}
}
}

为了观察 TCP 包,首先开启抓包程序 tcpdump。为了简单,两个程序都在本机运行。我环境的内网环路网卡为lo0,不同机器上可能不同:

$sudo tcpdump -X  -i lo0 'port 8000'

然后,再打开两个终端窗口,先执行服务端程序,再执行客户端程序:

$go run server.go
$go run client.go

观察抓包结果,我们会发现每次调用 write 函数发送"GOPHER",对应的都是一个独立的 TCP 包被发送。总共有 5 个 TCP 包。以下是抓包结果,为了简单,我只贴出两个包:

....
14:03:11.057782 IP localhost.58030 > localhost.irdmi: Flags [P.], seq 15:22, ack 1, win 6379, options [nop,nop,TS val 744132314 ecr 744132314], length 7
0x0000: 4500 003b 0000 4000 4006 0000 7f00 0001 E..;..@.@.......
0x0010: 7f00 0001 e2ae 1f40 80c5 9759 6171 9822 .......@...Yaq."
0x0020: 8018 18eb fe2f 0000 0101 080a 2c5a 8eda ...../......,Z..
0x0030: 2c5a 8eda 474f 5048 4552 0a ,Z..GOPHER.
14:03:11.057787 IP localhost.58030 > localhost.irdmi: Flags [P.], seq 22:29, ack 1, win 6379, options [nop,nop,TS val 744132314 ecr 744132314], length 7
0x0000: 4500 003b 0000 4000 4006 0000 7f00 0001 E..;..@.@.......
0x0010: 7f00 0001 e2ae 1f40 80c5 9760 6171 9822 .......@...`aq."
0x0020: 8018 18eb fe2f 0000 0101 080a 2c5a 8eda ...../......,Z..
0x0030: 2c5a 8eda 474f 5048 4552 0a ,Z..GOPHER.

...

如果我们打开客户端中被注释掉的conn.SetNoDelay(false)这行代码,也即禁用掉TCP_NODELAY,开启 Nagle 算法,再次抓包,结果如下:

14:27:20.120673 IP localhost.64086 > localhost.irdmi: Flags [P.], seq 8:36, ack 1, win 6379, options [nop,nop,TS val 745574362 ecr 745574362], length 28
0x0000: 4500 0050 0000 4000 4006 0000 7f00 0001 E..P..@.@.......
0x0010: 7f00 0001 fa56 1f40 07c9 d46f a115 3444 .....V.@...o..4D
0x0020: 8018 18eb fe44 0000 0101 080a 2c70 8fda .....D......,p..
0x0030: 2c70 8fda 474f 5048 4552 0a47 4f50 4845 ,p..GOPHER.GOPHE
0x0040: 520a 474f 5048 4552 0a47 4f50 4845 520a R.GOPHER.GOPHER.

可以看到,有四个"GOPHER"被合并到了一个 TCP 包中。

结论

TCP_NODELAY并不是万能的,有好处有坏处,需要根据实际业务场景决定打开还是关闭。但是,在使用具体语言编写网络服务时,我们需要知道它是否被默认开启。

还有其他一些类似的 socket 选项,比如TCP_QUICKACKTCP_CORK等。但是由于有些 socket 选项是平台相关的,因此 Go 没有提供和TCP_NODELAY相同的方式来控制这些 socket 选项。我们可以通过一些平台相关的包来实现这一点。比如说,在类 unix 系统下,我们可以使用golang.org/x/sys/unix包中的SetsockoptInt方法。

举例:

err = unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 1)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}

最后,如果你想学习更多和 Nagle 算法相关的知识,可以看看这篇英文博客[1]

英文原文链接:Control packet flow with TCP_NODELAY in Go[2]

参考资料

[1]

英文博客: https://www.extrahop.com/company/blog/2016/tcp-nodelay-nagle-quickack-best-practices/

[2]

Control packet flow with TCP_NODELAY in Go: https://blog.gopheracademy.com/advent-2019/control-packetflow-tcp-nodelay/

推荐阅读

  • 架构系列:高并发架构的CDN知识介绍

  • Go语言中如何开启 TCP keepalive?


喜欢本文的朋友,欢迎关注“Go语言中文网”:

7877f69240927a55c2177fa84b23a08e.png

Go语言中文网启用微信学习交流群,欢迎加微信:274768166

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/534671.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react取消所有请求_react 组件关闭后怎么消除还在进行中的ajax

把你的请求做成可以取消的&#xff0c; 这里的取消不是取消发送。 因为请求已经发送了&#xff0c;没有办法终止的。所谓的取消其实就是取消回调函数&#xff0c; react官方给出了一种最佳实践。const makeCancelable (promise) > { let hasCanceled_ false; const wrap…

css 涟漪,CSS3水波涟漪动画定位样式如何制作

CSS3水波涟漪动画定位样式如何制作宝剑锋从磨砺出&#xff0c;梅花香自苦寒来。以下是小编为大家搜索整理的CSS3水波涟漪动画定位样式如何制作&#xff0c;希望能给大家带来帮助!更多精彩内容请及时关注我们应届毕业生考试网!先上效果图:教程本动画需要用到的主要属性:animatio…

python基础实训_python基础实践(三)

-*-列表是新手可直接使用的最强大的python功能之一&#xff0c;它融合了众多重要的编程概念。-*-# -*- coding:utf-8 -*-# Author:sweeping-monkQuestion_1 "什么是列表&#xff1f;"print(Question_1)smg "列表由一系列按特定顺序排列的元素组成。你可以创建…

python axis 0 1_python pandas 中axis值0 1怎么分行还是列

axis的重点在于方向&#xff0c;而不是行和列。1表示横轴&#xff0c;方向从左到右&#xff1b;0表示纵轴&#xff0c;方向从上到下。即axis1为横向&#xff0c;axis0为纵向&#xff0c;而不是行和列&#xff0c;具体到各种用法而言也是如此。当axis1时&#xff0c;如果是求平均…

ajax php接收不到数据库,PHP更新MySQL数据库与AJAX调用没有做任何事情

我已经测试过&#xff0c;发现正确的数据被发送&#xff0c;但PHP更新数据库中的字段即处理更新无法正常工作。发生的一切就是我在条件中得到了else响应。我需要根据用户输入是什么来更新数据库。就像我说的&#xff0c;我得到的回应是else回应。$youruname $_POST[youruname]…

就业技术书文件表格_公路工程全套资料—开工施工检验等表格范本,及监理内业常用资料...

关键词&#xff1a;开工报告、 路基、排水、小桥、涵洞、水泥、沥青、混凝土、施工检验、监理、资料整理、基层&#xff0c;内业资料&#xff0c;监理资料&#xff0c;施工试验报告等。公路工程在管理、监理、施工过程中需及时、准确、完整地收集整理项目建设中各种档案资料&am…

龙神契约为什么显示服务器错误,龙神契约连服BOSS玩法介绍

龙神契约游戏中连服战场的游戏中的一个非常重要的部分&#xff0c;而其中的boss尤为重要。击杀boss玩家可以获得非常多的奖励&#xff0c;一般一下稀有材料都是可以获得的&#xff0c;所以打boss是重中之重的。今天小编简单给大家介绍一下。有兴趣的小伙伴千万不要错过。和小编…

python批量新建文件_python批量处理

python opencv图像二值化批量处理from skimage import data_dir,io,transform,color,filtersimport numpy as npimport cv2def convert_gray(f):rgbio.imread(f) #依次读取rgb图片#grayfilters.gaussian(rgb, sigma1, outputNone, modenearest, cval0, multichannelNone, prese…

dns电脑服务器发生故障怎么修复,电脑dns服务器发生故障怎么解决

一、DNS简介&#xff1a;DNS(Domain Name System&#xff0c;域名系统)&#xff0c;因特网上作为域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使用户更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。通过主机名&#xff0c;最终得到该主机…

button设置disabled属性不生效_jQuery属性节点

发现了很好玩的表情可以插入嘻嘻嘻嘻嘻嘻嘻 1.attr属性操作 在jQuery中&#xff0c;可以通过attr()方法操作属性&#xff0c;可以是固有属性&#xff0c;也可以是自定义属性。1.1 设置属性值// 设置单个属性 $("div").attr("title", "我是一个div&quo…

server_u文件服务器已停止,Serv-U停止服务怎么解决

近期很多朋友在使用Serv-U来架设FTP服务器时候&#xff0c;总会出现自动停止服务的现象&#xff0c;一旦停止就不能保证工作的正常运作&#xff0c;那么Serv-U停止服务怎么解决&#xff0c;下面跟随爱站技术频道小编来看看吧&#xff01;因为这个ftp服务很重要&#xff0c;要保…

如何知道电脑服务器操作系统,电脑如何查看服务器操作系统

电脑如何查看服务器操作系统 内容精选换一换北京时间1月3日&#xff0c;Intel处理器芯片被曝出存在严重的Meltdown和Spectre安全漏洞&#xff0c;漏洞详情如下&#xff1a;漏洞名称&#xff1a;Intel处理器存在严重芯片级漏洞漏洞编号&#xff1a;CVE-2017-5753、CVE-2017-5715…

哈哈机器人送到冰雪小镇_从小镇到上海:两代人的接力洄游 | 活动回顾

10月24日&#xff0c;在上海思南公馆&#xff0c;作家路明围绕着自己的新书《出小镇记》&#xff0c;和苏更生、景蛮蛮一起&#xff0c;向现场观众讲述了小镇和上海的故事。路明&#xff1a;我妈妈是69届的初中生&#xff0c;他们69届那些人走的时候&#xff0c;69届是一片红&a…

云服务器php版本修改,云服务器 更改php版本

云服务器 更改php版本 内容精选换一换本节操作介绍在管理控制台创建启动模板的操作步骤。每个账号在每个区域最多可创建30个启动模板。创建启动模板时&#xff0c;所有配置项均为可选。但如果缺失了创建实例的必要参数&#xff0c;例如规格、镜像类型&#xff0c;那么在使用该模…

opencv获得图片的像素宽度_使用OpenCV实现摄像头测距

原文链接&#xff1a;Find distance from camera to object using Python and OpenCV​www.pyimagesearch.com摄像头测距就是计算照片中的目标物体到相机的距离。可以使用相似三角形&#xff08;triangle similarity&#xff09;方法实现&#xff0c;或者使用更复杂但更准确的相…

ios下js复制到粘贴板_EXCEL被你忽视的粘贴板-11

这一节我们来聊聊粘贴板的用途&#xff0c;什么?你居然没用过粘贴板&#xff1f;平时只是ctrlc&#xff0c;然后ctrlv&#xff0c;好吧&#xff0c;那我们看看粘贴板到底能干些啥。1、提取区域内的内容有时我们选择一块区域发给同事&#xff0c;但当他需要编辑时发现居然是图片…

机器人 林州重机_林州重机募资11亿布局油气和机器人项目

OFweek工控网讯&#xff1a;林州重机7月31日晚间公布非公开发行股票预案&#xff0c;公司计划向包括公司控股股东郭现生、股东、原董事宋全启在内的不超过10名的特定对象非公开发行股票数量为不超过15000万股&#xff0c;发行价格不低于7.42元/股&#xff0c;募集资金总额不超过…

ros构建机器人运动学模型_ROS入门学习之八机器人综合应用

1.ROS机器人实例介绍(PR2,Turtlebot,HRMRP,Kungfu Arm)1).PR2:造就了ROS的机器人平台&#xff0c;完全基于ROS开发&#xff0c;功能丰富、强大2).Turtlebot:ROS社区中最流行的高性价比机器人平台&#xff0c;前后工发布三代3).Universal Robot:工业领域的协作机器人定义者4).HR…

微人事项目实战的数据库脚本_EMP微前端实战之cocos2d线上项目

团队原文&#xff1a;efoxTeam/emp​github.com一.背景目前cocos2d游戏最主要的开发方式是通过官方提供的GUI图形界面工具——creator&#xff0c;通过 creator 开发者无需关注构建本身&#xff0c;只需通过界面操作即可对游戏代码进行构建打包。但是这样也存在着以下几个问题&…

线粒体和叶绿体的基因组特点_如何组装植物叶绿体基因组

可能出现的问题&#xff1a;*个人电脑上遇到不能collect memery的情况&#xff0c;是电脑内存较少&#xff0c;建议分成用2G左右的数据进行组装。* Seed.fasta #用于起始组装的种子序列&#xff0c;NOVOPlasty安装软件目录下有这个文件&#xff0c;就叫这个名字&#xff0c;作者…