
在上一篇文章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个部分组成,其中两个是可选的. 每个16位的源端口和目标端口用于标记发送和接收应用程序进程. 因为UDP不需要响应,所以源端口是可选的. 如果未使用源端口,请将其设置为零. 目标端口之后是固定长度的长度字段(以字节为单位),用于指定包括数据部分的UDP数据报的长度,最小长度为8个字节. 标头的其余16位用于一起检查标头和数据. 这部分是可选的,但是此功能通常在实际应用中使用.
由于缺乏可靠性和非面向连接的协议,UDP应用程序通常必须允许一定量的数据包丢失,错误以及复制和粘贴. 但是,如果需要,某些应用程序(例如TFTP)必须在应用程序层添加基本的可靠性机制. 但是,大多数UDP应用程序不需要可靠的机制,甚至可能由于引入可靠的机制而降低性能. 流媒体(流技术),即时多媒体游戏和IP电话(VoIP)必须是典型的UDP应用. 如果应用程序要求高可靠性,则可以使用传输控制协议(TCP协议)代替UDP.
由于缺乏拥塞控制,需要一种基于网络的机制来减少由于失去控制和高速UDP流量负载而导致的拥塞崩溃的影响. 换句话说,由于UDP发送方无法检测到拥塞udp编程服务端和客户端,因此使用包排队和丢弃技术的基本网络设备(例如路由器)通常成为减少过多UDP流量的有效工具. 数据报拥塞控制协议(DCCP)旨在通过增加高速UDP流(例如流媒体)中的主机拥塞控制来减少此潜在问题.

在典型网络上,使用UDP协议的许多关键应用在某种程度上都相似. 这些应用程序包括域名系统(DNS),简单网络管理协议(SNMP),动态主机配置协议(DHCP),路由信息协议(RIP)以及一些视听流媒体服务等.
服务器
在Go语言包中处理UDP套接字和TCP套接字之间的区别在于服务器端以不同的方式处理多个客户端请求数据包. UDP缺少客户端连接请求的接受功能. 除了将TCP替换为UDP以外,其他所有内容几乎相同. UDP的主要功能如下:
123
func 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对象udp编程服务端和客户端,该对象可以读取数据并将数据发送到相应的客户端(指定UDPAddr).

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package mainimport (encoding/binaryflagfmtnetostime)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(Cant 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.

12345
func 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)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package mainimport (encoding/binaryflagfmtnetostime)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(Cant resolve address: , err)os.Exit(1)}conn, err := net.DialUDP(udp, nil, addr)if err != nil {fmt.Println(Cant 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.
您还可以使用公共网络时间服务器进行测试: 运行timeclient.go -host time.nist.gov
参考
如有任何疑问,请联系网站管理员微信
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-289168-1.html
不怕事