go网络编程积累(一)

written on Mon 20 June 2016 by

//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。

This entry was tagged on #web

comments powered by Disqus
 

Tags