Skip to content

[Question]: How does a UDP server send messages to multiple clients? #685

@fuhaodev

Description

@fuhaodev

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 版本:2.7.0

场景是这样的,我用 gnet 做了一个 udp 语音网关,服务端会保存客户端的udp 的地址和端口,维护一个地址 map,群组内有一个客户端发送 udp 语音流数据,服务端会给其他客户端分发数据。另外客户端会定时发送UDP 心跳包,服务端更新客户端的地址和端口,避免 NAT 超时。

原本是想用 gnet.Conn 直接给其他群组内的其他客户端分发数据,但是 conn 的 Write API 不能指定地址和端口。所以我不得不保存每个客户端的 udp gnet.Conn,为避免 conn 失效,收到心跳时更新 conn。需要分发数据时遍历 conn 来Write。但感觉应该不是这么用的,issue 也提到 udp 的conn 在OnTraffic结束后就释放了,文档里也没找到处理这种情况的 API,请教下作者这种场景如何处理。

伪代码:

var userConnMap sync.Map

type GnetUdpConnection struct {
    userID         int32
    conn           gnet.Conn
    remoteAddr     string
    lastPing       time.Time
}

func (h *GnetUdpEventHandler) OnTraffic(c gnet.Conn) (action gnet.Action) {
    buf, err := c.Next(-1)
    packet = praseRTPPacket(buf)
    if packet.Type == "ping" {
        value, ok := userConnMap.Load(packet.userID)
        if ok {
            // 更新conn
            userConn := value.(*GnetUdpConnection)
            userConn.conn = c
            userConn.remoteAddr = c.RemoteAddr().String()
            userConn.lastPing = time.Now()
        } else {
            userConn := &GnetUdpConnection{
                userID:     packet.userID,
                conn:       c,
                remoteAddr: c.RemoteAddr().String(),
                lastPing:   time.Now(),
            }
            userConnMap.Store(packet.userID, userConn)
        }
    } else {
        userID := packet.UserID
        // 分发语音数据,排除当前用户
        userConnMap.Range(func(key, value interface{}) bool {
            if  key.(uint32) != userID {
                userConn := value.(*GnetUdpConnection)
                userConn.conn.Write(packet.Data)
            }
            return true
        })
    }
}

PS:这种场景 netty 很容实现,服务启动绑定端口时会返回一个 ChannelFuture能获取到一个全局的 Channel,在需要给客户端发送数据是直接封装一个DatagramPacket指定地址和端口,用Channel就可以写入数据

Code snippets (optional)

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededquestionFurther information is requestedwaiting for responsewaiting for the response from commenter

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions