网页在线客服系统-im及时聊天工具socket代码


//断开链接,关系客户端
/**
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
 */
func close(client_id string, client ...interface{}) error {
    clientsList.Remove(client_id)
    clientUids.Remove(client_id) //删除uid绑定的客户端
    if len(client) > 0 {
        switch client[0].(type) {
        case clientOne:
            client[0].(clientOne).Conn.Close()
        }
    }
    for _, group_id := range WsGetGroupCountByClientId(client_id) {
        WsLeaveGroup(client_id, group_id)
    }
    return nil
}

/**
保持心跳
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func wsping() error {
    for {
        time.Sleep(time.Duration(HandshakeTimeout) * time.Second)
        WsSendToAll(Message{Type: "ping", Message: Now()})
    }
    return nil
}

/**
初始化连接
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func ConnectInit(ws *websocket.Conn) string {
    client := clientOne{Send: make(chan Message)}
    client.Conn = ws
    client_id := fmt.Sprintf("%p", unsafe.Pointer(ws))
    clientsList.Set(client_id, client)
    go readMsg(&client)
    go writeMsg(&client)
    Msg <- Message{Clientid: client_id, Type: "join", Message: "用户加入"}
    return client_id
}

/**
在client通道发送消息
*/
func writeMsg(client *clientOne) {
    client_id := fmt.Sprintf("%p", unsafe.Pointer(client.Conn))
    defer func() {
        //取出该clien_id绑定的group和uid
        message := map[string]interface{}{"uid": WsGetUidByClientId(client_id), "group": WsGetGroupCountByClientId(client_id)}
        Msg <- Message{Clientid: client_id, Type: "level", Message: JsonEncode(message)}
        client.Conn.Close()
        close(client_id)
        err := recover()
        if err != nil {
            fmt.Println(err)
        }
    }()
    for {
        select {
        case data := <-client.Send:
            err := client.Conn.WriteJSON(data)
            if err != nil {
                break
            }
        }
    }
}

/**
在client通道读取消息
*/
func readMsg(client *clientOne) {
    client_id := fmt.Sprintf("%p", unsafe.Pointer(client.Conn))
    defer func() {
        //取出该clien_id绑定的group和uid
        message := map[string]interface{}{"uid": WsGetUidByClientId(client_id), "group": WsGetGroupCountByClientId(client_id)}
        Msg <- Message{Clientid: client_id, Type: "level", Message: JsonEncode(message)}
        client.Conn.Close()
        close(client_id)
        err := recover()
        if err != nil {
            fmt.Println(err)
        }
    }()

    for {
        // 读取消息。如果连接断开,则会返回错误    // 由于WebSocket一旦连接,便可以保持长时间通讯,则该接口函数可以一直运行下去,直到连接断开
        var msg Message
        err := client.Conn.ReadJSON(&msg)
        if err != nil { // 如果返回错误,就退出循环
            break
        }
        msg.Clientid = client_id
        Msg <- msg
    }
}

/**
发送消息到所有客户端
clientids 不发消息的客户端
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsSendToAll(msg Message, clientids ...interface{}) {
    var noClientids []string
    if len(clientids) > 0 {
        for _, c := range clientids {
            noClientids = append(noClientids, c.(string))
        }
    }
    go func() {
        clientsList.Iterator(func(clientid interface{}, client interface{}) bool {
            if len(noClientids) > 0 {
                if !InArray(clientid.(string), noClientids) {
                    client.(clientOne).Send <- msg
                }
            } else {
                client.(clientOne).Send <- msg
            }
            return true
        })
    }()
}

/**
发送消息到客户端
client_id 客户端地址 字符串
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsSendToClient(client_id string, msg Message) error {
    client := clientsList.Get(client_id)
    if client == nil {
        return errors.New("客户端不存在")
    }
    client.(clientOne).Send <- msg
    return nil
}

/**
关闭客户端
client_id 客户端地址 字符串
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsCloseClient(client_id string) error {
    client := clientsList.Get(client_id)
    if client == nil {
        return errors.New("客户端不存在")
    }
    //取出该clien_id绑定的group和uid
    message := map[string]interface{}{"uid": WsGetUidByClientId(client_id), "group": WsGetGroupCountByClientId(client_id)}
    Msg <- Message{Clientid: client_id, Type: "level", Message: JsonEncode(message)}
    return close(client_id, client)
}

/**
关闭用户绑定的客户端
uid  字符串
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsCloseUid(uid string) error {
    clients := WsGetClientIdByUid(uid)
    if len(clients) == 0 {
        return errors.New("该ID没有绑定ws")
    }
    for _, client_id := range clients {
        WsCloseClient(client_id)
    }
    return nil
}

/**
判断客户端是否在线
bool  true 在线 false 不在线
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsIsOnline(client_id string) bool {
    client := clientsList.Get(client_id)
    return client != nil
}

/**
将client_id与uid绑定,以便通过WsSendToUid($uid)发送数据,通过WsIsUidOnline($uid)用户是否在线。
uid解释:这里uid泛指用户id或者设备id,用来唯一确定一个客户端用户或者设备。
1、uid与client_id是一对多的关系,系统允许一个uid下有多个client_id。
2、但是一个client_id只能绑定一个uid,如果绑定多次uid,则只有最后一次绑定有效。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsBindUid(client_id, uid string) error {
    if clientsList.Get(client_id) == nil {
        return errors.New("客户端不存在")
    }
    clientUids.Set(client_id, uid)
    return nil
}

//获取所有链接的client_id 数据 map为client_id和绑定的uid
func WsUGetClientsList() map[string]string {
    var clientList = make(map[string]string)
    clientsList.Iterator(func(client_id interface{}, clientone interface{}) bool {
        clientList[client_id.(string)] = WsGetUidByClientId(client_id.(string))
        return true
    })
    return clientList
}

/**
将client_id与uid解绑。
注意:当client_id下线(连接断开)时会自动与uid解绑
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsUnBindUid(client_id, uid string) error {
    clientUids.Iterator(func(c interface{}, u interface{}) bool {
        if Equal(client_id, c) && Equal(uid, u) {
            clientUids.Remove(client_id)
            return false
        }
        return true
    })
    return nil
}

/**
uid绑定的client_id是否在线
判断$uid是否在线,此方法需要配合BindUid($client_uid, $uid)使用。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsIsUidOnline(uid string) bool {
    if len(WsGetClientIdByUid(uid)) > 0 {
        return true
    }
    return false
}

/**
返回一个数组,数组元素为与uid绑定的所有在线的client_id。如果没有在线的client_id则返回一个空数组。
此方法可以判断一个uid是否在线。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetClientIdByUid(uid string) []string {
    var clientids []string
    clientUids.Iterator(func(client_id interface{}, cuid interface{}) bool {
        if Equal(uid, cuid) {
            if WsIsOnline(client_id.(string)) {
                clientids = append(clientids, client_id.(string))
            } else {
                clientUids.Remove(client_id)
            }
        }
        return true
    })
    return clientids
}

/**
返回client_id绑定的uid,如果client_id没有绑定uid,则返回空。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetUidByClientId(client_id string) string {
    uid := clientUids.Get(client_id)
    if uid != nil {
        return uid.(string)
    }
    return ""
}

/**
向uid绑定的所有在线client_id发送数据。
注意:默认uid与client_id是一对多的关系,如果当前uid下绑定了多个client_id,则多个client_id对应的客户端都会收到消息,这类似于PC QQ和手机QQ同时在线接收消息。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsSendToUid(uid string, msg Message) error {
    var err error
    for _, client_id := range WsGetClientIdByUid(uid) {
        err = WsSendToClient(client_id, msg)
    }
    return err
}

/**
将client_id加入某个组,以便通过sendToGroup发送数据。
1、同一个client_id可以加入多个分组,以便接收不同组发来的数据。
2、当client_id下线(连接断开)后,该client_id会自动从该分组中删除,开发者无需调用Gateway::leaveGroup。
3、如果对应分组的所有client_id都下线,则对应分组会被自动删除
client_id 加入群组
group_id 群组id
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsJoinGroup(client_id, group_id string) error {
    if group_id == "" {
        return errors.New("分组不存在")
    }
    if !WsIsOnline(client_id) {
        return errors.New("当前用户不在线")
    }
    groupsListInfo := groupsList.Get(group_id)
    if groupsListInfo == nil {
        g := garray.NewArray(true)
        g.Append(client_id)
        groupsList.Set(group_id, g)
    } else {
        gs := (groupsListInfo).(*garray.Array)
        gsId := gs.Search(client_id)
        if gsId == -1 {
            gs.Append(client_id)
            groupsList.Set(group_id, gs)
        }
    }
    Msg <- Message{Clientid: client_id, Type: "joingroup", Message: group_id}
    return nil
}

/**
将client_id从某个组中删除,不再接收该分组广播(sendToGroup)发送的数据。
当client_id下线(连接断开)时,client_id会自动从它所属的各个分组中删除
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsLeaveGroup(client_id, group_id string) error {
    groupsListInfo := groupsList.Get(group_id)
    if groupsListInfo == nil {
        return errors.New("分组不存在")
    } else {
        gs := (groupsListInfo).(*garray.Array)
        gsId := gs.Search(client_id)
        if gsId > -1 {
            gs.Remove(gsId)
            if gs.Len() > 0 {
                groupsList.Set(group_id, gs)
            } else {
                groupsList.Remove(group_id)
                Msg <- Message{Type: "ungroup", Message: group_id}
            }
        } else {
            return errors.New("客户端不存在")
        }
    }
    Msg <- Message{Clientid: client_id, Type: "levelgroup", Message: group_id}
    return nil
}

/**
取消分组,或者说解散分组。 取消分组后所有属于这个分组的用户的连接将被移出分组,此分组将不再存在
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsUngroup(group_id string) error {
    if groupsList.Get(group_id) == nil {
        return errors.New("分组不存在")
    } else {
        groupsList.Remove(group_id)
    }
    Msg <- Message{Type: "ungroup", Message: group_id}
    return nil
}

/**
发送消息到所有分组
clientids 不发消息的客户端
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsSendToGroup(msg Message, group_id string, clientids ...interface{}) error {
    groupsListInfo := groupsList.Get(group_id)
    if groupsListInfo == nil {
        return errors.New("分组不存在")
    }
    gs := (groupsListInfo).(*garray.Array)
    if gs.Len() == 0 {
        return errors.New("分组成员不存在")
    }

    var noClientids []string
    if len(clientids) > 0 {
        for _, c := range clientids {
            noClientids = append(noClientids, c.(string))
        }
    }
    go func() {
        for _, clientid := range gs.Slice() {
            if len(noClientids) > 0 && InArray(clientid, noClientids) {
                continue
            }
            clientOneInfo := clientsList.Get(clientid)
            if clientOneInfo != nil {
                clientOneInfo.(clientOne).Send <- msg
            }
        }
    }()
    return nil
}

/**
获取某分组当前在线成连接数(多少client_id在线)。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetClientIdCountByGroup(group_id string) int {
    groupsListInfo := groupsList.Get(group_id)
    if groupsListInfo == nil {
        return 0
    }
    gs := (groupsListInfo).(*garray.Array)
    return gs.Len()
}

/**
通过client_id 获取该client_id所在分组。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetGroupCountByClientId(client_id string) []string {
    var groups []string
    groupsList.Iterator(func(group_id interface{}, clientids interface{}) bool {
        if (clientids.(*garray.Array)).Search(client_id) >= 0 {
            groups = append(groups, group_id.(string))
        }
        return true
    })
    return groups
}

/**
获取当前在线连接总数(多少client_id在线)。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetAllClientIdCount() int {
    return clientsList.Size()
}

/**
获取当前在线client_id信息。
* @Author  chenzhenhui <971688607@qq.com>
* @Copyright  2020~2030 http://www.znkefu.cn All rights reserved.
*/
func WsGetClientSessions(client_id string) interface{} {
    return (clientsList.Get(client_id)).(clientOne).Session
}
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值