golang net包基础解析
2017-08-14 20:55
134 查看
这里主要介绍网络包内主要的几个类型。
net包是提供了底层的网络接口的,但是平时我们需要就是两个主要的接口,Listener和Conn。
生成满足Conn接口的类型的函数
通常在网络内,我们使用Dial()这个函数,来建立连接。
他的两个参数,第一个是网络连接类型。
有这么些种类:
在Go中,主要处理了IP网络、TCP网络、UDP网络和UNIX网络。Go中Conn主要是处理在网络层之上的。所以在使用IP网络的时候,后面必须添加IP层上使用的协议名称或协议号。比如
但是这样做之后,必须自己实现里面的IP数据包的内容,比如你协议使用TCP,那么TCP的字段都需要你手动来构造。通常我们直接使用TCP或UDP网络就可以了。
第二个参数是网络地址,在IP网络内,只用给出网络ip地址就可以了,在tcp或udp网络内,需要给出网络端口,但是如果你给出上层网络协议也是可以的,如
Conn是中间的网络接口,实际上是Dial()会判断所属的网络类型,如果是TCP网络,那么它会返回TCPConn,如果是UDP网络,则会返回UDPConn,如果是IP网络,则返回IPConn。
由于获得的是Conn接口变量,net包内没有以Conn作为参数的函数,所以获得Conn之后,只能调用他的接口函数。
其实最终生成满足这些Conn接口的变量类型其实是一个内部包含socket的结构体。它的真是类型其实是conn,但是只是在net包内部使用,对外使用的只有Conn接口类型变量和下面会提到的内嵌它的TCPConn这类类型变量。
这里提一下关于结构体内嵌接口(a struct with embedded an interface),其实含义比较简单,如果你通过传入一个非nil的接口变量来构造这个Struct,那么这个Struct就内嵌了满足这个接口的变量。但是也只能访问他的接口的函数。我们可以通过Struct来重新定义函数来覆盖原来的接口内的函数。比较明显的例子可以看sort.Reverse()。
TCPConn额外实现了一些便利的操作函数与TCP相关的函数。通常情况下,我们使用TCPConn已经满足了所有的TCP的连接需求。
比如ReadFrom()函数,示例如下
这里有个大坑在PacketConn里,PacketConn的ReadFrom(b []byte)函数指的是从Socket接收数据,写入到b字节切片中。刚好跟io.ReaderFrom接口的含义相反。相对应的WriteTo(b []byte, addr *UDPAddr)是把b的数据写入Socket,发送到指定地址。而TCPConn实现的是io.ReaderFrom接口,所以刚好和UDP的同名函数作用相反
这个以后再研究吧,todo
这里提示一下IPConn下,PacketConn实现的ReadFrom()和Conn实现的Read()的区别。Read会将整个IP包的内容读进来,而ReadFrom只会把IP包之上的内容读进来。比如在ICMP数据包。
net包中创建Listener接口的函数有两个
第一个函数可以通过文件描述符进行拷贝新建一个Listener,当然这两个Listener完全不冲突。
第二个函数是创建Listener的函数,net取值只能是
在UDP网络里,WriteTo实现了发送UDP数据包,ReadFrom实现了接受数据包。
至此,传输层的东西都介绍完了。
net包是提供了底层的网络接口的,但是平时我们需要就是两个主要的接口,Listener和Conn。
1. Conn
Conn是一个基本的接口类型,以数据流为向导的网络连接接口。注意他是接口类型,不需要我们来手动构造实现Conn接口。生成满足Conn接口的类型的函数
func Dial(network, address string) (Conn, error) func DialTimeout(network, address string, timeout time.Duration) (Conn, error) func FileConn(f *os.File) (c Conn, err error) func Pipe() (Conn, Conn)
通常在网络内,我们使用Dial()这个函数,来建立连接。
他的两个参数,第一个是网络连接类型。
有这么些种类:
"tcp",
"tcp4" (IPv4-only),
"tcp6" (IPv6-only),
"udp",
"udp4" (IPv4-only),
"udp6" (IPv6-only),
"ip",
"ip4" (IPv4-only),
"ip6" (IPv6-only),
"unix",
"unixgram"and
"unixpacket".
在Go中,主要处理了IP网络、TCP网络、UDP网络和UNIX网络。Go中Conn主要是处理在网络层之上的。所以在使用IP网络的时候,后面必须添加IP层上使用的协议名称或协议号。比如
Dial("ip4:1", "127.0.0.1") Dial("ip6:ospf", "::1")
但是这样做之后,必须自己实现里面的IP数据包的内容,比如你协议使用TCP,那么TCP的字段都需要你手动来构造。通常我们直接使用TCP或UDP网络就可以了。
第二个参数是网络地址,在IP网络内,只用给出网络ip地址就可以了,在tcp或udp网络内,需要给出网络端口,但是如果你给出上层网络协议也是可以的,如
Dial("tcp", "12.34.56.78:80") Dial("tcp", "google.com:http") Dial("tcp", "[2001:db8::1]:http") //注意ipv6,必须使用[]进行包裹 Dial("tcp", "[fe80::1%lo0]:80") //[ipv6-host%zone]:80
Conn是中间的网络接口,实际上是Dial()会判断所属的网络类型,如果是TCP网络,那么它会返回TCPConn,如果是UDP网络,则会返回UDPConn,如果是IP网络,则返回IPConn。
由于获得的是Conn接口变量,net包内没有以Conn作为参数的函数,所以获得Conn之后,只能调用他的接口函数。
conn.Write(data) conn.Read(data)
其实最终生成满足这些Conn接口的变量类型其实是一个内部包含socket的结构体。它的真是类型其实是conn,但是只是在net包内部使用,对外使用的只有Conn接口类型变量和下面会提到的内嵌它的TCPConn这类类型变量。
1.1 TCPConn
TCPConn是在TCP网络上Conn接口类型的一种实现。注意他是实实在在的结构体类型type TCPConn struct { conn //这个conn是内部的一个Conn接口类型的实现 }
这里提一下关于结构体内嵌接口(a struct with embedded an interface),其实含义比较简单,如果你通过传入一个非nil的接口变量来构造这个Struct,那么这个Struct就内嵌了满足这个接口的变量。但是也只能访问他的接口的函数。我们可以通过Struct来重新定义函数来覆盖原来的接口内的函数。比较明显的例子可以看sort.Reverse()。
TCPConn额外实现了一些便利的操作函数与TCP相关的函数。通常情况下,我们使用TCPConn已经满足了所有的TCP的连接需求。
比如ReadFrom()函数,示例如下
msg := bytes.NewBufferString("woqunidaye") //可以创建一个Buffer变量 _, err = tcpConn.ReadFrom(msg) //Buffer变量是实现了io.Reader的接口
1.2 UDPConn
与TCPConn同理,只是对conn的一个封装,并添加了许多额外的函数。1.3 总结
在Conn里,其实扮演了一个连接通道的角色,类似于C语音的里的socket。在net包里,有实现Conn接口的类型有TCPConn、UDPConn、UnixConn、IPConn这4个结构体类型。其实他们都是内部内嵌了一个conn结构体(它其实包含了socket的文件描述符),我们可以直接使用net.Dial()来获得Conn接口变量,也可以使用各自的如net.DialTCP()函数来获得TCPConn类型变量,我们也可以通过对Conn接口变量进行断言,来转化成如TCPConn这些实际的类型。1.4 示例
func dialInTCP() { conn, err := net.Dial("tcp", "127.0.0.1:8080") //开启TCP连接端口 if err != nil { log.Fatal(err) } defer conn.Close() msg := []byte("woqunidaye") if tconn, ok := conn.(*net.TCPConn); ok { //这里可以断言,因为是tcp连接,其实值类型就是TCPConn fmt.Println("assert success") tconn.CloseRead() //尝试调用TCPConn的函数 _, err = tconn.Write(msg) if err != nil { log.Fatal(err) } } else { _, err = conn.Write(msg) if err != nil { log.Fatal(err) } } } func listenTCP() { l, err := net.Listen("tcp", ":8080") //监听TCP,8080端口 if err != nil { log.Fatal(err) } defer l.Close() for { conn, err := l.Accept() //接受连接,当上面Dial的时候,这就会接受连接 if err != nil { log.Fatal(err) } go func(c net.Conn) { //使用goroutine,并发 buf := make([]byte, 256) //接受字段 _, err = c.Read(buf) //读取内容 if err != nil { log.Fatal(err) } fmt.Println(string(buf)) c.Close() }(conn) } }
2. PacketConn
PacketConn是一个接口类型,与Conn的区别在于,Conn是基于流式的网络连接接口,而PacketConn是基于网络包的网络连接接口。由于其基于网络包的特性,所以实现该接口的类型有IPConn和UDPConn类型,即是网络层的连接类型。PacketConn的与Conn的实现函数的区别在,前者实现的是ReadFrom和
WriteTo函数,而后者实现的是
Read和
Write函数。
这里有个大坑在PacketConn里,PacketConn的ReadFrom(b []byte)函数指的是从Socket接收数据,写入到b字节切片中。刚好跟io.ReaderFrom接口的含义相反。相对应的WriteTo(b []byte, addr *UDPAddr)是把b的数据写入Socket,发送到指定地址。而TCPConn实现的是io.ReaderFrom接口,所以刚好和UDP的同名函数作用相反
这个以后再研究吧,todo
这里提示一下IPConn下,PacketConn实现的ReadFrom()和Conn实现的Read()的区别。Read会将整个IP包的内容读进来,而ReadFrom只会把IP包之上的内容读进来。比如在ICMP数据包。
3. Listener
Listener是流式协议网络的监听接口。它也是通过给定的函数来进行创建返回获得。满足Listener接口的类型有TCPListener、UnixListener。net包中创建Listener接口的函数有两个
func FileListener(f *os.File) (ln Listener, err error) //这个函数用来拷贝Listener用的 func Listen(net, laddr string) (Listener, error) //创建Listener的函数
第一个函数可以通过文件描述符进行拷贝新建一个Listener,当然这两个Listener完全不冲突。
第二个函数是创建Listener的函数,net取值只能是
tcp,
tcp4,
tcp6,
unix,
unixpacket。注意啊,没有UDP啊。laddr取值规范和上面
Dial()一样。
3.1 TCPListener
TCPListener的监听对象类型,可以通过ListenTCP()函数来创建该类型变量,但是也可以通过Listener接口变量断言转化。
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)
3.2 UnixListener
3.3 UDP的listener
在go的net包设计里,没有UDPListener类型。因为UDP网络连接不需要建立连接,也就是没有Accept()步骤。我们知道UDPConn也是实现了PacketConn接口,也就是实现了WriteTo和ReadFrom接口。
在UDP网络里,WriteTo实现了发送UDP数据包,ReadFrom实现了接受数据包。
func listenUDP() { addr, err := net.ResolveUDPAddr("udp4", ":8081") if err != nil { log.Fatal(err) return } l, err := net.ListenUDP("udp4", addr) if err != nil { log.Fatal(err) return } defer l.Close() buf := make([]byte, 256) len, _, err := l.ReadFrom(buf) if err != nil { log.Fatal(err) } buf = buf[:len] fmt.Println(string(buf)) } func dialUDP() { remoteAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:8081") if err != nil { log.Fatal(err) return } conn, err := net.DialUDP("udp4", nil, remoteAddr) if err != nil { log.Fatal(err) return } msg := []byte("UDP-woqunidaye") _, err = conn.Write(msg) if err != nil { log.Fatal(err) return } }
至此,传输层的东西都介绍完了。
相关文章推荐
- 基础的对象初始化解析
- mybatis入门基础(三)----SqlMapConfig.xml全局配置文件解析------------转载
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
- golang基础知识之encoding/json package
- java基础18 String字符串和Object类(以及“equals” 和 “==”的解析)
- hadoop基础----hadoop实战(五)-----myeclipse开发MapReduce---WordCount例子---解析MapReduce的写法
- java基础解析系列(十一)---equals、==和hashcode方法
- [C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托
- Android多线程基础 解析Handler机制
- Elasticsearch技术解析与实战(一)基础概念及环境搭建
- 黑马程序员--Java基础加强--14.利用反射操作泛型III【解析关于泛型类型的细节信息的获取方法】【Method与泛型相关的方法】【个人总结】
- [WebKit] JavaScriptCore解析--基础篇(二)解释器基础与JSC核心组件
- [置顶] Spark基础全解析
- 编程基础-面向对象-基于模板的面向对象解析
- 从DNS基础到在CentOS6.5上“玩着”搭建一个支持正向、反向解析的“
- ios之UIGestureRecognizer手势基础使用解析
- [GO]Golang 解析xml 生成json 传到前台
- Java内存模型深度解析:基础部分
- golang基础数据结构
- java基础----解析json文件