• 基本介绍
    • 写入操作
    • 读取操作
    • 超时处理
    • 使用示例1,简单使用
    • 使用示例2,回显服务
    • 使用示例3,HTTP客户端

    gtcp模块提供了简便易用的gtcp.Conn链接操作对象。

    使用方式

    1. import "github.com/gogf/gf/g/net/gtcp"

    接口文档

    https://godoc.org/github.com/gogf/gf/g/net/gtcp

    1. type Conn
    2. func NewConn(addr string, timeout ...int) (*Conn, error)
    3. func NewConnByNetConn(conn net.Conn) *Conn
    4. func NewConnKeyCrt(addr, crtFile, keyFile string) (*Conn, error)
    5. func NewConnTLS(addr string, tlsConfig *tls.Config) (*Conn, error)
    6. func (c *Conn) Close() error
    7. func (c *Conn) LocalAddr() net.Addr
    8. func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error)
    9. func (c *Conn) RecvLine(retry ...Retry) ([]byte, error)
    10. func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) ([]byte, error)
    11. func (c *Conn) RemoteAddr() net.Addr
    12. func (c *Conn) Send(data []byte, retry ...Retry) error
    13. func (c *Conn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error)
    14. func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error)
    15. func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) error
    16. func (c *Conn) SetDeadline(t time.Time) error
    17. func (c *Conn) SetRecvBufferWait(bufferWaitDuration time.Duration)
    18. func (c *Conn) SetRecvDeadline(t time.Time) error
    19. func (c *Conn) SetSendDeadline(t time.Time) error

    基本介绍

    写入操作

    TCP通信写入操作由Send方法实现,并提供了错误重试的机制,由第二个非必需参数retry提供。需要注意的是Send方法不带任何的缓冲机制,也就是说每调用一次Send方法将会立即调用底层的TCP Write方法写入数据(缓冲机制依靠系统底层实现)。因此,如果想要进行输出缓冲控制,请在业务层进行处理。

    在进行TCP写入时,可靠的通信场景下往往是一写一读,即Send成功之后接着便开始Recv获取目标的反馈结果。因此gtcp.Conn也提供了方便的SendRecv方法。

    读取操作

    TCP通信读取操作由Recv方法实现,同时也提供了错误重试的机制,由第二个非必需参数retry提供。Recv方法提供了内置的读取缓冲控制,读取数据时可以指定读取的长度(由length参数指定),当读取到指定长度的数据后将会立即返回。如果length < 0那么将会读取所有可读取的缓冲区数据并返回。当length = 0时表示获取一次缓冲区的数据后立即返回。

    如果使用Recv(-1)可以读取所有缓冲区可读数据(长度不定,如果发送的数据包太长有可能会被截断),但需要注意包的解析问题,容易产生非完整包的情况。这个时候,业务层需要根据既定的数据包结构自己负责包的完整性处理。推荐使用后续介绍的简单协议通过SendPkg/RecvPkg来实现消息包的发送/接收,具体请查看后续章节。

    超时处理

    gtcp.Conn对TCP通信时的数据写入和读取提供了超时处理,通过方法中的timeout参数指定,这块比较简单,不过多阐述。


    我们接下来通过通过几个例子来看看如何使用gtcp.Conn对象。

    使用示例1,简单使用

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. "github.com/gogf/gf/g/net/gtcp"
    6. "github.com/gogf/gf/g/os/glog"
    7. "github.com/gogf/gf/g/util/gconv"
    8. )
    9. func main() {
    10. // Server
    11. go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
    12. defer conn.Close()
    13. for {
    14. data, err := conn.Recv(-1)
    15. if len(data) > 0 {
    16. fmt.Println(string(data))
    17. }
    18. if err != nil {
    19. break
    20. }
    21. }
    22. }).Run()
    23. time.Sleep(time.Second)
    24. // Client
    25. conn, err := gtcp.NewConn("127.0.0.1:8999")
    26. if err != nil {
    27. panic(err)
    28. }
    29. for i := 0; i < 10000; i++ {
    30. if err := conn.Send([]byte(gconv.String(i))); err != nil {
    31. glog.Error(err)
    32. }
    33. time.Sleep(time.Second)
    34. }
    35. }
    1. Server端,接收到客户端的数据后立即打印到终端上。
    2. Client端,使用同一个连接对象,在循环中每隔1秒向服务端发送当前递增的数字。同时,该功能也可以演示出底层Socket通信并没有使用缓冲实现,也就是说,执行一次Send即立刻向服务端发送数据。因此,客户端需要在本地自行管理好需要发送的缓冲数据。
    3. 执行结果 执行后,可以看到Server在终端上输出以下信息:
      1. 2018-07-11 22:11:08.650 0
      2. 2018-07-11 22:11:09.651 1
      3. 2018-07-11 22:11:10.651 2
      4. 2018-07-11 22:11:11.651 3
      5. 2018-07-11 22:11:12.651 4
      6. 2018-07-11 22:11:13.651 5
      7. 2018-07-11 22:11:14.652 6
      8. 2018-07-11 22:11:15.652 7
      9. 2018-07-11 22:11:16.652 8
      10. 2018-07-11 22:11:17.652 9
      11. 2018-07-11 22:11:18.652 10
      12. 2018-07-11 22:11:19.653 11
      13. ...

    使用示例2,回显服务

    我们将之前的回显服务改进一下:

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. "github.com/gogf/gf/g/net/gtcp"
    6. "github.com/gogf/gf/g/os/glog"
    7. "github.com/gogf/gf/g/os/gtime"
    8. )
    9. func main() {
    10. // Server
    11. go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
    12. defer conn.Close()
    13. for {
    14. data, err := conn.Recv(-1)
    15. if len(data) > 0 {
    16. if err := conn.Send(append([]byte("> "), data...)); err != nil {
    17. fmt.Println(err)
    18. }
    19. }
    20. if err != nil {
    21. break
    22. }
    23. }
    24. }).Run()
    25. time.Sleep(time.Second)
    26. // Client
    27. for {
    28. if conn, err := gtcp.NewConn("127.0.0.1:8999"); err == nil {
    29. if b, err := conn.SendRecv([]byte(gtime.Datetime()), -1); err == nil {
    30. fmt.Println(string(b), conn.LocalAddr(), conn.RemoteAddr())
    31. } else {
    32. fmt.Println(err)
    33. }
    34. conn.Close()
    35. } else {
    36. glog.Error(err)
    37. }
    38. time.Sleep(time.Second)
    39. }
    40. }

    该示例程序中,Client每隔1秒钟向Server发送当前的时间信息,Server接收到之后返回原数据信息,Client接收到Server端返回信息后立即打印到终端。

    执行后,输出结果为:

    1. > 2018-07-19 23:25:43 127.0.0.1:34306 127.0.0.1:8999
    2. > 2018-07-19 23:25:44 127.0.0.1:34308 127.0.0.1:8999
    3. > 2018-07-19 23:25:45 127.0.0.1:34312 127.0.0.1:8999
    4. > 2018-07-19 23:25:46 127.0.0.1:34314 127.0.0.1:8999

    使用示例3,HTTP客户端

    我们在这个示例中使用gtcp包来实现一个简单的HTTP客户端,读取并打印出百度首页的header和content内容。

    1. package main
    2. import (
    3. "fmt"
    4. "bytes"
    5. "github.com/gogf/gf/g/net/gtcp"
    6. "github.com/gogf/gf/g/util/gconv"
    7. )
    8. func main() {
    9. conn, err := gtcp.NewConn("www.baidu.com:80")
    10. if err != nil {
    11. panic(err)
    12. }
    13. defer conn.Close()
    14. if err := conn.Send([]byte("GET / HTTP/1.1\n\n")); err != nil {
    15. panic(err)
    16. }
    17. header := make([]byte, 0)
    18. content := make([]byte, 0)
    19. contentLength := 0
    20. for {
    21. data, err := conn.RecvLine()
    22. // header读取,解析文本长度
    23. if len(data) > 0 {
    24. array := bytes.Split(data, []byte(": "))
    25. // 获得页面内容长度
    26. if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) {
    27. contentLength = gconv.Int(array[1])
    28. }
    29. header = append(header, data...)
    30. header = append(header, '\n')
    31. }
    32. // header读取完毕,读取文本内容
    33. if contentLength > 0 && len(data) == 0 {
    34. content, _ = conn.Recv(contentLength)
    35. break
    36. }
    37. if err != nil {
    38. fmt.Errorf("ERROR: %s\n", err.Error())
    39. break
    40. }
    41. }
    42. if len(header) > 0 {
    43. fmt.Println(string(header))
    44. }
    45. if len(content) > 0 {
    46. fmt.Println(string(content))
    47. }
    48. }

    执行后,输出结果为:

    1. HTTP/1.1 200 OK
    2. Accept-Ranges: bytes
    3. Cache-Control: no-cache
    4. Connection: Keep-Alive
    5. Content-Length: 14615
    6. Content-Type: text/html
    7. Date: Sat, 21 Jul 2018 04:21:03 GMT
    8. Etag: "5b3c3650-3917"
    9. Last-Modified: Wed, 04 Jul 2018 02:52:00 GMT
    10. P3p: CP=" OTI DSP COR IVA OUR IND COM "
    11. Pragma: no-cache
    12. Server: BWS/1.1
    13. ...
    14. (略)