Zinx 프레임워크 학습 - 읽기 및 쓰기 코루틴 분리

Zinx - V0.7 읽기 및 쓰기 코루틴 분리

  • 이전 연결은 클라이언트 데이터에 StartReader를 사용했습니다.
  • 다음으로, 우리는 Zinx에 약간의 변화를 줄 것입니다. 즉, 데이터를 배우기 위해 클라이언트와 상호 작용하는 Gouroutine이 하나에서 두 개로 변경됩니다. 하나는 클라이언트에서 데이터를 읽는 전용이고 다른 하나는 클라이언트에 데이터를 보내는 전용입니다. . 클라이언트가 데이터를 씁니다. 이 디자인의 이점은 무엇입니까?물론 목적은 높은 응집력이며 모듈의 기능은 단일합니다.
  • 서버는 여전히 클라이언트의 응답을 처리하고 있으며 주요 메서드는 Listen, Accept 등입니다. 클라이언트와 소켓이 성립되면 2개의 고루틴이 오픈되어 각각 데이터 읽기 업무와 데이터 쓰기 업무를 처리하며, 데이터 읽기와 쓰기 사이의 메시지는 채널을 통해 전달된다.

 구현 아이디어

  • 커넥션.고

채널을 추가하기 위한 연결 정의

type Connection struct {
	//当前链接的socket TCP套接字
	Conn *net.TCPConn

	//链接的ID
	ConnID uint32

	//当前的链接状态
	isClosed bool

	//告知当前链接已经退出的/停止 channel(由Reader告知Writer退出)
	ExitChan chan bool

	//无缓冲d管道,用于读、写Goroutine之间的消息通信
	msgChan chan []byte

	//消息的管理MsgID 和对应的处理业务API关系
	MsgHandler ziface.IMsgHandle
}

링크 방법을 초기화하여 채널을 늘립니다.

//初始化链接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		Conn:       conn,
		ConnID:     connID,
		MsgHandler: msgHandler,
		isClosed:   false,
		msgChan:    make(chan []byte),
		ExitChan:   make(chan bool, 1),
	}
	return c
}

StartWriter 메서드 추가

//写消息Goroutine, 专门发送给客户端消息的模块
func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println("[conn Writer exit!]", c.RemoteAddr().String())

	//不断的阻塞的等待channel的消息,进行写给客户端
	for {
		select {
		case data := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data error, ", err)
				return
			}
		case <-c.ExitChan:
			//代表Reader已经退出,此时Writer也要推出
			return
		}
	}
}

for 루프를 사용하여 채널의 메시지 대기를 계속 차단하고 클라이언트에 다시 씁니다. 종료는 독자가 종료하도록 작성자에게 알려야 합니다. 종료합니다.

읽기 및 쓰기 고루틴을 분리하도록 Start 메서드 수정

//启动链接 让当前的链接准备开始工作
func (c *Connection) Start() {
	fmt.Println("Conn Start() ... ConnID = ", c.ConnID)
	//启动从当前链接的读数据的业务
	go c.StartReader()
	//启动从当前链接写数据的业务
	go c.StartWriter()
}

SendMsg에서 데이터는 채널을 통해 클라이언트로 전송됩니다.

//提供一个SendMsg方法 将我们要发送给客户端的数据,先进行封包,再发送
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send msg")
	}

	//将data进行封包 MsgDataLen|MsgID|Data
	dp := NewDataPack()

	//MsgDataLen|MsgID|Data
	binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return errors.New("Pack error msg")
	}

	//将数据发送给客户端
	c.msgChan <- binaryMsg
	return nil
}

Writer에게 닫으라고 알리는 Stop 메서드가 추가되었습니다.

//停止链接 结束当前链接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop().. ConnID = ", c.ConnID)

	//如果当前链接已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true

	//关闭socket链接
	c.Conn.Close()

	//告知Writer关闭
	c.ExitChan <- true

	//回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

Reader가 종료되면 ExitChan에 데이터를 쓰고 오류가 있으면 Reader만 중단한 다음 Stop 메서드를 호출하므로 Stop 함수에서 Writer에게 이 메시지를 보낼 수 있습니다.

전체 아이디어

연결 구조에서 고루틴 통신을 읽고 쓰기 위한 파이프라인을 추가하고, NewConnection에서 파이프라인을 초기화하고, StartReader()에서 일부 비즈니스 처리를 수행하고, 데이터를 읽은 후 데이터를 보내고 DoMsgHandler 메서드를 호출한 다음 DoMsgHandler 메서드에서 Handle 메서드 호출됩니다. 서버가 Handle 메소드를 호출할 때 SendMsg 메소드를 호출하여 클라이언트에게 메시지를 보내는데, 과거에는 클라이언트에게 직접 쓰여졌다면 이제는 msgChan 파이프라인으로 메시지를 보낸다. ()는 파이프라인에서 전송된 데이터를 가져온 다음 클라이언트로 보냅니다. 종료할 때 StartReader는 연결에 따라 차단하고 요청을 기다리기 때문에 클라이언트가 종료되었음을 감지합니다.클라이언트가 종료된 후 for 루프를 중단하고 Stop 메소드를 호출합니다.Stop 메소드에서, 연결을 닫고 Writer에게 리소스를 종료하고 재활용하라고 지시한 다음 StartWriter는 메시지를 받으면 종료합니다.

추천

출처blog.csdn.net/qq_47431008/article/details/131032274