udp的服务端有一个大坑,即如果收包不及时,在系统缓冲写满后,将大量丢包。

在网上通常的示例中,一般在for循环中执行操作逻辑。这在生产环境将是一个隐患。是的,俺就翻车了。

go强大简易的并发能力可以用在处理udp数据上。

	PoolSizeUDP := 1472
listener, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.ParseIP(listenIP),
Port: port,
})
if err != nil {
logrus.Fatalf("RunUdpServer failed to listen: %v", err)
return nil
}
// 如果还不行,考虑把系统的buffer设大一点
// listener.SetReadBuffer(1024 * 1024 * 8)
// listener.SetWriteBuffer(1024 * 1024 * 8)
var data = make([]byte, PoolSizeUDP)
chLimit := make(chan int, 64) // 最多创建64个协程,避免内存爆炸
for {
select {
case <-ctx.Done():
return nil
default:
}
n, addr, err := listener.ReadFromUDP(data)
if err != nil {
logrus.Errorf("RunUdpServer ReadFromUDP err: %v", err)
continue
}
raw := make([]byte, n) // 重点注意,每次循环都必须创建新的raw变量,否则踩内存
copy(raw, data[:n])
chLimit <- 1
go func(udpMsg []byte) {
// 拿 udpMsg 做点什么
defer func() {
<-chLimit
}()
DoSth(udpMsg)
}(raw)
}

注意点:

  1. data可以在循环外创建,复用即可。每次ReadFromUDP并不会受到上次数据残留的影响。
  2. 不要在for中执行重逻辑,避免等待太久时间udp大量丢包。所以每次收到udpMsg,都交给go协程来处理。
  3. raw必须每次在循环内创建,否则在后面的go并发会踩内存。
  4. SetReadBuffer这个配置很有用

更新:上面的示例为了避免在后续的go中,有不可控的异步操作引用了数据导致踩内存,每次收消息都分配了新的[]byte。

raw := make([]byte, n) // 重点注意,每次循环都必须创建新的raw变量,否则踩内存
copy(raw, data[:n])

经大佬提醒,这其实是一个不小的开销。如果go中执行的行为可控,引入sync.Pool可以很方便的做内存复用。

var udpBytesPool = sync.Pool{
New: func() any {
return make([]byte, PoolSizeUDP)
},
}
......
for {
data := udpBytesPool.Get().([]byte)
n, addr, err := listener.ReadFromUDP(data)
if err != nil {
logrus.Errorf("RunUdpServer ReadFromUDP err: %v", err)
continue
}
go func(){
defer udpBytesPool.Put(data) // 注意,在协程退出执行这个操作时,一定确认 data不会再被引用了
// do sth
}()
}

go: 如何编写一个正确的udp服务端的更多相关文章

  1. 编写一个简单的TCP服务端和客户端

    下面的实验环境是linux系统. 效果如下: 1.启动服务端程序,监听在6666端口上  2.启动客户端,与服务端建立TCP连接  3.建立完TCP连接,在客户端上向服务端发送消息 4.断开连接 实现 ...

  2. [C语言]一个很实用的服务端和客户端进行TCP通信的实例

    本文给出一个很实用的服务端和客户端进行TCP通信的小例子.具体实现上非常简单,只是平时编写类似程序,具体步骤经常忘记,还要总是查,暂且将其记下来,方便以后参考. (1)客户端程序,编写一个文件clie ...

  3. socket创建UDP服务端和客户端

    UDP服务端代码示例: from socket import * #1.创建数据报套接字 sockfd = socket(AF_INET, SOCK_DGRAM) #2.绑定服务端地 sockfd.b ...

  4. python创建udp服务端和客户端

    1.udp服务端server from socket import * from time import ctime HOST = '' PORT = 8888 BUFSIZ = 1024 ADDR ...

  5. Node.js是一个事件驱动I/O服务端JavaScript环境

    Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎.目的是为了提供撰写可扩充网络程序,如Web服务.第一个版本由Ryan Dahl于2009年发布,后来,Jo ...

  6. [C语言]一个很实用的服务端和客户端进行UDP通信的实例

    前段时间发了个TCP通信的例子,现在再来一个UDP通信的例子.这些可以作为样本程序,用到开发中.“裸写”socket老是记不住步骤,经常被鄙视…… 下面的例子很简单,写一个UDP的server用于收包 ...

  7. udp服务端收发数据流程

    1.创建服务端的socket以便开始通讯.2.绑定ip以及端口号,这样客户端才能找到这个程序.3.因为本地网卡不止一个所以尽量不写死,一般用""空来表示所有本地网卡.4.接下来开始 ...

  8. 简单的UDP服务端和客户端示例

    UDP的理论不再多说,我这里直接给出一个关于UDP的HelloWorld程序,代码明了,希望对刚入门的学生有所帮助! 当然,实际上,在这块我也刚入门! 首先写服务端代码,服务端邦定本地的IP和端口来监 ...

  9. socket手写一个简单的web服务端

    直接进入正题吧,下面的代码都是我在pycharm中写好,再粘贴上来的 import socket server = socket.socket() server.bind(('127.0.0.1', ...

随机推荐

  1. TypeScript学习_入门向

    TypeScript学习_入门向 1-TypeScript简介 首先官网祭天 ---> https://www.tslang.cn/ TypeScript 是 JavaScript 的一个超集, ...

  2. 论文解读(SelfGNN)《Self-supervised Graph Neural Networks without explicit negative sampling》

    论文信息 论文标题:Self-supervised Graph Neural Networks without explicit negative sampling论文作者:Zekarias T. K ...

  3. python基础练习题(题目 输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数)

    day10 --------------------------------------------------------------- 实例017:字符串构成 题目 输入一行字符,分别统计出其中英 ...

  4. struts2绕过waf读写文件及另类方式执行命令

    之前碰到过好几次Struts2,还都是016,项目.众测都遇到过,每次都只是证明了一下存在,由于waf的存在,没有深入去利用,这里简单的记录下. 0x01 背景 xray或者Struts2漏扫可以扫到 ...

  5. 使用钡铼BL102网关连接西门子S7-1200PLC 以及mosquitto服务器方法

    一.软硬件描述 西门子PLC S7-1215 钡铼BL102网关 mosquitto MQTT服务器(腾讯云上搭建) 可以上网的路由器一套 二.需要使用的软件. 西门子Portal v15.1 (西门 ...

  6. k8s入门之集群搭建(二)

    一.准备三台节点 从上篇文章 k8s入门之基础环境准备(一)安装的Ubuntu虚拟机克隆出三台虚拟机,如图所示 启动这三台虚拟机节点,分别做如下配置 虚拟机名称 IP HostName k8sMast ...

  7. 异步请求与中断 ( XHR,Axios,Fetch对比 )

    随着AJAX技术的诞生,前端正式进入了局部刷新和前后端分离的新时代,最初的服务请求技术是XHR,随着技术发展和ES6的诞生,jquery ajax,axios,fetch 等技术的产生让前端的异步请求 ...

  8. IDEA新建项目时的默认配置与模版配置

    今天一大早,群里(点击加群)有小伙伴问了这样的一个问题: 在我们使用IDEA开发项目的时候,通常都会有很多配置项需要去设置,比如对于Java项目来说,一般就包含:JDK配置.Maven配置等.那么如果 ...

  9. 离谱的 CSS!从表盘刻度到艺术剪纸

    某日,群里有这样一个问题,如何实现这样的表盘刻度: 这其实是个挺有意思的问题,方法也有很多. 单标签,使用 conic-gradient 实现表盘刻度 最简单便捷的方式,就是利用角向渐变的方式 con ...

  10. 哈工大软件构造Lab1(2022)

    目录 一.实验目标概述 二.实验环境配置 1.安装编写java程序的IDE--IntelliJ IDEA 2.安装Git 3.安装Junit 4.GitHub Lab1仓库的URL地址 三.实验过程 ...