Go socket编程实践: UDP服务器和客户端实现 - Go语言中文网 - Golang中文社区


本站和网页 https://studygolang.com/articles/9307 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

Go socket编程实践: UDP服务器和客户端实现 - Go语言中文网 - Golang中文社区
<meta name="author" content="polaris ">
主题
文章
项目
资源
图书
Go网址导航
下载
官方文档
英文文档
中文文档
标准库中文版
Go指南
注册登录
分享
首页
文章
Go socket编程实践: UDP服务器和客户端实现
smallnest
· · 7290 次点击 ·
开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
第一次,站长亲自招 Gopher 了>>>
在上一篇文章Go socket编程实践: TCP服务器和客户端实现, 我们实现了一个ECHO服务器, 并且也实现了一个客户端来访问服务器。这篇文章讲解如何实现一个UDP服务器和客户端。这次我们使用的协议是 RFC 868,此协议提供了一个独立于站点的,机器可读的日期和时间信息。时间服务返回的提供了一个32位的数字,是从1900年1月1日午夜到现在的秒数。RFC 868定义时间协议使用端口37, TCP和UDP协议都可以。
另外还有两个关于时间/日期的RFC协议。NTP (RFC 1305)是网络时间协议,提供了精确的时间同步。daytime (RFC 867)在TCP端口13侦听,返回ACSII格式的日期和时间。
TCP/IP模型中,UDP为网络层以上和应用层以下提供了一个简单的接口。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)。
UDP首部字段由4个部分组成,其中两个是可选的。各16bit的来源端口和目的端口用来标记发送和接受的应用进程。因为UDP不需要应答,所以来源端口是可选的,如果来源端口不用,那么置为零。在目的端口后面是长度固定的以字节为单位的长度域,用来指定UDP数据报包括数据部分的长度,长度最小值为8byte。首部剩下地16bit是用来对首部和数据部分一起做校验和(Checksum)的,这部分是可选的,但在实际应用中一般都使用这一功能。
由于缺乏可靠性且属于非连接导向协定,UDP应用一般必须允许一定量的丢包、出错和复制贴上。但有些应用,比如TFTP,如果需要则必须在应用层增加根本的可靠机制。但是绝大多数UDP应用都不需要可靠机制,甚至可能因为引入可靠机制而降低性能。流媒体(串流技术)、即时多媒体游戏和IP电话(VoIP)一定就是典型的UDP应用。如果某个应用需要很高的可靠性,那么可以用传输控制协议(TCP协议)来代替UDP。
由于缺乏拥塞控制(congestion control),需要基于网络的机制来减少因失控和高速UDP流量负荷而导致的拥塞崩溃效应。换句话说,因为UDP发送者不能够检测拥塞,所以像使用包队列和丢弃技术的路由器这样的网络基本设备往往就成为降低UDP过大通信量的有效工具。数据报拥塞控制协议(DCCP)设计成通过在诸如流媒体类型的高速率UDP流中,增加主机拥塞控制,来减小这个潜在的问题。
典型网络上的众多使用UDP协议的关键应用一定程度上是相似的。这些应用包括域名系统(DNS)、简单网络管理协议(SNMP)、动态主机配置协议(DHCP)、路由信息协议(RIP)和某些影音串流服务等等。、
服务器
Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端处理多个客户端请求数据包的方式不同,UDP缺少了对客户端连接请求的Accept函数。其他基本几乎一模一样,只有TCP换成了UDP而已。UDP的几个主要函数如下所示:
123func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Errorfunc (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error)
net.ListenUDP返回UDPConn对象, 可以读取数据,以及往相应的client发送数据 (指定UDPAddr)。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package mainimport ("encoding/binary""flag""fmt""net""os""time")var host = flag.String("host", "", "host")var port = flag.String("port", "37", "port")func main() {flag.Parse()addr, err := net.ResolveUDPAddr("udp", *host+":"+*port)if err != nil {fmt.Println("Can't resolve address: ", err)os.Exit(1)}conn, err := net.ListenUDP("udp", addr)if err != nil {fmt.Println("Error listening:", err)os.Exit(1)}defer conn.Close()for {handleClient(conn)}}func handleClient(conn *net.UDPConn) {data := make([]byte, 1024)n, remoteAddr, err := conn.ReadFromUDP(data)if err != nil {fmt.Println("failed to read UDP msg because of ", err.Error())return}daytime := time.Now().Unix()fmt.Println(n, remoteAddr)b := make([]byte, 4)binary.BigEndian.PutUint32(b, uint32(daytime))conn.WriteToUDP(b, remoteAddr)}
时间数是64位的,需要将其转换成一个32位的字节。
执行 go run timeserver.go启动服务器
客户端
客户端代码和TCP类似。
12345func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)func (c *UDPConn) Read(b []byte) (int, error)func (c *UDPConn) Write(b []byte) (int, error)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package mainimport ("encoding/binary""flag""fmt""net""os""time")var host = flag.String("host", "localhost", "host")var port = flag.String("port", "37", "port")//go run timeclient.go -host time.nist.govfunc main() {flag.Parse()addr, err := net.ResolveUDPAddr("udp", *host+":"+*port)if err != nil {fmt.Println("Can't resolve address: ", err)os.Exit(1)}conn, err := net.DialUDP("udp", nil, addr)if err != nil {fmt.Println("Can't dial: ", err)os.Exit(1)}defer conn.Close()_, err = conn.Write([]byte(""))if err != nil {fmt.Println("failed:", err)os.Exit(1)}data := make([]byte, 4)_, err = conn.Read(data)if err != nil {fmt.Println("failed to read UDP msg because of ", err)os.Exit(1)}t := binary.BigEndian.Uint32(data)fmt.Println(time.Unix(int64(t), 0).String())os.Exit(0)}
执行go run timeclient.go测试UDP。也可以用公网的时间服务器测试: go run timeclient.go -host time.nist.gov
参考
https://tools.ietf.org/html/rfc868
有疑问加站长微信联系(非本文作者)
本文来自:鸟窝
感谢作者:smallnest
查看原文:Go socket编程实践: UDP服务器和客户端实现
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
7290 次点击
加入收藏
微博
收入我的专栏
上一篇:Go socket编程实践: TCP服务器和客户端实现
下一篇:Go的50度灰:Golang新开发者要注意的陷阱和常见错误
net
流媒体
信息
函数
0 回复
暂无回复
添加一条新回复
(您需要 登录 后才能回复 没有账号 ?)
编辑
预览
请尽量让自己的回复能够对别人有帮助
支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
图片支持拖拽、截图粘贴等方式上传
提交
用户登录
记住登录状态
没有账号?注册
忘记密码?
GitHub 登录
Gitea 登录
Go今日面试题
今日阅读排行
一周阅读排行
关注我
扫码关注领全套学习资料
加入 QQ 群:
192706294(已满)
731990104(已满)
798786647(已满)
729884609(已满)
977810755(已满)
815126783(已满)
812540095(已满)
1006366459(已满)
692541889
加入微信群:liuxiaoyan-s,备注入群
也欢迎加入知识星球 Go粉丝们(免费)
给该专栏投稿
写篇新文章
每篇文章有总共有 5 次投稿机会
收入到我管理的专栏
新建专栏
在上一篇文章Go socket编程实践: TCP服务器和客户端实现, 我们实现了一个ECHO服务器, 并且也实现了一个客户端来访问服务器。这篇文章讲解如何实现一个UDP服务器和客户端。这次我们使用的协议是 RFC 868,此协议提供了一个独立于站点的,机器可读的日期和时间信息。时间服务返回的提供了一个32位的数字,是从1900年1月1日午夜到现在的秒数。RFC 868定义时间协议使用端口37, TCP和UDP协议都可以。
另外还有两个关于时间/日期的RFC协议。NTP (RFC 1305)是网络时间协议,提供了精确的时间同步。daytime (RFC 867)在TCP端口13侦听,返回ACSII格式的日期和时间。
TCP/IP模型中,UDP为网络层以上和应用层以下提供了一个简单的接口。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)。
UDP首部字段由4个部分组成,其中两个是可选的。各16bit的来源端口和目的端口用来标记发送和接受的应用进程。因为UDP不需要应答,所以来源端口是可选的,如果来源端口不用,那么置为零。在目的端口后面是长度固定的以字节为单位的长度域,用来指定UDP数据报包括数据部分的长度,长度最小值为8byte。首部剩下地16bit是用来对首部和数据部分一起做校验和(Checksum)的,这部分是可选的,但在实际应用中一般都使用这一功能。
由于缺乏可靠性且属于非连接导向协定,UDP应用一般必须允许一定量的丢包、出错和复制贴上。但有些应用,比如TFTP,如果需要则必须在应用层增加根本的可靠机制。但是绝大多数UDP应用都不需要可靠机制,甚至可能因为引入可靠机制而降低性能。流媒体(串流技术)、即时多媒体游戏和IP电话(VoIP)一定就是典型的UDP应用。如果某个应用需要很高的可靠性,那么可以用传输控制协议(TCP协议)来代替UDP。
由于缺乏拥塞控制(congestion control),需要基于网络的机制来减少因失控和高速UDP流量负荷而导致的拥塞崩溃效应。换句话说,因为UDP发送者不能够检测拥塞,所以像使用包队列和丢弃技术的路由器这样的网络基本设备往往就成为降低UDP过大通信量的有效工具。数据报拥塞控制协议(DCCP)设计成通过在诸如流媒体类型的高速率UDP流中,增加主机拥塞控制,来减小这个潜在的问题。
典型网络上的众多使用UDP协议的关键应用一定程度上是相似的。这些应用包括域名系统(DNS)、简单网络管理协议(SNMP)、动态主机配置协议(DHCP)、路由信息协议(RIP)和某些影音串流服务等等。、
服务器
Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端处理多个客户端请求数据包的方式不同,UDP缺少了对客户端连接请求的Accept函数。其他基本几乎一模一样,只有TCP换成了UDP而已。UDP的几个主要函数如下所示:
123func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Errorfunc (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error)
net.ListenUDP返回UDPConn对象, 可以读取数据,以及往相应的client发送数据 (指定UDPAddr)。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package mainimport ("encoding/binary""flag""fmt""net""os""time")var host = flag.String("host", "", "host")var port = flag.String("port", "37", "port")func main() {flag.Parse()addr, err := net.ResolveUDPAddr("udp", *host+":"+*port)if err != nil {fmt.Println("Can't resolve address: ", err)os.Exit(1)}conn, err := net.ListenUDP("udp", addr)if err != nil {fmt.Println("Error listening:", err)os.Exit(1)}defer conn.Close()for {handleClient(conn)}}func handleClient(conn *net.UDPConn) {data := make([]byte, 1024)n, remoteAddr, err := conn.ReadFromUDP(data)if err != nil {fmt.Println("failed to read UDP msg because of ", err.Error())return}daytime := time.Now().Unix()fmt.Println(n, remoteAddr)b := make([]byte, 4)binary.BigEndian.PutUint32(b, uint32(daytime))conn.WriteToUDP(b, remoteAddr)}
时间数是64位的,需要将其转换成一个32位的字节。
执行 go run timeserver.go启动服务器
客户端
客户端代码和TCP类似。
12345func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)func (c *UDPConn) Read(b []byte) (int, error)func (c *UDPConn) Write(b []byte) (int, error)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package mainimport ("encoding/binary""flag""fmt""net""os""time")var host = flag.String("host", "localhost", "host")var port = flag.String("port", "37", "port")//go run timeclient.go -host time.nist.govfunc main() {flag.Parse()addr, err := net.ResolveUDPAddr("udp", *host+":"+*port)if err != nil {fmt.Println("Can't resolve address: ", err)os.Exit(1)}conn, err := net.DialUDP("udp", nil, addr)if err != nil {fmt.Println("Can't dial: ", err)os.Exit(1)}defer conn.Close()_, err = conn.Write([]byte(""))if err != nil {fmt.Println("failed:", err)os.Exit(1)}data := make([]byte, 4)_, err = conn.Read(data)if err != nil {fmt.Println("failed to read UDP msg because of ", err)os.Exit(1)}t := binary.BigEndian.Uint32(data)fmt.Println(time.Unix(int64(t), 0).String())os.Exit(0)}
执行go run timeclient.go测试UDP。也可以用公网的时间服务器测试: go run timeclient.go -host time.nist.gov
参考
https://tools.ietf.org/html/rfc868
关于 •
FAQ •
贡献者 •
晨读 •
Github •
新浪微博 •
Play •
免责声明 •
联系我们 •
捐赠 •
酷站 •
Feed订阅 •
243586 人在线
最高记录 5390
2013-2022 studygolang.com Go语言中文网,中国 Golang 社区,致力于构建完善的 Golang 中文社区,Go语言爱好者的学习家园。
Powered by StudyGolang(Golang + MySQL) • · CDN 采用 七牛云
VERSION: V4.0.0 · 7.893423ms · 为了更好的体验,本站推荐使用 Chrome 或 Firefox 浏览器
京ICP备14030343号-1
登录和大家一起探讨吧
用户名
密码
记住登录状态
登录
GitHub 登录
忘记密码?
还不是会员现在注册