//断开链接,关系客户端
/**
* @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
}

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

  1. PHP在线客服系统平台源码(完全开源的网页在线客服系统)

    在线客服系统是一个使用PHP.JavaScript和CSS开发的即时网页聊天咨询系统.该项目包含管理员和用户端.管理员端管理所有的管理,如编辑站点内容.管理提供者和预订,管理员在这个系统的管理中起着重 ...

  2. 开源在线客服系统源码(支持PC/H5/公众号/小程序)基于golang的网页在线客服系统...

    近年来市面上出现了越来越多的在线客服系统,还不断有新的在线客服企业加入,这让刚接触在线客服系统的人挑得眼花缭乱,那到底应该怎么选择一个适合企业使用的在线客服系统呢 我先给大家介绍下在线客服发展的历史, ...

  3. 在线客服系统源码(外贸多语言带翻译网页在线客服系统源码完整搭建)

    什么是在线客服系统? 在线客服系统是通过独立应用程序或嵌入式脚本代码进行的实时即时通讯消息交换.早期互联网发展还不流行的时候,那时候的网页结构还比较单一,很多企业的网站上只是简单挂一个邮箱地址,qq按 ...

  4. 在线客服系统源码(thinkphp网页在线客服系统源码完整搭建)

    PHP网站在线客#服系统源代码是一套全球范围内最为先进的.为同时在线用户数为100K~10M应用而设计的开源即时通讯引擎. (当前Demo的服务端版本:/turms-admin:latest./tur ...

  5. h5在线1v1客服|web在线客服系统|h5即时聊天

    网上有很多环信.美恰之类的客服系统,最近也使用h5+css3+fontJs+swiper+wcPop等技术架构开发了一个在线客服(1v1沟通聊天),可以应用到在线临时聊天.在线咨询等情景.实现了消息. ...

  6. 网站客服系统_网页客服系统安装使用_GOFLY在线客服系统

    GOFLY提供网站客服系统,网页客服系统 GOFLY,一套可私有化部署的免费开源客服系统,基于Golang开发,编译后的二进制文件可直接使用无需搭开发环境,下载zip解压即可,仅依赖MySQL数据库, ...

  7. 在线客服系统源码开发实战总结:动态加载js文件实现粘贴一段js的sdk代码,直接引入插件效果...

    常见的在线客服系统中,或者是统计代码中,粘贴一段js代码,就能引入某个插件的效果.这个是怎么实现的呢? 原理非常的简单: 对于不同的加载文件类型创建不同的节点,然后添加各自的属性,最后扔到head 标 ...

  8. java在线客服系统源码 springboot客服聊天源码 网页客服源码 netty通信技术,java源码

    ava在线客服系统源码 springboot客服聊天源码 网页客服源码 netty通信技术,java源码 Java在线客服系统源码 企业网站客服聊天源码 网页客服源码 开发环境:Java + Spri ...

  9. 【客服系统】在线客服系统源码外贸聊天通讯带翻译多语言支持网页安卓苹果打包封装APP

    随着全球化的加速推进,外贸行业对于在线客服系统的需求日益增长.一款功能强大.支持多语言交流.适用于网页和移动端的在线客服系统源码成为了众多企业的首选.本文将介绍一款名为"外贸聊天通讯带翻译多 ...

最新文章

  1. 4-20模块 序列化模块 hashlib模块
  2. 椭圆中心到椭圆切线的距离
  3. linux如何安装eclipse
  4. JAVA15.JDK15新特性.4 TextBlock
  5. Colossal Fibonacci Numbers! UVA - 11582(斐波那契求模)+快速幂+周期规律
  6. 如何在一年内从零基础到前端就业?
  7. udf iso9660 java_ISO和UDF光盘格式、扩展ISO9660----Joliet和Romeo文件系统
  8. 最全的常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
  9. Alert提示框插件
  10. ezd激光雕刻机软件使用笔记。
  11. Repeater实现批量删除
  12. 流媒体服务器分发RTSP视频流并发压力测试
  13. c语言验证费马大定理,数论概论 第四章 高次幂之和与费马大定理 习题解答(宋二娃的BLOG)...
  14. 身份证实名认证API接口,选择的时候应该注意什么?
  15. 3 Directory traversal
  16. 从头开始学习->JVM(八):运行时数据区(下)
  17. 算法基础—数据结构—双链表
  18. 爬取猫眼电影评论及数据分析(三)之数据可视化
  19. 一个免费全格式MP3音乐播放器的工具
  20. 基于阈值处理的图像分割算法!

热门文章

  1. viper4android耳机爆音,利用ViPER4Android FX音效驱动提升耳机音质
  2. ubuntu18镜像源修改为清华镜像源
  3. 【如何成为一名优秀的项目经理】跟着本文8个步骤走下去
  4. 计算机毕业设计垃圾分类回收微信小程序源码
  5. JdbcTemplate操作数据库demo
  6. 大恒工业相机搭建双目相机(软件)
  7. 给自己定个小目标 --java
  8. springboot整合mybatis错误 Invalid bound statement (not found): com.yuan.mapper.UserMapper.getUserList
  9. poj3666(基础dp+离散化)
  10. jQuery中的Ajax (六个Ajax的操作方法) 细解!!!