Go因为作为非常好用的系统编程语言而知名。开发者很喜欢它在开发后端服务时的简洁,易于开发和高性能。在很多后端服务中一个关键的特性是网络通信。有很多应用级别的协议用于软件之间通过网络进行交互。在它们的底层大都依赖于 TCP 或者 UDP。在本文中, 我们将讨论在Golang中实现UDP和TCP所涉及到的一些代码。让我们开始吧。
在Go语言中支持TCP已经有大量的文章和资源讲解了。显然,因为它作为互联网中广泛使用的HTTP协议的底层协议,使得它变得非常流行。 让我们看看在Go语言中使用TCP的一些实例。
首先,最重要的是Go的net包,它是在Go中任何网络通信的关键。在net包中,包含了 TCPAddr, TCPConn, and TCPListener 这几个用于支持TCP的数据类型。如果你对此感兴趣,可以花一些时间去研究一下它们。在大多数情况下,除非需要访问连接的高级属性,我们并不需要直接使用这些类型。Go的net包支持一些不仅仅包含TCP,还包含其它的面向流的网络协议,比如TCP, unix或者unixpocket。这些接口是 Conn 和 Listener,我们将会简短的了解一下这两个接口。
在Go语言中,UDP的支持并不像TCP那样在很多博客和论坛中有很多指南。对于现代软件来说,UDP是非常重要的协议,有些情况下使用UDP作为我们的网络协议是非常合理的。
Go的net包提供了对UDP相关类型的支持,主要包含 UDPConn 和 UDPAddr。在网络上我找到的大部分例子都是直接使用这些类型,但是,在Go中有更好的方式去开发基于UDP的软件。
与TCP一样,也有很多抽象的接口用于使用UDP进行通信。最重要的是 PacketConn,它提供了对面向数据报的协议如UDP,IP或者Unix数据报的支持。
现在我们开始来看看实际的代码,如果我们使用接口,那么 TCP 和 UDP 版本的实现是一致的,来看看具体代码:
TCP:
//Connect TCP
conn, err := net.Dial("tcp", "host:port")
if err != nil {
return err
}
defer conn.Close()
//simple Read
buffer := make([]byte, 1024)
conn.Read(buffer)
//simple write
conn.Write([]byte("Hello from client"))
net.Dial()
返回 Conn 接口,支持读和写方法。Conn 支持超级受欢迎的 io.Reader
和 io.Writer
接口,在 Go 的很多包中都有实现,例如 bufio
允许缓冲 I/O 读写。
我们注意到net.Dial()有一个“tcp”的字符串参数,它用于告诉Go初始化一个tcp连接。第二个参数是目标地址。
那关于UDP客户端怎么写?猜一下!!
//Connect udp
conn, err := net.Dial("udp", "host:port")
if err != nil {
return err
}
defer conn.Close()
//simple Read
buffer := make([]byte, 1024)
conn.Read(buffer)
//simple write
conn.Write([]byte("Hello from client"))
非常直接对吧?唯一的不同是net.Dial()
函数的第一个参数不同。我们使用“udp”表明我们希望创建一个UDP连接。
TCP和UDP在服务端的实现是不同的。在实现TCP的时候,我们需要使用 Listener 接口 监听和接受TCP连接。让我们看一段代码:
// listen to incoming tcp connections
l, err := net.Listen("tcp", "host:port")
if err != nil {
return err
}
defer l.Close()
// A common pattern is to start a loop to continously accept connections
for {
//accept connections using Listener.Accept()
c, err := l.Accept()
if err!= nil {
return err
}
//It's common to handle accepted connection on different goroutines
go handleConnection(c)
}
代码是非常简单和直接的,只需要对TCP服务端有一些基础的理解:你需要监听来自外部的连接,然后通过在连接上读写数据处理对它进行处理。让我们看一段关于如何读写数据的最简单的代码:
func handleConnection(c net.Conn) {
//some code...
//Simple read from connection
buffer := make([]byte, 1024)
c.Read(buffer)
//simple write to connection
c.Write([]byte("Hello from server"))
}
现在,你注意到了吗:Listener.Accept()
返回的连接是与TCP客户端一样的,都实现了 Conn 接口。对io.Reader和io.Writer的支持使它可以在很多Go的包中得到很好地兼容。
在Go中实现一个UDP服务器与TCP有些不同,它使用 PacketConn 接口取代了Conn接口和listener。让我们看一下如何实现:
// listen to incoming udp packets
pc, err := net.ListenPacket("udp", "host:port")
if err != nil {
log.Fatal(err)
}
defer pc.Close()
//simple read
buffer := make([]byte, 1024)
pc.ReadFrom(buffer)
//simple write
pc.WriteTo([]byte("Hello from client"), addr)
对于UDP服务器来说,我们使用net.ListenPacket()
方法,加上"udp"参数宣称我们准备在服务器的地址上接收UDP交互。我们之后就可以对UDP客户端进行读写了。
与类型Conn不同的是,PackerConn
并不支持io.reader
和io.writer
接口,但是它支持两个特殊的读写方法ReadFrom()
和 WriteTo()
。WriteTo()
方法需要提供一个参数作为发送的数据到哪个地址,ReadFrom()
则返回数据是从哪个地址接收的。
本文应该可以作为在Go中实现TCP和UDP的代码实践,希望你能够学到一些新的东西。
原文: