The types Conn, PacketConn and Listener

So far we have differentiated between the API for TCP and the API for UDP, using for example DialTCP and DialUDP returning a TCPConn and UDPConn respectively. The type Conn is an interface and both TCPConn and UDPConn implement this interface. To a large extent you can deal with this interface rather than the two types.

Instead of separate dial functions for TCP and UDP, you can use a single function

  1. func Dial(net, laddr, raddr string) (c Conn, err os.Error)

The net can be any of "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" (IPv4-only) and "ip6" IPv6-only). It will return an appropriate implementation of the Conn interface. Note that this function takes a string rather than address as raddr argument, so that programs using this can avoid working out the address type first.

Using this function makes minor changes to programs. For example, the earlier program to get HEAD information from a Web page can be re-written as

  1. /* IPGetHeadInfo
  2. */
  3. package main
  4. import (
  5. "bytes"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net"
  10. "os"
  11. )
  12. func main() {
  13. if len(os.Args) != 2 {
  14. fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
  15. os.Exit(1)
  16. }
  17. service := os.Args[1]
  18. conn, err := net.Dial("tcp", service)
  19. checkError(err)
  20. defer conn.Close()
  21. _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
  22. checkError(err)
  23. result, err := ioutil.ReadAll(conn)
  24. checkError(err)
  25. fmt.Println(string(result))
  26. os.Exit(0)
  27. }
  28. func checkError(err error) {
  29. if err != nil {
  30. fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
  31. os.Exit(1)
  32. }
  33. }

Writing a server can be similarly simplified using the function

  1. func Listen(net, laddr string) (l Listener, err os.Error)

which returns an object implementing the Listener interface. This interface has a method

  1. func (l Listener) Accept() (c Conn, err os.Error)

which will allow a server to be built. Using this, the multi-threaded Echo server given earlier becomes

  1. /* ThreadedIPEchoServer
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "net"
  7. "os"
  8. )
  9. func main() {
  10. service := ":1200"
  11. listener, err := net.Listen("tcp", service)
  12. checkError(err)
  13. for {
  14. conn, err := listener.Accept()
  15. if err != nil {
  16. continue
  17. }
  18. go handleClient(conn)
  19. }
  20. }
  21. func handleClient(conn net.Conn) {
  22. defer conn.Close()
  23. var buf [512]byte
  24. for {
  25. n, err := conn.Read(buf[0:])
  26. if err != nil {
  27. return
  28. }
  29. _, err2 := conn.Write(buf[0:n])
  30. if err2 != nil {
  31. return
  32. }
  33. }
  34. }
  35. func checkError(err error) {
  36. if err != nil {
  37. fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
  38. os.Exit(1)
  39. }
  40. }

If you want to write a UDP server, then there is an interface PacketConn and a method to return an implementation of this:

  1. func ListenPacket(net, laddr string) (c PacketConn, err os.Error)

This interface has primary methods ReadFrom and WriteTo to handle packet reads and writes.

The Go net package recommends using these interface types rather than the concrete ones. But by using them, you lose specific methods such as SetKeepAlive or TCPConn and SetReadBuffer of UDPConn, unless you do a type cast. It is your choice.