go网络编程积累(一)
written on Mon 20 June 2016 by importcjj
//server.go
package main
import (
"fmt"
"net"
"os"
"io"
)
func main() {
ln, err := net.Listen("tcp", ":8888")
if err != nil {
fmt.Println("Can't listen")
os.Exit(2)
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println("Can't accept a conn")
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
buf := make([]byte, 30 * 1024)
for {
n, err := conn.Read(buf)
if n > 0 {
fmt.Println(buf[:n])
}
if err == io.EOF {
fmt.Println("Conn was closed.")
return
}
if err != nil {
fmt.Println(err)
return
}
}
}
//client.go
package main
import (
"net"
"fmt"
"os"
"time"
)
func main() {
conn, err := net.Dial("tcp", ":8888")
if err != nil {
fmt.Println(err)
os.Exit(2)
}
defer conn.Close()
buf := []byte("Hello, my server")
for {
n, err := conn.Write(buf)
if n > 0 {
fmt.Printf("Write %s to conn", buf[:n])
}
if err != nil {
fmt.Println(err)
os.Exit(2)
}
time.Sleep(1 * time.Second)
}
}
最近在看unix网络编程这本书,顺便结合go来试试手。在写完这个简单的客户端服务器程序后,发现了几个以前一直没注意的问题。
发现一: Reader.Read
func (*Reader) Read
func (b *Reader) Read(p []byte) (n int, err error)
Read reads data into p. It returns the number of bytes
read into p. It calls Read at most once on the underlying
Reader, hence n may be less than len(p). At EOF, the count
will be zero and err will be io.EOF.
将数据读取到提供的byte切片p中,读取的数据量与p的长度有关。这一点以前一直没有注意到,以致于程序一直有问题。以前我一直这样使用:
var buf []byte
n, err := conn.Read(buf)
if n > 0 {
fmt.Println(buf)
}
由于buf在声明之后没有分配内存,所以一直是nil,因此len(buf)当然也是0,所以在Read数据的时候,返回的n一直都是0,这导致了无法正常显示客户端发送的数据。正确的用法是在Read之前先使用make为buf分配一定长度的空间:
var buf = make([]byte, 30 * 1024)
n, err := conn.Read(buf)
if n > 0 {
fmt.Println(buf[:n])
}
上述例子中,首先为buf分配了30*1024字节的内存空间。这样就能正常读取连接中的数据了。n总是小于等于len(buf)。当EOF时,Read返回0和一个io.EOF错误,表示无法读取更多的数据。另外,如果返回了io.EOF以外的错误,则表示还没读取完全之前就遇到了某个错误。
发现二:判断是否实现了特定接口
在go中,无需显式声明自己实现了某个接口。如果类型实现了某个接口定义的全部方法,则该类型实现了该接口。在某些特定的时候,我们需要在代码逻辑中判断接口的实现。如果实现了某接口,还需要调用该接口中的方法。
type Reader interface {
Read(p []byte) (n int, err error)
}
func main() {
conn , err := net.Dial("tcp", ":8080")
if rd, ok := conn.(Reader); ok {
buf := make([]byte, 30 * 1024)
n , err := rd.Read(buf)
}
}
发现三:defer应该在error判断之后
使用defer的时候经常会写出以下代码
func main() {
conn, err := net.Dial("tcp", ":8080")
defer conn.Close()
if err != nil {
fmt.Println(err)
}
}
这样写保证了函数main函数执行退出时必定能关闭tcp连接。但是在没有建立正常连接时,conn为nil,并没有实现Closer接口,也就没有Close方法可供调用,这样的代码就会引发panic。