import “net”

net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
虽然本包提供了对网络原语的访问,大部分使用者只需要Dial、Listen和Accept函数提供的基本接口;以及相关的Conn和Listener接口。crypto/tls包提供了相同的接口和类似的Dial和Listen函数。
Dial函数和服务端建立连接:

  1. conn, err := net.Dial("tcp", "google.com:80")
  2. if err != nil {
  3. // handle error
  4. }
  5. fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
  6. status, err := bufio.NewReader(conn).ReadString('\n')
  7. // ...

Listen函数创建的服务端:

  1. ln, err := net.Listen("tcp", ":8080")
  2. if err != nil {
  3. // handle error
  4. }
  5. for {
  6. conn, err := ln.Accept()
  7. if err != nil {
  8. // handle error
  9. continue
  10. }
  11. go handleConnection(conn)
  12. }
  1. type Listener interface {
  2. // Addr返回该接口的网络地址
  3. Addr() Addr
  4. // Accept等待并返回下一个连接到该接口的连接
  5. Accept() (c Conn, err error)
  6. // Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误。
  7. Close() error
  8. }
  1. type Conn interface {
  2. // Read从连接中读取数据
  3. // Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
  4. Read(b []byte) (n int, err error)
  5. // Write从连接中写入数据
  6. // Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
  7. Write(b []byte) (n int, err error)
  8. // Close方法关闭该连接
  9. // 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
  10. Close() error
  11. // 返回本地网络地址
  12. LocalAddr() Addr
  13. // 返回远端网络地址
  14. RemoteAddr() Addr
  15. // 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline
  16. // deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
  17. // deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作
  18. // 参数t为零值表示不设置期限
  19. SetDeadline(t time.Time) error
  20. // 设定该连接的读操作deadline,参数t为零值表示不设置期限
  21. SetReadDeadline(t time.Time) error
  22. // 设定该连接的写操作deadline,参数t为零值表示不设置期限
  23. // 即使写入超时,返回值n也可能>0,说明成功写入了部分数据
  24. SetWriteDeadline(t time.Time) error
  25. }
  1. type Addr interface {
  2. Network() string // 网络名
  3. String() string // 字符串格式的地址
  4. }

终端的命令都是落到文件里,然后再读文件(不是再内存里)
tcp编程初识 - 图1

代码体验

tcpdemo/server/server.go

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. fmt.Println("服务器开始监听...")
  8. ln, err := net.Listen("tcp", "0.0.0.0:8888")
  9. if err != nil {
  10. fmt.Println("监听失败")
  11. return
  12. } else {
  13. fmt.Printf("监听成功, ln类型: %T, 值: %v \n", ln, ln)
  14. // ln类型: *net.TCPListener, 值: &{0xc00008ea00 {<nil> 0}}
  15. }
  16. defer ln.Close()
  17. // 等待客户端连接
  18. for {
  19. conn, err := ln.Accept()
  20. if err != nil {
  21. // handle error
  22. fmt.Println("n.Accept() err = ", err)
  23. continue
  24. } else {
  25. fmt.Printf("🔊 n.Accept() suc conn: %v, 连接的客户端ip: %v \n", conn, conn.RemoteAddr().String())
  26. // n.Accept() suc conn: &{{0xc00010ec80}}, 连接的客户端ip: 172.15.37.225:63367
  27. }
  28. // 这里准备一个协程,与客户端交互
  29. go handleConnection(conn)
  30. }
  31. }
  32. func handleConnection(conn net.Conn) {
  33. // 循环接收客户端连接
  34. defer conn.Close()
  35. for {
  36. // 创建切片
  37. buf := make([]byte, 1024)
  38. // 1.等待客户端通过 conn 发送信息
  39. // 2.如果客户端没有 发送信息 ,那么就阻塞
  40. // fmt.Printf("👀 服务端在等待客户端: %v 输入信息 \n", conn.RemoteAddr().String())
  41. n, err := conn.Read(buf)
  42. /* if err == io.EOF {
  43. fmt.Printf("客户端: %v 退出\n", conn.RemoteAddr().String())
  44. return
  45. } */
  46. if err != nil {
  47. fmt.Println("客户端退出: ", err)
  48. return
  49. }
  50. // 3.显示服务端接收到的客户端信息
  51. fmt.Printf("👉 接收到客户端: %v 发来的信息:", conn.RemoteAddr().String())
  52. fmt.Print(string(buf[:n])) // 这里需要对切片截取,只取有效字符,避免出现未知错误
  53. fmt.Println()
  54. }
  55. }
  56. /*
  57. go run server.go
  58. 服务器开始监听...
  59. 监听成功, ln类型: *net.TCPListener, 值: &{0xc00010ea00 {<nil> 0}}
  60. 🔊 n.Accept() suc conn: &{{0xc00010ec80}}, 连接的客户端ip: 172.15.37.225:61240
  61. 👉 接收到客户端: 172.15.37.225:61240 发来的信息:11
  62. 👉 接收到客户端: 172.15.37.225:61240 发来的信息:hello
  63. 🔊 n.Accept() suc conn: &{{0xc00009a000}}, 连接的客户端ip: 172.15.37.225:52461
  64. 👉 接收到客户端: 172.15.37.225:52461 发来的信息:xixi
  65. 👉 接收到客户端: 172.15.37.225:61240 发来的信息:haha
  66. */

tcpdemo/client/client.go
可以多开几个终端测试连接的 goroutine 处理机制

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "net"
  6. "os"
  7. "strings"
  8. )
  9. func main() {
  10. conn, err := net.Dial("tcp", "172.15.37.225:8888")
  11. if err != nil {
  12. // handle error
  13. fmt.Println("client connect err", err)
  14. return
  15. }
  16. fmt.Println("连接成功", conn)
  17. // 客户端发送数据给服务端
  18. reader := bufio.NewReader(os.Stdin) // 从标准输入【终端】拿到信息
  19. for {
  20. line, err := reader.ReadString('\n')
  21. if err != nil {
  22. fmt.Println("文件读取错误", err)
  23. return
  24. }
  25. line = strings.Trim(line, "\r\n")
  26. if line == "exit" {
  27. fmt.Println("客户端退出")
  28. break
  29. }
  30. fmt.Println("line = ", line)
  31. n, err := conn.Write([]byte(line))
  32. if err != nil {
  33. fmt.Println("conn.Write() err = ", err)
  34. return
  35. }
  36. fmt.Printf("客户端发送 %v 个字节 \n", n)
  37. }
  38. }
  39. /*
  40. go run client.go
  41. 连接成功 &{{0xc00008ea00}}
  42. xixi
  43. line = xixi
  44. 客户端发送 4 个字节
  45. */