package tcp import ( "context" "fmt" "io" "log/slog" "net" ) type TcpClient struct { conn *net.TCPConn handler func(n int, data []byte) ctx context.CancelFunc conning bool } func StartTcpClient(rAddr string, handler func(n int, data []byte), readErr func(err error)) (*TcpClient, error) { raddr, addErr := net.ResolveTCPAddr("tcp", rAddr) if addErr != nil { return nil, addErr } conn, err := net.DialTCP("tcp", nil, raddr) ctx, ctxFun := context.WithCancel(context.Background()) client := &TcpClient{conn: conn, ctx: ctxFun} if err != nil { client.ctx = ctxFun client.conning = false return nil, err } go func() { for { select { case <-ctx.Done(): return default: } data := make([]byte, 1024) l, readDataErr := conn.Read(data) if readDataErr != nil { if opErr, ok := readDataErr.(*net.OpError); ok { slog.Error(fmt.Sprintf("TCP客户端[rAddr:%s]读取数据异常连接可能断开:", rAddr), opErr) client.conning = false readErr(readDataErr) } else if readDataErr == io.EOF { slog.Warn(fmt.Sprintf("TCP客户端[rAddr:%s]断开连接:", rAddr)) client.conning = false readErr(readDataErr) } else { slog.Error(fmt.Sprintf("TCP客户端[rAddr:%s]读数据出错:%s", raddr, readDataErr)) client.conning = false readErr(readDataErr) } return } client.conning = true handler(l, data) } }() client.conning = true return client, nil } func (c *TcpClient) Close() { if c != nil && c.conn != nil { slog.Info(fmt.Sprintf("TCP客户端[rAddr:%s]关闭连接", c.conn.RemoteAddr().String())) c.ctx() c.conn.Close() c.conn = nil } } func (c *TcpClient) IsConning() bool { if c != nil && c.conn != nil { return c.conning } return false } func (c *TcpClient) Send(data []byte) error { if c == nil || c.conn == nil { //slog.Error("tcp client send error,conn is nil") return fmt.Errorf("tcp client send error,conn is nil") } _, err := c.conn.Write(data) if err != nil { //slog.Error("tcp client send error", "error", err) return err } return nil }