panjf2000 / gnet

🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.

Home Page:https://gnet.host

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question]: Server端回包立刻关闭连接,OnTraffic 收不到最后一个包的数据

xspren opened this issue · comments

Actions I've taken before I'm here

  • I've thoroughly read the documentations about this problem but still have no answer.
  • I've searched the Github Issues/Discussions but didn't find any similar problems that have been solved.
  • I've searched the internet for this problem but didn't find anything helpful.

Questions with details

你好,我最近使用 gnet v2 v2.3.4 版本 client 遇到一个问题。我们的场景是先建立一个连接到注册中心然后注册中心会返回一个重定向的 url 并立即发送关闭连接的操作。我这的现象是在还未处理 OnTraffic 的消息的时候连接已经关闭了,请问这种场景我需要怎么处理?

Code snippets (optional)

jConn := &jmtpConnection{
		callBack:      cb,
		reconnectChan: make(chan bool, 1),
		heartbeatSec:  heartbeatSec,
		serializeType: serializeType,
		applicationId: applicationId,
		instanceId:    instanceId,
		closeSignal:   make(chan bool, 1),
	}
	parse, err := urlParser.Parse(url)
	if err != nil {
		return nil, err
	}
	jConn.url = parse.Host
	cli, err := gnet.NewClient(jConn, gnet.WithMulticore(true), gnet.WithTCPNoDelay(gnet.TCPNoDelay))

func (j *jmtpConnection) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	log.Printf("W! jmtp connection will be shutdown, conn=%v, err=%v", c, err)
	return gnet.Close
}

func (j *jmtpConnection) OnTraffic(c gnet.Conn) (action gnet.Action) {
	packet, err := protocol.PacketDecoderWithGNet(c)
	if err != nil {
		log.Printf("E! jmtp packet decode failed. err=%v", err)
		j.callBack(nil, err)
		return gnet.None
	}
	switch pack := packet.(type) {
	case *v1.ConnectAck:
		log.Printf("I! receive connectAck packet, detail=%v", pack)
		if pack.Code != 0 {
			if "" != pack.RedirectUrl {
				log.Printf("I! receive connectAck paket, redirect url=%s", pack.RedirectUrl)
				err := j.setRedirectUrl(pack.RedirectUrl)
				if err != nil {
					log.Printf("E! set redirect url failed. redirectUrl=%s, err=%v", pack.RedirectUrl, err)
					j.callBack(nil, err)
					return gnet.Close
				}
				j.reconnect()
				return gnet.Close
			}
		}
		j.initSuccess.Store(true)
	case *v1.Disconnect:
		if "" != pack.RedirectUrl {
			log.Printf("I! receive diconnect paket, redirect url=%s", pack.RedirectUrl)
			err := j.setRedirectUrl(pack.RedirectUrl)
			if err != nil {
				log.Printf("E! set redirect url failed. redirectUrl=%s, err=%v", pack.RedirectUrl, err)
				j.callBack(nil, err)
				return gnet.Close
			}
			j.reconnectChan <- true
			return gnet.Close
		}
	case *v1.CommandAck, *v1.ReportAck:
		j.callBack(pack, nil)
	}
	return gnet.None
}

两个问题:

  1. 你确定对端已经把消息成功发送出去了?
  2. 你确定 OnTraffic 没有正常被调用?

你在 OnTraffic 里打印一下日志看看?

这个很确定,抓包看了,另外在 server 端做了点测试,发送 ack包 以后通过远程断点 hang 住后面流程不发 close 信号, client 这边就一切正常。。。另外还做了一个测试在 OnClose 的时候从新 reconnect 链接,基本上重试两三次可以成功,但是这么做在 clinet 数量多的时候怕重试太多对 sever 端造成影响。。

按照 TCP 协议,这种情况理论上不应该发生的。你能弄一个简单的、可以复现这个问题的 demo 代码 (客户端和服务端)然后贴在这里吗?我本地调试看看是哪里的问题,这种问题以前也没人遇到过。

对是的,按照 TCP 协议来说不应该有这个问题。代码您稍等一下,我看看怎么撸一个轻量点的,晚些时候放出来

我大概写了一个逻辑,server 端回写包后立刻 close,OnTraffic 是没有打印响应请求的。实际情况是我的 server 端是 java 的 netty 写的,这两个操作我不太清楚是不是等效的

package jmtpclient

import (
	"bufio"
	"fmt"
	"github.com/panjf2000/gnet/v2"
	"net"
	"testing"
	"time"
)

func TestTcpClient(t *testing.T) {
	listen, err := net.Listen("tcp", "127.0.0.1:9999")
	if err != nil {
		fmt.Println("Listen() failed, err: ", err)
		return
	}
	go func() {
		for {
			conn, err := listen.Accept() // 监听客户端的连接请求
			if err != nil {
				fmt.Println("Accept() failed, err: ", err)
				continue
			}
			go process(conn) // 启动一个goroutine来处理客户端的连接请求
		}
	}()

	cli, err := gnet.NewClient(&GNetClient{}, gnet.WithMulticore(true))
	defer cli.Stop()
	if err != nil {
		t.Error(err)
	}
	cli.Start()
	_, err = cli.Dial("tcp", "127.0.0.1:9999")
	if err != nil {
		t.Error(err)
	}
	time.Sleep(10 * time.Second)
}

func process(conn net.Conn) {
	defer conn.Close() // 关闭连接
	for {
		reader := bufio.NewReader(conn)
		var buf [128]byte
		n, err := reader.Read(buf[:]) // 读取数据
		if err != nil {
			fmt.Println("read from client failed, err: ", err)
			break
		}
		recvStr := string(buf[:n])
		fmt.Println("收到Client端发来的数据:", recvStr)
		conn.Write([]byte(recvStr)) // 发送数据
		conn.Close()
	}
}

type GNetClient struct {
	gnet.BuiltinEventEngine
}

func (G GNetClient) OnBoot(eng gnet.Engine) (action gnet.Action) {
	return gnet.None
}

func (G GNetClient) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	return []byte("test"), gnet.None
}

func (G GNetClient) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	return gnet.Close
}

func (G GNetClient) OnTraffic(c gnet.Conn) (action gnet.Action) {
	buf, err := c.Next(4)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("======================" + string(buf))
	}
	return gnet.None
}

不小心关错了。。。

好的,谢谢提供 demo。我等会儿 debug 一下看看。

我自己测试了一下,在 macOS 上确实有这个问题,但是在 Linux 是没问题的,你的客户端是部署在 mac 上的?
@xspren

对,是在 mac 上测的。。。单测有问题没上生产,我找个 linux 环境试一下

我已经定位到原因了,等这个 PR #531 合入后应该就能修复在 BSD 上的这个 bug 了。

Thanks for the bug report!

好的,多谢