精华内容
下载资源
问答
  • 开发集成云信IM小程序聊天室流程

    千次阅读 2018-11-28 10:08:37
    微信小程序开发交流qq群...请求白名单的设置 相关配置列表如下: request 合法域名: https://lbs.netease.im https://wlnimsc0.netease.im https://wlnimsc1.netease.im https://dr.netease.im htt...

    微信小程序开发交流qq群   173683895

       承接微信小程序开发。扫码加微信。

    请求白名单的设置

    地址:点击跳转

    调用聊天室API

    创建聊天室

    请求说明

    POST https://api.netease.im/nimserver/chatroom/create.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    创建聊天室

    参数说明

     

    参数类型必须说明
    creatorString聊天室属主的账号accid
    nameString聊天室名称,长度限制128个字符
    announcementString公告,长度限制4096个字符
    broadcasturlString直播地址,长度限制1024个字符
    extString扩展字段,最长4096字符
    queuelevelint队列管理权限:0:所有人都有权限变更队列,1:只有主播管理员才能操作变更。默认0

    curl请求示例

    curl -X POST -H "CheckSum: fc040248923c881f2fe7cc39602b79565230155c" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451217360" -H "Content-Type: application/x-www-form-urlencoded" -d 'name=mychatroom&announcement=&broadcasturl=xxxxxx&creator=zhangsan' 'https://api.netease.im/nimserver/chatroom/create.action'

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "chatroom": {
        "roomid": 66,
        "valid": true,
        "announcement": null,
        "name": "mychatroom",
        "broadcasturl": "xxxxxx",
        "ext": "",
        "creator": "zhangsan"
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、419、431、500

    具体请参考code状态表


    查询聊天室信息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/get.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    查询聊天室信息

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    needOnlineUserCountString是否需要返回在线人数,true或false,默认false

    curl请求示例

    curl -X POST -H "CheckSum: fc040248923c881f2fe7cc39602b79565230155c" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451217360"  -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=66&needOnlineUserCount=true' 'https://api.netease.im/nimserver/chatroom/get.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
      "chatroom": {
        "roomid": 66,
        "valid": true,
        "muted":false, //聊天室是否处于全体禁言状态,全体禁言时仅管理员和创建者可以发言 
        "announcement": null,
        "name": "mychatroom",
        "broadcasturl": "xxxxxx",
        "onlineusercount": 1,
        "ext": "",
        "creator": "zhangsan",
        "queuelevel": 0
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、431、500

    具体请参考code状态表


    批量查询聊天室信息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/getBatch.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    查询聊天室信息

    参数说明

    参数类型必须说明
    roomidsString多个roomid,格式为:["6001","6002","6003"](JSONArray对应的roomid,如果解析出错,会报414错误),限20个roomid
    needOnlineUserCountString是否需要返回在线人数,true或false,默认false

    curl请求示例

    curl -X POST -H "CheckSum: fc040248923c881f2fe7cc39602b79565230155c" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451217360"  -H "Content-Type: application/x-www-form-urlencoded" -d 'roomids=["6001","6002","6003"]&needOnlineUserCount=true' 'https://api.netease.im/nimserver/chatroom/getBatch.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
        "noExistRooms": [ //不存在的聊天室id列表
            6001
        ],
        "succRooms": [
            {
                "roomid": 6002,
                "valid": true,
                "announcement": "hi,this is announcement",
                "muted": false,
                "name": "6002 chatroom",
                "broadcasturl": "",
                "onlineusercount": 0,
                "ext": "6002 ext",
                "creator": "zhangsan",
                "queuelevel": 0
            }
        ],
        "failRooms": [ //失败的聊天室id,有可能是查的时候有500错误
        6003
        ],
        "code": 200
    }

    主要的返回码

    200、403、404、414、416、431、500

    具体请参考code状态表


    更新聊天室信息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/update.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    更新聊天室信息

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    nameString聊天室名称,长度限制128个字符
    announcementString公告,长度限制4096个字符
    broadcasturlString直播地址,长度限制1024个字符
    extString扩展字段,长度限制4096个字符
    needNotifyStringtrue或false,是否需要发送更新通知事件,默认true
    notifyExtString通知事件扩展字段,长度限制2048
    queuelevelint队列管理权限:0:所有人都有权限变更队列,1:只有主播管理员才能操作变更

    curl请求示例

    curl -X POST -H "CheckSum: 95a26060d002a473057a71cb7d949d6e91d6d167" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451214690" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=66&name=mynewchatroom&announcement=mynewchatroom' 'https://api.netease.im/nimserver/chatroom/update.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
      "chatroom": {
        "roomid": 66,
        "valid": true,
        "announcement": "这是聊天室",
        "name": "mychatroom",
        "broadcasturl": "xxxxxx",
        "ext": "",
        "creator": "zhangsan"
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、431、500、13002

    具体请参考code状态表


    修改聊天室开/关闭状态

    请求说明

    POST https://api.netease.im/nimserver/chatroom/toggleCloseStat.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    修改聊天室开/关闭状态

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    operatorString操作者账号,必须是创建者才可以操作
    validStringtrue或false,false:关闭聊天室;true:打开聊天室

    curl请求示例

    curl -X POST -H "CheckSum: 95a26060d002a473057a71cb7d949d6e91d6d167" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451214690"  -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=66&operator=zhangsan&valid=true' 'https://api.netease.im/nimserver/chatroom/toggleCloseStat.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "roomid": 13,
        "valid": true,
        "announcement": "这是聊天室",
        "name": "myChatroom",
        "broadcasturl": "http://www.xxxx.com/xxxxxx",
        "ext": "",
        "creator": "zhangsan"
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、417、419、431、500

    具体请参考code状态表


    设置聊天室内用户角色

    请求说明

    POST https://api.netease.im/nimserver/chatroom/setMemberRole.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    设置聊天室内用户角色

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    operatorString操作者账号accid
    targetString被操作者账号accid
    optint操作:
    1: 设置为管理员,operator必须是创建者 
    2:设置普通等级用户,operator必须是创建者或管理员 
    -1:设为黑名单用户,operator必须是创建者或管理员 
    -2:设为禁言用户,operator必须是创建者或管理员
    optvalueStringtrue或false,true:设置;false:取消设置
    notifyExtString通知扩展字段,长度限制2048,请使用json格式

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=66&operator=zhangsan&target=lisi&opt=2&optvalue=true' 'https://api.netease.im/nimserver/chatroom/setMemberRole.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "roomid": 16,
        "level": 10,
        "accid": "zhangsan",
        "type": "COMMON"
      },
      "code": 200
    }
    
    备注:
    返回的type字段可能为:
            LIMITED,          //受限用户,黑名单+禁言 
            COMMON,           //普通固定成员
            CREATOR,          //创建者 
            MANAGER,          //管理员 
            TEMPORARY,        //临时用户,非固定成员

    主要的返回码

    200、403、404、414、416、417、419、431、500、13002

    具体请参考code状态表


    请求聊天室地址

    请求说明

    POST https://api.netease.im/nimserver/chatroom/requestAddr.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    请求聊天室地址与令牌

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    accidString进入聊天室的账号
    clienttypeint1:weblink(客户端为web端时使用); 2:commonlink(客户端为非web端时使用);3:wechatlink(微信小程序使用), 默认1
    clientipString客户端ip,传此参数时,会根据用户ip所在地区,返回合适的地址

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: f1234540c12345673123456847aaaaaa" -H "Nonce: 1" -H "CurTime: 1451217708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=66&accidzhangsan&clienttype=1' 'https://api.netease.im/nimserver/chatroom/requestAddr.action'

    返回说明

    http 响应:json

    "Content-Type": "application/json; charset=utf-8"
    {
      "addr": [
        "testchat.netease.im:6666",
        "testchat.netease.im:8888"
      ],
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500、514、13002

    具体请参考code状态表


    发送聊天室消息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/sendMsg.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    往聊天室内发消息

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    msgIdString客户端消息id,使用uuid等随机串,msgId相同的消息会被客户端去重
    fromAccidString消息发出者的账号accid
    msgTypeint消息类型:
    0: 表示文本消息, 
    1: 表示图片, 
    2: 表示语音, 
    3: 表示视频, 
    4: 表示地理位置信息,
    6: 表示文件,
    10: 表示Tips消息,
    100: 自定义消息类型(特别注意,对于未对接易盾反垃圾功能的应用,该类型的消息不会提交反垃圾系统检测)
    resendFlagint重发消息标记,0:非重发消息,1:重发消息,如重发消息会按照msgid检查去重逻辑
    attachString消息内容,格式同消息格式示例中的body字段,长度限制4096字符
    extString消息扩展字段,内容可自定义,请使用JSON格式,长度限制4096字符
    antispamString对于对接了易盾反垃圾功能的应用,本消息是否需要指定经由易盾检测的内容(antispamCustom)。
    true或false, 默认false。
    只对消息类型为:100 自定义消息类型 的消息生效。
    antispamCustomString在antispam参数为true时生效。
    自定义的反垃圾检测内容, JSON格式,长度限制同body字段,不能超过5000字符,要求antispamCustom格式如下:

    {"type":1,"data":"custom content"}

    字段说明:
    1. type: 1:文本,2:图片。
    2. data: 文本内容or图片地址。
    skipHistoryint是否跳过存储云端历史,0:不跳过,即存历史消息;1:跳过,即不存云端历史;默认0
    bidString可选,反垃圾业务ID,实现“单条消息配置对应反垃圾”,若不填则使用原来的反垃圾配置
    highPriorityBoolean可选,true表示是高优先级消息,云信会优先保障投递这部分消息;false表示低优先级消息。默认false。
    强烈建议应用恰当选择参数,以便在必要时,优先保障应用内的高优先级消息的投递。若全部设置为高优先级,则等于没有设置。
    useYidunint可选,单条消息是否使用易盾反垃圾,可选值为0。 
    0:(在开通易盾的情况下)不使用易盾反垃圾而是使用通用反垃圾,包括自定义消息。

    若不填此字段,即在默认情况下,若应用开通了易盾反垃圾功能,则使用易盾反垃圾来进行垃圾消息的判断
    needHighPriorityMsgResendBoolean可选,true表示会重发消息,false表示不会重发消息。默认true

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: f541664055e557244421661866ad7799" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&fromAccid=zhangsan&msgType=0&attach=This+is+test+msg&msgId=c9e6c306-804f-4ec3-b8f0-573778829419' 'https://api.netease.im/nimserver/chatroom/sendMsg.action'

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "code":200,
      "desc":{
        "time": "1456396333115", 
        "fromAvator":"http://b12026.nos.netease.com/MTAxMTAxMA==/bmltYV84NDU4OF8xNDU1ODczMjA2NzUwX2QzNjkxMjI2LWY2NmQtNDQ3Ni0E2LTg4NGE4MDNmOGIwMQ==",
        "msgid_client": "c9e6c306-804f-4ec3-b8f0-573778829419",
        "fromClientType": "REST",
        "attach": "This+is+test+msg",
        "roomId": "36",
        "fromAccount": "zhangsan",
        "fromNick": "张三",
        "type": "0",
        "ext": "",
        "highPriorityFlag":1, //高优先级消息标记,不带此标记表示非高优先级
      } 
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    往聊天室内添加机器人

    请求说明

    POST https://api.netease.im/nimserver/chatroom/addRobot.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    往聊天室内添加机器人,机器人过期时间为24小时。

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    accidsJSONArray机器人账号accid列表,必须是有效账号,账号数量上限100个
    roleExtString机器人信息扩展字段,请使用json格式,长度4096字符
    notifyExtString机器人进入聊天室通知的扩展字段,请使用json格式,长度2048字符

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: f541664055e557244421661866ad7799" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=70&accids=["zhangsan","lisi"]' 'https://api.netease.im/nimserver/chatroom/addRobot.action'

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "failAccids": "[\"hzzhangsan\"]",
        "successAccids": "[\"hzlisi\"]",
        "oldAccids": "[\"hzwangwu\"]"
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、417、419、431、500、13003

    具体请参考code状态表


    从聊天室内删除机器人

    请求说明

    POST https://api.netease.im/nimserver/chatroom/removeRobot.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    从聊天室内删除机器人

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    accidsJSONArray机器人账号accid列表,必须是有效账号,账号数量上限100个

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: f541664055e557244421661866ad7799" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=70&accids=["zhangsan","lisi"]' 'https://api.netease.im/nimserver/chatroom/removeRobot.action'

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "failAccids": "[\"hzzhangsan\"]",
        "successAccids": "[\"hzlisi\"]"
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、419、431、500

    具体请参考code状态表


    设置临时禁言状态

    请求说明

    POST https://api.netease.im/nimserver/chatroom/temporaryMute.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    将聊天室内成员设置为临时禁言

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    operatorString操作者accid,必须是管理员或创建者
    targetString被禁言的目标账号accid
    muteDurationlong0:解除禁言;>0设置禁言的秒数,不能超过2592000秒(30天)
    needNotifyString操作完成后是否需要发广播,true或false,默认true
    notifyExtString通知广播事件中的扩展字段,长度限制2048字符

    curl请求示例

    curl -X POST -H "CheckSum: 66dc1ad0166a37037abcdfeb749bhh049c937777" -H "AppKey: acd7y640c85j8kf734219e1847aggggg" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=123&operator=zhangsan&target=lisi&muteDuration=300&notifyExt=这是扩展字段' "https://api.netease.im/nimserver/chatroom/temporaryMute.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "muteDuration": 300
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、417、419、431、500、13002

    具体请参考code状态表


    往聊天室有序队列中新加或更新元素

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queueOffer.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    往聊天室有序队列中新加或更新元素

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    keyStringelementKey,新元素的UniqKey,长度限制128字符
    valueStringelementValue,新元素内容,长度限制4096字符
    operatorString提交这个新元素的操作者accid,默认为该聊天室的创建者,若operator对应的帐号不存在,会返回404错误。
    若指定的operator不在线,则添加元素成功后的通知事件中的操作者默认为聊天室的创建者;若指定的operator在线,则通知事件的操作者为operator。
    transientString这个新元素的提交者operator的所有聊天室连接在从该聊天室掉线或者离开该聊天室的时候,提交的元素是否需要删除。 
    true:需要删除;false:不需要删除。默认false。 
    当指定该参数为true时,若operator当前不在该聊天室内,则会返回403错误。

    curl请求示例

    curl -X POST -H "CheckSum: 35dc67d0890f67007abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&key=1111&value=66666'  "https://api.netease.im/nimserver/chatroom/queueOffer.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    从队列中取出元素

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queuePoll.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    从队列中取出元素

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    keyString目前元素的elementKey,长度限制128字符,不填表示取出头上的第一个

    curl请求示例

    curl -X POST -H "CheckSum: 37dc19d0190007037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&key=333334444'   "https://api.netease.im/nimserver/chatroom/queuePoll.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": { 
        "value": "66666", 
        "key": "1111" 
      },
      "code": 200
    }

    主要的返回码

    200、403、404、414、416、431、500

    具体请参考code状态表


    排序列出队列中所有元素

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queueList.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    排序列出队列中所有元素

    参数说明

    参数类型必须说明
    roomidlong聊天室id

    curl请求示例

    curl -X POST -H "CheckSum: 37dc87di0190037037900999b74kkbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&key=1111'    "https://api.netease.im/nimserver/chatroom/queueList.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": { 
        "list": [ 
          { 
            "33333": "33333" 
          } 
        ] 
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    删除清理整个队列

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queueDrop.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    删除清理整个队列

    参数说明

    参数类型必须说明
    roomidlong聊天室id

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&key=k2&value=v2'  "https://api.netease.im/nimserver/chatroom/queueDrop.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": { 
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    初始化队列

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queueInit.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    初始化队列

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    sizeLimitlong队列长度限制,0~1000

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=38&sizeLimit=10'  "https://api.netease.im/nimserver/chatroom/queueInit.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": { 
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    将聊天室整体禁言

    请求说明

    POST https://api.netease.im/nimserver/chatroom/muteRoom.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    设置聊天室整体禁言状态(仅创建者和管理员能发言)

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    operatorString操作者accid,必须是管理员或创建者
    muteStringtrue或false
    needNotifyStringtrue或false,默认true
    notifyExtString通知扩展字段

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=4235&operator=zhangsan&mute=true'  "https://api.netease.im/nimserver/chatroom/muteRoom.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "success": true
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    查询聊天室统计指标TopN

    请求说明

    POST https://api.netease.im/nimserver/stats/chatroom/topn.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    1、根据时间戳,按指定周期列出聊天室相关指标的TopN列表 
    2、当天的统计指标需要到第二天才能查询

    参数说明

    参数类型必须说明
    topninttopn值,可选值 1~500,默认值100
    timestamplong需要查询的指标所在的时间坐标点,不提供则默认当前时间,单位秒/毫秒皆可
    periodString统计周期,可选值包括 hour/day, 默认hour
    orderbyString取排序值,可选值 active/enter/message,分别表示按日活排序,进入人次排序和消息数排序, 默认active

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451200147" -H "Content-Type: application/x-www-form-urlencoded" -d 'timestamp=1471781873&period=hour&topn=3&orderby=enter'  "https://api.netease.im/nimserver/stats/chatroom/topn.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "code": 200,
      "data": [
        {
          "activeNums": 5955,       // 该聊天室内的活跃数
          "datetime": 1471712400,   // 统计时间点,单位秒,按天统计的是当天的0点整点;按小时统计的是指定小时的整点
          "enterNums": 18621,       // 进入人次数量
          "msgs": 2793,             // 聊天室内发生的消息数
          "period": "HOUR",         // 统计周期,HOUR表示按小时统计;DAY表示按天统计
          "roomId": 3571337         // 聊天室ID号
        },
        {
          "activeNums": 6047,
          "datetime": 1471708800,
          "enterNums": 15785,
          "msgs": 2706,
          "period": "HOUR",
          "roomId": 3573737
        },
        {
          "activeNums": 5498,
          "datetime": 1471708800,
          "enterNums": 14590,
          "msgs": 2258,
          "period": "HOUR",
          "roomId": 3513774
        }
      ]
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    分页获取成员列表

    请求说明

    POST https://api.netease.im/nimserver/chatroom/membersByPage.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    分页获取成员列表

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    typeint需要查询的成员类型,0:固定成员;1:非固定成员;2:仅返回在线的固定成员
    endtimelong单位毫秒,按时间倒序最后一个成员的时间戳,0表示系统当前时间
    limitlong返回条数,<=100

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=4235&type=0&endtime=0&limit=20'   "https://api.netease.im/nimserver/chatroom/membersByPage.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "data": [
           {
              "roomid": 111,
              "accid": "abc",
              "nick": "abc",
              "avator": "http://nim.nos.netease.com/MTAxMTAwMg==/bmltYV8xNzg4NTA1NF8xNDU2Mjg0NDQ3MDcyX2E4NmYzNWI5LWRhYWEtNDRmNC05ZjU1LTJhMDUyMGE5MzQ4ZA==",
              "ext": "ext",
              "type": "MANAGER",
              "level": 2,
              "onlineStat": true,
              "enterTime": 1487145487971,
              "blacklisted": true,
              "muted": true,
              "tempMuted": true,
              "tempMuteTtl": 120,
              "isRobot": true,
              "robotExpirAt":120
           }
        ]
      },
      "code": 200
    }

    返回结果中字段说明

    字段类型说明
    roomidlong聊天室id
    accidString用户accid
    nickString聊天室内的昵称
    avatorString聊天室内的头像
    extString开发者扩展字段
    typeString角色类型:
    UNSET(未设置),
    LIMITED(受限用户,黑名单或禁言),
    COMMON(普通固定成员),
    CREATOR(创建者),
    MANAGER(管理员),
    TEMPORARY(临时用户,非固定成员)
    levelint成员级别(若未设置成员级别,则无此字段)
    onlineStatBoolean是否在线
    enterTimelong进入聊天室的时间点
    blacklistedBoolean是否在黑名单中(若未被拉黑,则无此字段)
    mutedBoolean是否被禁言(若未被禁言,则无此字段)
    tempMutedBoolean是否被临时禁言(若未被临时禁言,则无此字段)
    tempMuteTtllong临时禁言的解除时长,单位秒(若未被临时禁言,则无此字段)
    isRobotBoolean是否是聊天室机器人(若不是机器人,则无此字段)
    robotExpirAtint机器人失效的时长,单位秒(若不是机器人,则无此字段)

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    批量获取在线成员信息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queryMembers.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    批量获取在线成员信息

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    accidsJSONArray["abc","def"], 账号列表,最多200条

    curl请求示例

    curl -X POST -H "CheckSum: 32dc17d0190f37037abc9feb749bbf049c9367e7" -H "AppKey: fe416640c8e8a72734219e1847ad2547" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=4235&accids=["abc","def"]' "https://api.netease.im/nimserver/chatroom/queryMembers.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "desc": {
        "data": [
          {
            "roomid": 111,
            "accid": "abc",
            "nick": "cba",
            "type": 1, //0:普通成员(固定成员);1:聊天室创建者;2:聊天室管理员;3:临时用户(非聊天室固定成员);4:匿名用户(未注册账号);-1:受限用户(黑名单+禁言)
            "onlineStat": true
          }
        ]
      },
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    变更聊天室内的角色信息

    请求说明

    POST https://api.netease.im/nimserver/chatroom/updateMyRoomRole.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    变更聊天室内的角色信息

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    accidString需要变更角色信息的accid
    saveboolean变更的信息是否需要持久化,默认false,仅对聊天室固定成员生效
    needNotifyboolean是否需要做通知
    notifyExtString通知的内容,长度限制2048
    nickString聊天室室内的角色信息:昵称
    avatorString聊天室室内的角色信息:头像
    extString聊天室室内的角色信息:开发者扩展字段

    curl请求示例

    curl -X POST -H "CheckSum: 51eb13ea5ee3a2c00e8388e48e61c65c7866c366" -H "AppKey: he516640c8e8a72734219eh847ad2547" -H "Nonce: 1" -H "CurTime: 1451207708" -H "Content-Type: application/x-www-form-urlencoded" -d 'roomid=36&accid=hzyangfeng201403&save=true&needNotify=true&nick=myNick' "https://api.netease.im/nimserver/chatroom/updateMyRoomRole.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "code": 200
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    批量更新聊天室队列元素

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queueBatchUpdateElements.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    批量更新聊天室队列元素

    参数说明

    参数类型必须说明
    roomidlong聊天室id
    operatorString操作者accid,必须是管理员或创建者
    elementsString更新的key-value对,最大200个,示例:{"k1":"v1","k2":"v2"}
    needNotifybooleantrue或false,是否需要发送更新通知事件,默认true
    notifyExtString通知事件扩展字段,长度限制2048

    curl请求示例

    curl -X POST  -H 'appkey: fe416640c8e8a72734219e1847ad2547'  -H 'cache-control: no-cache'  -H 'checksum: 18f5435a7bf287af9150ad608797f75265495034'  -H 'content-type: application/x-www-form-urlencoded'   -H 'curtime: 1508481877'   -H 'nonce: 12345' -d 'roomid=18&operator=xiaoming01&elements=%7b%22k1%22%3a%22v1%22%2c%22k2%22%3a%22v2%22%7d' "https://api.netease.im/nimserver/chatroom/queueBatchUpdateElements.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "code": 200,
      "desc":{
            "noExistElementKey":[
                "k1"
            ]
        }
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表


    查询用户创建的开启状态聊天室列表

    请求说明

    POST https://api.netease.im/nimserver/chatroom/queryUserRoomIds.action HTTP/1.1
    Content-Type:application/x-www-form-urlencoded;charset=utf-8

    请求中Headers的设置请参考接口概述

    接口描述

    批量更新聊天室队列元素

    参数说明

    参数类型必须说明
    creatorString聊天室创建者accid

    curl请求示例

    curl -X POST  -H 'appkey: fe416640c8e8a72734219e1847ad2547'  -H 'cache-control: no-cache'  -H 'checksum: 18f5435a7bf287af9150ad608797f75265495034'  -H 'content-type: application/x-www-form-urlencoded'   -H 'curtime: 1508481877'   -H 'nonce: 12345' -d 'creator=xiaoming01' "https://api.netease.im/nimserver/chatroom/queryUserRoomIds.action"

    返回说明

    http 响应:json

    
    "Content-Type": "application/json; charset=utf-8"
    {
      "code": 200,
      "desc":{
        "roomids": [
            "1015",
            "2016",
            "2017",
            "5062",
            "23037398"
        ]
        }
    }

    主要的返回码

    200、403、414、416、431、500

    具体请参考code状态表

    展开全文
  • Netty 实现 WebSocket 聊天功能

    万次阅读 2015-03-26 21:32:00
    上一次我们用 [Netty](http://netty.io/) 快速实现了一个 Java 聊天程序(见)。现在,我们要做下修改,加入 WebSocket 的支持,使它可以在浏览器里进行文本聊天

    Netty 实现 WebSocket 聊天功能

    原文同步至http://www.waylau.com/netty-websocket-chat/

    上一次我们用 Netty 快速实现了一个 Java 聊天程序(见http://www.waylau.com/netty-chat/)。现在,我们要做下修改,加入 WebSocket 的支持,使它可以在浏览器里进行文本聊天。

    准备

    • JDK 7+
    • Maven 3.2.x
    • Netty 4.x
    • Eclipse 4.x

    WebSocket

    WebSocket 通过“Upgrade handshake(升级握手)”从标准的 HTTP 或HTTPS 协议转为 WebSocket。因此,使用 WebSocket 的应用程序将始终以 HTTP/S 开始,然后进行升级。在什么时候发生这种情况取决于具体的应用;它可以是在启动时,或当一个特定的 URL 被请求时。

    在我们的应用中,当 URL 请求以“/ws”结束时,我们才升级协议为WebSocket。否则,服务器将使用基本的 HTTP/S。一旦升级连接将使用的WebSocket 传输所有数据。

    整个服务器逻辑如下:

    1.客户端/用户连接到服务器并加入聊天

    2.HTTP 请求页面或 WebSocket 升级握手

    3.服务器处理所有客户端/用户

    4.响应 URI “/”的请求,转到默认 html 页面

    5.如果访问的是 URI“/ws” ,处理 WebSocket 升级握手

    6.升级握手完成后 ,通过 WebSocket 发送聊天消息

    服务端

    让我们从处理 HTTP 请求的实现开始。

    处理 HTTP 请求

    HttpRequestHandler.java

    public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1
        private final String wsUri;
        private static final File INDEX;
    
        static {
            URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation();
            try {
                String path = location.toURI() + "WebsocketChatClient.html";
                path = !path.contains("file:") ? path : path.substring(5);
                INDEX = new File(path);
            } catch (URISyntaxException e) {
                throw new IllegalStateException("Unable to locate WebsocketChatClient.html", e);
            }
        }
    
        public HttpRequestHandler(String wsUri) {
            this.wsUri = wsUri;
        }
    
        @Override
        public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
            if (wsUri.equalsIgnoreCase(request.getUri())) {
                ctx.fireChannelRead(request.retain());                  //2
            } else {
                if (HttpHeaders.is100ContinueExpected(request)) {
                    send100Continue(ctx);                               //3
                }
    
                RandomAccessFile file = new RandomAccessFile(INDEX, "r");//4
    
                HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);
                response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8");
    
                boolean keepAlive = HttpHeaders.isKeepAlive(request);
    
                if (keepAlive) {                                        //5
                    response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length());
                    response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
                }
                ctx.write(response);                    //6
    
                if (ctx.pipeline().get(SslHandler.class) == null) {     //7
                    ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
                } else {
                    ctx.write(new ChunkedNioFile(file.getChannel()));
                }
                ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);           //8
                if (!keepAlive) {
                    future.addListener(ChannelFutureListener.CLOSE);        //9
                }
    
                file.close();
            }
        }
    
        private static void send100Continue(ChannelHandlerContext ctx) {
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
            ctx.writeAndFlush(response);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            Channel incoming = ctx.channel();
            System.out.println("Client:"+incoming.remoteAddress()+"异常");
            // 当出现异常就关闭连接
            cause.printStackTrace();
            ctx.close();
        }
    }
    

    1.扩展 SimpleChannelInboundHandler 用于处理 FullHttpRequest信息

    2.如果请求是 WebSocket 升级,递增引用计数器(保留)并且将它传递给在 ChannelPipeline 中的下个 ChannelInboundHandler

    3.处理符合 HTTP 1.1的 “100 Continue” 请求

    4.读取默认的 WebsocketChatClient.html 页面

    5.判断 keepalive 是否在请求头里面

    6.写 HttpResponse 到客户端

    7.写 index.html 到客户端,判断 SslHandler 是否在 ChannelPipeline 来决定是使用 DefaultFileRegion 还是 ChunkedNioFile

    8.写并刷新 LastHttpContent 到客户端,标记响应完成

    9.如果 keepalive 没有要求,当写完成时,关闭 Channel

    HttpRequestHandler 做了下面几件事,

    • 如果该 HTTP 请求被发送到URI “/ws”,调用 FullHttpRequest 上的 retain(),并通过调用 fireChannelRead(msg) 转发到下一个 ChannelInboundHandler。retain() 是必要的,因为 channelRead() 完成后,它会调用 FullHttpRequest 上的 release() 来释放其资源。 (请参考我们先前的 SimpleChannelInboundHandler 在第6章中讨论)
    • 如果客户端发送的 HTTP 1.1 头是“Expect: 100-continue” ,将发送“100 Continue”的响应。
    • 在 头被设置后,写一个 HttpResponse 返回给客户端。注意,这是不是 FullHttpResponse,唯一的反应的第一部分。此外,我们不使用 writeAndFlush() 在这里 - 这个是在最后完成。
    • 如果没有加密也不压缩,要达到最大的效率可以是通过存储 index.html 的内容在一个 DefaultFileRegion 实现。这将利用零拷贝来执行传输。出于这个原因,我们检查,看看是否有一个 SslHandler 在 ChannelPipeline 中。另外,我们使用 ChunkedNioFile。
    • 写 LastHttpContent 来标记响应的结束,并终止它
    • 如果不要求 keepalive ,添加 ChannelFutureListener 到 ChannelFuture 对象的最后写入,并关闭连接。注意,这里我们调用 writeAndFlush() 来刷新所有以前写的信息。

    处理 WebSocket frame

    WebSockets 在“帧”里面来发送数据,其中每一个都代表了一个消息的一部分。一个完整的消息可以利用了多个帧。
    WebSocket “Request for Comments” (RFC) 定义了六中不同的 frame; Netty 给他们每个都提供了一个 POJO 实现 ,而我们的程序只需要使用下面4个帧类型:

    • CloseWebSocketFrame
    • PingWebSocketFrame
    • PongWebSocketFrame
    • TextWebSocketFrame

    在这里我们只需要显示处理 TextWebSocketFrame,其他的会由 WebSocketServerProtocolHandler 自动处理。

    下面代码展示了 ChannelInboundHandler 处理 TextWebSocketFrame,同时也将跟踪在 ChannelGroup 中所有活动的 WebSocket 连接

    TextWebSocketFrameHandler.java

    public class TextWebSocketFrameHandler extends
            SimpleChannelInboundHandler<TextWebSocketFrame> {
    
        public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx,
                TextWebSocketFrame msg) throws Exception { // (1)
            Channel incoming = ctx.channel();
            for (Channel channel : channels) {
                if (channel != incoming){
                    channel.writeAndFlush(new TextWebSocketFrame("[" + incoming.remoteAddress() + "]" + msg.text()));
                } else {
                    channel.writeAndFlush(new TextWebSocketFrame("[you]" + msg.text() ));
                }
            }
        }
    
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {  // (2)
            Channel incoming = ctx.channel();
            for (Channel channel : channels) {
                channel.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入"));
            }
            channels.add(ctx.channel());
            System.out.println("Client:"+incoming.remoteAddress() +"加入");
        }
    
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
            Channel incoming = ctx.channel();
            for (Channel channel : channels) {
                channel.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开"));
            }
            System.out.println("Client:"+incoming.remoteAddress() +"离开");
            channels.remove(ctx.channel());
        }
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
            Channel incoming = ctx.channel();
            System.out.println("Client:"+incoming.remoteAddress()+"在线");
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
            Channel incoming = ctx.channel();
            System.out.println("Client:"+incoming.remoteAddress()+"掉线");
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            Channel incoming = ctx.channel();
            System.out.println("Client:"+incoming.remoteAddress()+"异常");
            // 当出现异常就关闭连接
            cause.printStackTrace();
            ctx.close();
        }
    
    }
    

    1.TextWebSocketFrameHandler 继承自 SimpleChannelInboundHandler,这个类实现了 ChannelInboundHandler 接口,ChannelInboundHandler 提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承 SimpleChannelInboundHandler 类而不是你自己去实现接口方法。

    2.覆盖了 handlerAdded() 事件处理方法。每当从服务端收到新的客户端连接时,客户端的 Channel 存入 ChannelGroup 列表中,并通知列表中的其他客户端 Channel

    3.覆盖了 handlerRemoved() 事件处理方法。每当从服务端收到客户端断开时,客户端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客户端 Channel

    4.覆盖了 channelRead0() 事件处理方法。每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel。其中如果你使用的是 Netty 5.x 版本时,需要把 channelRead0() 重命名为messageReceived()

    5.覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动

    6.覆盖了 channelInactive() 事件处理方法。服务端监听到客户端不活动

    7.exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。在大部分情况下,捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。

    上面显示了 TextWebSocketFrameHandler 仅作了几件事:

    • 当WebSocket 与新客户端已成功握手完成,通过写入信息到 ChannelGroup 中的 Channel 来通知所有连接的客户端,然后添加新 Channel 到 ChannelGroup
    • 如果接收到 TextWebSocketFrame,调用 retain() ,并将其写、刷新到 ChannelGroup,使所有连接的 WebSocket Channel 都能接收到它。和以前一样,retain() 是必需的,因为当 channelRead0()返回时,TextWebSocketFrame 的引用计数将递减。由于所有操作都是异步的,writeAndFlush() 可能会在以后完成,我们不希望它来访问无效的引用。

    由于 Netty 处理了其余大部分功能,唯一剩下的我们现在要做的是初始化 ChannelPipeline 给每一个创建的新的 Channel 。做到这一点,我们需要一个ChannelInitializer

    WebsocketChatServerInitializer.java

    public class WebsocketChatServerInitializer extends
            ChannelInitializer<SocketChannel> { //1
    
        @Override
        public void initChannel(SocketChannel ch) throws Exception {//2
             ChannelPipeline pipeline = ch.pipeline();
    
            pipeline.addLast(new HttpServerCodec());
            pipeline.addLast(new HttpObjectAggregator(64*1024));
            pipeline.addLast(new ChunkedWriteHandler());
            pipeline.addLast(new HttpRequestHandler("/ws"));
            pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
            pipeline.addLast(new TextWebSocketFrameHandler());
    
        }
    }
    

    1.扩展 ChannelInitializer

    2.添加 ChannelHandler 到 ChannelPipeline

    initChannel() 方法设置 ChannelPipeline 中所有新注册的 Channel,安装所有需要的  ChannelHandler。

    WebsocketChatServer.java

    编写一个 main() 方法来启动服务端。

    public class WebsocketChatServer {
    
        private int port;
    
        public WebsocketChatServer(int port) {
            this.port = port;
        }
    
        public void run() throws Exception {
    
            EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap(); // (2)
                b.group(bossGroup, workerGroup)
                 .channel(NioServerSocketChannel.class) // (3)
                 .childHandler(new WebsocketChatServerInitializer())  //(4)
                 .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                 .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
    
                System.out.println("WebsocketChatServer 启动了");
    
                // 绑定端口,开始接收进来的连接
                ChannelFuture f = b.bind(port).sync(); // (7)
    
                // 等待服务器  socket 关闭 。
                // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
                f.channel().closeFuture().sync();
    
            } finally {
                workerGroup.shutdownGracefully();
                bossGroup.shutdownGracefully();
    
                System.out.println("WebsocketChatServer 关闭了");
            }
        }
    
        public static void main(String[] args) throws Exception {
            int port;
            if (args.length > 0) {
                port = Integer.parseInt(args[0]);
            } else {
                port = 8080;
            }
            new WebsocketChatServer(port).run();
    
        }
    }
    

    1.NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,Netty 提供了许多不同的 EventLoopGroup 的实现用来处理不同的传输。在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的 Channel上都需要依赖于 EventLoopGroup 的实现,并且可以通过构造函数来配置他们的关系。

    2.ServerBootstrap 是一个启动 NIO 服务的辅助启动类。你可以在这个服务中直接使用 Channel,但是这会是一个复杂的处理过程,在很多情况下你并不需要这样做。

    3.这里我们指定使用 NioServerSocketChannel 类来举例说明一个新的 Channel 如何接收进来的连接。

    4.这里的事件处理类经常会被用来处理一个最近的已经接收的 Channel。SimpleChatServerInitializer 继承自ChannelInitializer 是一个特殊的处理类,他的目的是帮助使用者配置一个新的 Channel。也许你想通过增加一些处理类比如 SimpleChatServerHandler 来配置一个新的 Channel 或者其对应的ChannelPipeline 来实现你的网络程序。当你的程序变的复杂时,可能你会增加更多的处理类到 pipline 上,然后提取这些匿名类到最顶层的类上。

    5.你可以设置这里指定的 Channel 实现的配置参数。我们正在写一个TCP/IP 的服务端,因此我们被允许设置 socket 的参数选项比如tcpNoDelay 和 keepAlive。请参考 ChannelOption 和详细的 ChannelConfig 实现的接口文档以此可以对ChannelOption 的有一个大概的认识。

    6.option() 是提供给NioServerSocketChannel 用来接收进来的连接。childOption() 是提供给由父管道 ServerChannel 接收到的连接,在这个例子中也是 NioServerSocketChannel。

    7.我们继续,剩下的就是绑定端口然后启动服务。这里我们在机器上绑定了机器所有网卡上的 8080 端口。当然现在你可以多次调用 bind() 方法(基于不同绑定地址)。

    恭喜!你已经完成了基于 Netty 聊天服务端程序。

    客户端

    在程序的 resources 目录下,我们创建一个 WebsocketChatClient.html 页面来作为客户端

    WebsocketChatClient.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    </head>
    <body>
        <script type="text/javascript">
            var socket;
            if (!window.WebSocket) {
                window.WebSocket = window.MozWebSocket;
            }
            if (window.WebSocket) {
                socket = new WebSocket("ws://localhost:8080/ws");
                socket.onmessage = function(event) {
                    var ta = document.getElementById('responseText');
                    ta.value = ta.value + '\n' + event.data
                };
                socket.onopen = function(event) {
                    var ta = document.getElementById('responseText');
                    ta.value = "连接开启!";
                };
                socket.onclose = function(event) {
                    var ta = document.getElementById('responseText');
                    ta.value = ta.value + "连接被关闭";
                };
            } else {
                alert("你的浏览器不支持 WebSocket!");
            }
    
            function send(message) {
                if (!window.WebSocket) {
                    return;
                }
                if (socket.readyState == WebSocket.OPEN) {
                    socket.send(message);
                } else {
                    alert("连接没有开启.");
                }
            }
        </script>
        <form onsubmit="return false;">
            <h3>WebSocket 聊天室:</h3>
            <textarea id="responseText" style="width: 500px; height: 300px;"></textarea>
            <br> 
            <input type="text" name="message"  style="width: 300px" value="Welcome to www.waylau.com">
            <input type="button" value="发送消息" onclick="send(this.form.message.value)">
            <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录">
        </form>
        <br> 
        <br> 
        <a href="http://www.waylau.com/" >更多例子请访问 www.waylau.com</a>
    </body>
    </html>
    

    逻辑比较简单,不累述。

    运行效果

    先运行 WebsocketChatServer,再打开多个浏览器页面实现多个 客户端访问 http://localhost:8080

    源码

    https://github.com/waylau/netty-4-user-guide-demoswebsocketchat

    参考

    展开全文
  • thinkphp5+GatewayWorker+Workerman聊天室,可以多人聊天,指定某个人进行聊天,还可以切换聊天房间Windows版安装 a) 安装thinkphp5; composercreate-projecttopthink/thinktp5--prefer-dist 复制代码 b) 进入...

    thinkphp5+GatewayWorker+Workerman聊天室,可以多人聊天,指定某个人进行聊天,还可以切换聊天房间
    Windows版安装
    a) 安装thinkphp5;

    1. composer create-project topthink/think tp5  --prefer-dist

    复制代码

     

    b) 进入tp5的目录,安装Windows版本的workerman;

    1. composer require workerman/workerman-for-win

    复制代码

     

    c} 安装Windows版本的gateway;

    1. composer require workerman/gateway-worker-for-win

    复制代码

     

    开始关键部分,服务端实现

    控制器 控制器:app\index\controller\Sregister

    1. <?php
    2. namespace app\index\controller;
    3.  
    4. use Workerman\Worker;
    5. use GatewayWorker\Register;
    6.  
    7. class Sregister{
    8.  
    9.     public function __construct(){
    10.         // register 服务必须是text协议
    11.         $register = new Register('text://0.0.0.0:1236');
    12.         
    13.         // 如果不是在根目录启动,则运行runAll方法
    14.         if(!defined('GLOBAL_START'))
    15.         {
    16.             Worker::runAll();
    17.         }
    18.     }
    19. }

    复制代码

     

    控制器:app\index\controller\Sgateway

    1. <?php
    2. namespace app\index\controller;
    3.  
    4. use Workerman\Worker;
    5. use GatewayWorker\Gateway;
    6. use Workerman\Autoloader;
    7.  
    8. class Sgateway{
    9.     public function __construct(){
    10.         // gateway 进程
    11.         $gateway = new Gateway("Websocket://0.0.0.0:7272");
    12.         // 设置名称,方便status时查看
    13.         $gateway->name = 'ChatGateway';
    14.         // 设置进程数,gateway进程数建议与cpu核数相同
    15.         $gateway->count = 4;
    16.         // 分布式部署时请设置成内网ip(非127.0.0.1)
    17.         $gateway->lanIp = '127.0.0.1';
    18.         // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
    19.         // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
    20.         $gateway->startPort = 2300;
    21.         // 心跳间隔
    22.         $gateway->pingInterval = 10;
    23.         // 心跳数据
    24.         $gateway->pingData = '{"type":"ping"}';
    25.         // 服务注册地址
    26.         $gateway->registerAddress = '127.0.0.1:1236';
    27.         
    28.         /*
    29.          // 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
    30.          $gateway->onConnect = function($connection)
    31.          {
    32.          $connection->onWebSocketConnect = function($connection , $http_header)
    33.          {
    34.          // 可以在这里判断连接来源是否合法,不合法就关掉连接
    35.          // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
    36.          if($_SERVER['HTTP_ORIGIN'] != 'http://chat.workerman.net')
    37.          {
    38.          $connection->close();
    39.          }
    40.          // onWebSocketConnect 里面$_GET $_SERVER是可用的
    41.          // var_dump($_GET, $_SERVER);
    42.          };
    43.          };
    44.          */
    45.         
    46.         // 如果不是在根目录启动,则运行runAll方法
    47.         if(!defined('GLOBAL_START'))
    48.         {
    49.             Worker::runAll();
    50.         }
    51.         
    52.         
    53.     }
    54. }

    复制代码

     

    控制器:app\index\controller\Sbusinessworker

    1. <?php
    2. namespace app\index\controller;
    3.  
    4. use Workerman\Worker;
    5. use GatewayWorker\BusinessWorker;
    6. use Workerman\Autoloader;
    7.  
    8. class Sbusinessworker{
    9.     public function __construct(){
    10.         // bussinessWorker 进程
    11.         $worker = new BusinessWorker();
    12.         // worker名称
    13.         $worker->name = 'ChatBusinessWorker';
    14.         // bussinessWorker进程数量
    15.         $worker->count = 4;
    16.         // 服务注册地址
    17.         $worker->registerAddress = '127.0.0.1:1236';
    18.         //设置处理业务的类,此处制定Events的命名空间
    19.         $worker->eventHandler = 'app\index\controller\Events';
    20.         
    21.         // 如果不是在根目录启动,则运行runAll方法
    22.         if(!defined('GLOBAL_START'))
    23.         {
    24.             Worker::runAll();
    25.         }
    26.     }
    27. }

    复制代码

     

    控制器:app\index\controller\Events

    1. <?php
    2. namespace app\index\controller;
    3.  
    4.  
    5. /**
    6.  * 用于检测业务代码死循环或者长时间阻塞等问题
    7.  * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
    8.  * 然后观察一段时间workerman.log看是否有process_timeout异常
    9.  */
    10. //declare(ticks=1);
    11.  
    12. /**
    13.  * 聊天主逻辑
    14.  * 主要是处理 onMessage onClose 
    15.  */
    16. use \GatewayWorker\Lib\Gateway;
    17.  
    18. class Events
    19. {
    20.    /**
    21.     * 有消息时
    22.     * @param int $client_id
    23.     * @param mixed $message
    24.     */
    25.    public static function onMessage($client_id, $message)
    26.    {
    27.         // debug
    28.         echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n";
    29.         
    30.         // 客户端传递的是json数据
    31.         $message_data = json_decode($message, true);
    32.         if(!$message_data)
    33.         {
    34.             return ;
    35.         }
    36.         
    37.         // 根据类型执行不同的业务
    38.         switch($message_data['type'])
    39.         {
    40.             // 客户端回应服务端的心跳
    41.             case 'pong':
    42.                 return;
    43.             // 客户端登录 message格式: {type:login, name:xx, room_id:1} ,添加到客户端,广播给所有客户端xx进入聊天室
    44.             case 'login':
    45.                 // 判断是否有房间号
    46.                 if(!isset($message_data['room_id']))
    47.                 {
    48.                     throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message");
    49.                 }
    50.                 
    51.                 // 把房间号昵称放到session中
    52.                 $room_id = $message_data['room_id'];
    53.                 $client_name = htmlspecialchars($message_data['client_name']);
    54.                 $_SESSION['room_id'] = $room_id;
    55.                 $_SESSION['client_name'] = $client_name;
    56.               
    57.                 // 获取房间内所有用户列表 
    58.                 $clients_list = Gateway::getClientSessionsByGroup($room_id);
    59.                 foreach($clients_list as $tmp_client_id=>$item)
    60.                 {
    61.                     $clients_list[$tmp_client_id] = $item['client_name'];
    62.                 }
    63.                 $clients_list[$client_id] = $client_name;
    64.                 
    65.                 // 转播给当前房间的所有客户端,xx进入聊天室 message {type:login, client_id:xx, name:xx} 
    66.                 $new_message = array('type'=>$message_data['type'], 'client_id'=>$client_id, 'client_name'=>htmlspecialchars($client_name), 'time'=>date('Y-m-d H:i:s'));
    67.                 Gateway::sendToGroup($room_id, json_encode($new_message));
    68.                 Gateway::joinGroup($client_id, $room_id);
    69.                
    70.                 // 给当前用户发送用户列表 
    71.                 $new_message['client_list'] = $clients_list;
    72.                 Gateway::sendToCurrentClient(json_encode($new_message));
    73.                 return;
    74.                 
    75.             // 客户端发言 message: {type:say, to_client_id:xx, content:xx}
    76.             case 'say':
    77.                 // 非法请求
    78.                 if(!isset($_SESSION['room_id']))
    79.                 {
    80.                     throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
    81.                 }
    82.                 $room_id = $_SESSION['room_id'];
    83.                 $client_name = $_SESSION['client_name'];
    84.                 
    85.                 // 私聊
    86.                 if($message_data['to_client_id'] != 'all')
    87.                 {
    88.                     $new_message = array(
    89.                         'type'=>'say',
    90.                         'from_client_id'=>$client_id, 
    91.                         'from_client_name' =>$client_name,
    92.                         'to_client_id'=>$message_data['to_client_id'],
    93.                         'content'=>"<b>对你说: </b>".nl2br(htmlspecialchars($message_data['content'])),
    94.                         'time'=>date('Y-m-d H:i:s'),
    95.                     );
    96.                     Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));
    97.                     $new_message['content'] = "<b>你对".htmlspecialchars($message_data['to_client_name'])."说: </b>".nl2br(htmlspecialchars($message_data['content']));
    98.                     return Gateway::sendToCurrentClient(json_encode($new_message));
    99.                 }
    100.                 
    101.                 $new_message = array(
    102.                     'type'=>'say', 
    103.                     'from_client_id'=>$client_id,
    104.                     'from_client_name' =>$client_name,
    105.                     'to_client_id'=>'all',
    106.                     'content'=>nl2br(htmlspecialchars($message_data['content'])),
    107.                     'time'=>date('Y-m-d H:i:s'),
    108.                 );
    109.                 return Gateway::sendToGroup($room_id ,json_encode($new_message));
    110.         }
    111.    }
    112.    
    113.    /**
    114.     * 当客户端断开连接时
    115.     * @param integer $client_id 客户端id
    116.     */
    117.    public static function onClose($client_id)
    118.    {
    119.        // debug
    120.        echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id onClose:''\n";
    121.        
    122.        // 从房间的客户端列表中删除
    123.        if(isset($_SESSION['room_id']))
    124.        {
    125.            $room_id = $_SESSION['room_id'];
    126.            $new_message = array('type'=>'logout', 'from_client_id'=>$client_id, 'from_client_name'=>$_SESSION['client_name'], 'time'=>date('Y-m-d H:i:s'));
    127.            Gateway::sendToGroup($room_id, json_encode($new_message));
    128.        }
    129.    }
    130.   
    131. }

    复制代码

     

    代码目录截图


    然后在项目根目录 新增入口文件 start_register.php 、start_gateway.php 、start_businessworker.php三个入口文件

    文件:start_register.php

    1. <?php 
    2. define('APP_PATH', __DIR__ . '/application/');
    3. define('BIND_MODULE','index/Sregister');
    4. // 加载框架引导文件
    5. require __DIR__ . '/thinkphp/start.php';

    复制代码

     

    文件:start_gateway.php

    1. <?php
    2. define('APP_PATH', __DIR__ . '/application/');
    3. define('BIND_MODULE','index/Sgateway');
    4. // 加载框架引导文件
    5. require __DIR__ . '/thinkphp/start.php';

    复制代码

     

    文件:start_businessworker.php

    1. <?php
    2. define('APP_PATH', __DIR__ . '/application/');
    3. define('BIND_MODULE','index/Sbusinessworker');
    4. // 加载框架引导文件
    5. require __DIR__ . '/thinkphp/start.php';

    复制代码

     

    [b]由于PHP-CLI在windows系统无法实现多进程以及守护进程,所以只能把三个文件放到bat文件,然后双击启动

    bat文件:start_for_win.bat

    1. php start_register.php start_gateway.php start_businessworker.phppause

    复制代码

     

    代码目录截图


    启动程序

    在项目的根目录下双击启动 start_for_win.bat


    服务端实现
    view : tp5\application\index\view\index.html

    1. <html><head>
    2.   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    3.   <title>workerman-chat PHP聊天室 Websocket(HTLM5/Flash)+PHP多进程socket实时推送技术</title>
    4.   <script type="text/javascript">
    5.   //WebSocket = null;
    6.   </script>
    7.   <link href="__PUBLIC__/chat/css/bootstrap.min.css" rel="stylesheet">
    8.   <link href="__PUBLIC__/chat/css/style.css" rel="stylesheet">
    9.   <!-- Include these three JS files: -->
    10.   <script type="text/javascript" src="__PUBLIC__/chat/js/swfobject.js"></script>
    11.   <script type="text/javascript" src="__PUBLIC__/chat/js/web_socket.js"></script>
    12.   <script type="text/javascript" src="__PUBLIC__/chat/js/jquery.min.js"></script>
    13.  
    14.   <script type="text/javascript">
    15.     if (typeof console == "undefined") {    this.console = { log: function (msg) {  } };}
    16.     // 如果浏览器不支持websocket,会使用这个flash自动模拟websocket协议,此过程对开发者透明
    17.     WEB_SOCKET_SWF_LOCATION = "__PUBLIC__/chat/swf/WebSocketMain.swf";
    18.     // 开启flash的websocket debug
    19.     WEB_SOCKET_DEBUG = true;
    20.       
    21.     var ws, name, client_list={};
    22.  
    23.     // 连接服务端
    24.     function connect() {
    25.        // 创建websocket
    26.        ws = new WebSocket("ws://"+document.domain+":7272");
    27.        // 当socket连接打开时,输入用户名
    28.        ws.onopen = onopen;
    29.        // 当有消息时根据消息类型显示不同信息
    30.        ws.onmessage = onmessage; 
    31.        ws.onclose = function() {
    32.           console.log("连接关闭,定时重连");
    33.           connect();
    34.        };
    35.        ws.onerror = function() {
    36.            console.log("出现错误");
    37.        };
    38.     }
    39.  
    40.     // 连接建立时发送登录信息
    41.     function onopen()
    42.     {
    43.         if(!name)
    44.         {
    45.             show_prompt();
    46.         }
    47.         // 登录
    48.         var login_data = '{"type":"login","client_name":"'+name.replace(/"/g, '\\"')+'","room_id":"<?php echo isset($_GET['room_id']) ? $_GET['room_id'] : 1?>"}';
    49.         console.log("websocket握手成功,发送登录数据:"+login_data);
    50.         ws.send(login_data);
    51.     }
    52.  
    53.     // 服务端发来消息时
    54.     function onmessage(e)
    55.     {
    56.         console.log(e.data);
    57.         var data = eval("("+e.data+")");
    58.         switch(data['type']){
    59.             // 服务端ping客户端
    60.             case 'ping':
    61.                 ws.send('{"type":"pong"}');
    62.                 break;;
    63.             // 登录 更新用户列表
    64.             case 'login':
    65.                 //{"type":"login","client_id":xxx,"client_name":"xxx","client_list":"[...]","time":"xxx"}
    66.                 say(data['client_id'], data['client_name'],  data['client_name']+' 加入了聊天室', data['time']);
    67.                 if(data['client_list'])
    68.                 {
    69.                     client_list = data['client_list'];
    70.                 }
    71.                 else
    72.                 {
    73.                     client_list[data['client_id']] = data['client_name']; 
    74.                 }
    75.                 flush_client_list();
    76.                 console.log(data['client_name']+"登录成功");
    77.                 break;
    78.             // 发言
    79.             case 'say':
    80.                 //{"type":"say","from_client_id":xxx,"to_client_id":"all/client_id","content":"xxx","time":"xxx"}
    81.                 say(data['from_client_id'], data['from_client_name'], data['content'], data['time']);
    82.                 break;
    83.             // 用户退出 更新用户列表
    84.             case 'logout':
    85.                 //{"type":"logout","client_id":xxx,"time":"xxx"}
    86.                 say(data['from_client_id'], data['from_client_name'], data['from_client_name']+' 退出了', data['time']);
    87.                 delete client_list[data['from_client_id']];
    88.                 flush_client_list();
    89.         }
    90.     }
    91.  
    92.     // 输入姓名
    93.     function show_prompt(){  
    94.         name = prompt('输入你的名字:', '');
    95.         if(!name || name=='null'){  
    96.             name = '游客';
    97.         }
    98.     }
    99.  
    100.     // 提交对话
    101.     function onSubmit() {
    102.       var input = document.getElementById("textarea");
    103.       var to_client_id = $("#client_list option:selected").attr("value");
    104.       var to_client_name = $("#client_list option:selected").text();
    105.       ws.send('{"type":"say","to_client_id":"'+to_client_id+'","to_client_name":"'+to_client_name+'","content":"'+input.value.replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r')+'"}');
    106.       input.value = "";
    107.       input.focus();
    108.     }
    109.  
    110.     // 刷新用户列表框
    111.     function flush_client_list(){
    112.         var userlist_window = $("#userlist");
    113.         var client_list_slelect = $("#client_list");
    114.         userlist_window.empty();
    115.         client_list_slelect.empty();
    116.         userlist_window.append('<h4>在线用户</h4><ul>');
    117.         client_list_slelect.append('<option value="all" id="cli_all">所有人</option>');
    118.         for(var p in client_list){
    119.             userlist_window.append('<li id="'+p+'">'+client_list[p]+'</li>');
    120.             client_list_slelect.append('<option value="'+p+'">'+client_list[p]+'</option>');
    121.         }
    122.         $("#client_list").val(select_client_id);
    123.         userlist_window.append('</ul>');
    124.     }
    125.  
    126.     // 发言
    127.     function say(from_client_id, from_client_name, content, time){
    128.         $("#dialog").append('<div class="speech_item"><img src="http://lorempixel.com/38/38/?'+from_client_id+'" class="user_icon" /> '+from_client_name+' <br> '+time+'<div style="clear:both;"></div><p class="triangle-isosceles top">'+content+'</p> </div>');
    129.     }
    130.  
    131.     $(function(){
    132.         select_client_id = 'all';
    133.         $("#client_list").change(function(){
    134.              select_client_id = $("#client_list option:selected").attr("value");
    135.         });
    136.     });
    137.   </script>
    138. </head>
    139. <body onload="connect();">
    140.     <div class="container">
    141.         <div class="row clearfix">
    142.             <div class="col-md-1 column">
    143.             </div>
    144.             <div class="col-md-6 column">
    145.                <div class="thumbnail">
    146.                    <div class="caption" id="dialog"></div>
    147.                </div>
    148.                <form onsubmit="onSubmit(); return false;">
    149.                     <select style="margin-bottom:8px" id="client_list">
    150.                         <option value="all">所有人</option>
    151.                     </select>
    152.                     <textarea class="textarea thumbnail" id="textarea"></textarea>
    153.                     <div class="say-btn"><input type="submit" class="btn btn-default" value="发表" /></div>
    154.                </form>
    155.                <div>
    156.                    <b>房间列表:</b>(当前在 房间<?php echo isset($_GET['room_id'])&&intval($_GET['room_id'])>0 ? intval($_GET['room_id']):1; ?>)<br>
    157.                    <a href="/?room_id=1">房间1</a>    <a href="/?room_id=2">房间2</a>    <a href="/?room_id=3">房间3</a>    <a href="/?room_id=4">房间4</a>
    158.                <br><br>
    159.                </div>
    160.                <p class="cp">PHP多进程+Websocket(HTML5/Flash)+PHP Socket实时推送技术    Powered by <a href="http://www.workerman.net/workerman-chat" target="_blank">workerman-chat</a></p>
    161.             </div>
    162.             <div class="col-md-3 column">
    163.                <div class="thumbnail">
    164.                    <div class="caption" id="userlist"></div>
    165.                </div>
    166.               
    167.             </div>
    168.         </div>
    169.     </div>
    170.     <script type="text/javascript">var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F7b1919221e89d2aa5711e4deb935debd' type='text/javascript'%3E%3C/script%3E"));</script>
    171. </body>
    172. </html>

    复制代码

     

    运行结果截图


    linux版安装
    a) 安装thinkphp5;

    1. composer create-project topthink/think tp5  --prefer-dist

    复制代码

     

    b) 进入tp5的目录,安装linux版本的workerman;

    1. composer require topthink/think-worker

    复制代码

     

    c} 安装linux版本的gateway;

    1. composer require workerman/gateway-worker-for-win

    复制代码

     

    1. 关键部分,服务端实现

    复制代码

     

    控制器 app\index\controller\Gate

    1. <?php 
    2. /**
    3.  * linux workerman例子测试
    4.  * 需要在Linux系统控制台进行启动,启动文件位于根目录的start.php文件中
    5.  * Windows无法进行同时启动多个协议
    6.  * 由于PHP-CLI在windows系统无法实现多进程以及守护进程,所以windows版本Workerman建议仅作开发调试使用。
    7.  */
    8. namespace app\index\controller;
    9.  
    10. use Workerman\Worker;
    11. use GatewayWorker\Gateway;
    12. use GatewayWorker\Register;
    13. use GatewayWorker\BusinessWorker;
    14.  
    15.  
    16. class Gate
    17. {
    18.     /**
    19.      * 构造函数
    20.      * @access public
    21.      */
    22.     public function __construct(){
    23.         
    24.         //初始化各个GatewayWorker
    25.         //初始化register register 服务必须是text协议
    26.         $register = new Register('text://0.0.0.0:1236');
    27.     
    28.         //初始化 bussinessWorker 进程
    29.         $worker = new BusinessWorker();
    30.         // worker名称
    31.         $worker->name = 'ChatBusinessWorker';
    32.         // bussinessWorker进程数量
    33.         $worker->count = 4;
    34.         // 服务注册地址
    35.         $worker->registerAddress = '127.0.0.1:1236';
    36.         //设置处理业务的类,此处制定Events的命名空间
    37.         $worker->eventHandler = 'app\index\controller\Events';
    38.         // 初始化 gateway 进程
    39.         $gateway = new Gateway("websocket://0.0.0.0:7272");
    40.         // 设置名称,方便status时查看
    41.         $gateway->name = 'ChatGateway';
    42.         $gateway->count = 4;
    43.         // 分布式部署时请设置成内网ip(非127.0.0.1)
    44.         $gateway->lanIp = '127.0.0.1';
    45.         // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
    46.         // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
    47.         $gateway->startPort = 2300;
    48.         // 心跳间隔
    49.         $gateway->pingInterval = 10;
    50.         // 心跳数据
    51.         $gateway->pingData = '{"type":"ping"}';
    52.         // 服务注册地址
    53.         $gateway->registerAddress = '127.0.0.1:1236';
    54.     
    55.         //运行所有Worker;
    56.         Worker::runAll();
    57.     }
    58. }

    复制代码

     

    入口文件
    文件: start.php

    1. <?php
    2. /**
    3.  * workerman + GatewayWorker
    4.  * 此文件只能在Linux运行
    5.  * run with command
    6.  * php start.php start
    7.  */
    8. ini_set('display_errors', 'on');
    9. if(strpos(strtolower(PHP_OS), 'win') === 0)
    10. {
    11.     exit("start.php not support windows.\n");
    12. }
    13. //检查扩展
    14. if(!extension_loaded('pcntl'))
    15. {
    16.     exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
    17. }
    18. if(!extension_loaded('posix'))
    19. {
    20.     exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
    21. }
    22.  
    23. define('APP_PATH', __DIR__ . '/application/');
    24. define('BIND_MODULE','chat/Gate');
    25. // 加载框架引导文件
    26. require __DIR__ . '/thinkphp/start.php';

    复制代码

     

    启动程序

    1. php start.php start

    复制代码

     

    客户端跟Windows一样就可以了

    workerman官网:http://www.workerman.net/
    workerman文档:http://doc3.workerman.net/
    GatewayWorker文档:http://doc3.workerman.net/

    附件 tp5.zip ( 837.52 KB 下载:905 次 )

    展开全文
  • C语言实现Linux聊天工具

    千次阅读 2019-01-08 11:46:13
    C语言实现Linux聊天工具。

    项目源码https://github.com/One-Snail/mychatroom

    引言

    在互联网越来越发达的今天,人们对网络的依赖越来越多,越来越离不开网 络,QQ、微软MSN、移动的Fetion等,都是做的比较成功的, 随着网络的日益普及,各种聊天工具也层出不穷,当我们学习《网络与程序设计》这门课程之 后,接下来的课程设计就是针对一个简单的网络聊天程序,利用C语言为开发工具,实现基本的通讯功能。

     

    【一】项目题目及功能

    项目题目:socket聊天工具

    功能:

    • 实现注册功能。
    • 登录功能。
    • 客户可以通过服务器转发,实现一对一聊天。
      • 4.客户端程序应该可以实时显示目前其它用户的在线状态。
      • 5.具有文件传输功能
      • 6.具有易用、美观的界面。


     

    【二】项目整体设计框架图及说明

    • 客户端:

     

     

     

     1. 界面部分 

    主界面分别为登录(login),注册(register),离开(quit)三个选项,当用户登陆成功以后,二级界面为显示在线用户列表(show users_online),聊天(chat),返回(goback),注销(write off).

    2. 功能部分

    主函数使用TCP流式套接字与服务器进行通信,通信地址为本机地址,主进程负责连接服务器,另有聊天函数,在此函数中新建一个子进程用于接受来自服务器端的通信,而父进程负责发送客户端的数据到服务器,这样可以避免进程因为write函数和read函数一直阻塞,并且各个客户端发送过程中不会互相干扰.除此之外,还有注册函数和登录函数,用户注册和登录的信息会被发送到服务器,然后由服务器发送给MySQL。

     

    • 服务器:

     

    1. 主函数模块

    该模块负责服务器创建套接字、绑定、监听等初始化设置,以及如果有连接到达了就创建新的线程,然后在线程中处理客户端的请求。

    2. 功能函数

    服务器的功能是与客户端的功能大多一一对应的,例如登录、注册、聊天等,服务器根据客户机的请求提供不同的服务。此外,服务器与MySQL的交互函数也在这个模块里。


     

    【三】项目分模块设计说明,流程图

    • 服务器:

    1. 处理注册信息
    2. 处理登录信息
    3. 转发聊天消息
    4. 发送在线用户列表

    • 客户端:

    1. 主函数模块
    2. 界面模块
    3. 聊天模块
    4. 注册模块
    5. 登录模块
    6. 文件传输模块


     

    【四】项目关键数据结构及说明

    服务器与客户端都定义了两个结构体变量:

    typedef struct
    
    {
    
        char loginid[20];         /*用户名*/
    
        char password[20];      /*密码*/
    
    } users_t;
    typedef struct
    
    {
    
        int r_sockfd;          //接收方的socketfd
    
        char content[500];     //消息内容
    
    } hdr_t;
    
    


    其中,users_t存放用户的信息,包括用户的ID和这密码;hdr_t存放用户发送的消息,包括接收方的sockfd和要发送的消息内容。


     

    【五】项目关键函数说明及流程图

    • 服务器:

    1_register()

    1. 函数形式:void _register(int);
    2. 功能:处理客户的注册请求,成功则将客户发送的用户信息加入MySQL中的“users_info”表中,并发送注册成功的消息给用户;否则发送注册失败的提示信息给用户。

    2_login()

    1. 函数形式:users_t  _login(int);
    2. 功能:处理客户的登录请求,成功则将客户信息加入MySQL中的“users_online”表中,并发送登录成功的消息给用户;否则发送登录失败的提示信息给用户。
    3. 返回值:成功则返回包含客户信息的users_t结构体,否则返回NULL。

    3_chat()

    1. 函数形式:void _chat(int);
    2. 功能:转发客户的消息,以实现聊天功能。

    4_online()

    1. 函数形式:void _online(int);
    2. 功能:显示在线用户的列表。

    5_quit()

    1. 函数形式:void _quit(int);
    2. 功能:用户退出。

    6write_off()

    1. 函数形式:void write_off(int);
    2. 功能:用户注销。

    7gif_handle_client()

    1. 函数形式:void gif_handle_client(int *);
    2. 功能:线程处理函数。创建新线程后线程的处理函数,根据用户输入的不同命令执行不同的动作。

    8sql_manage()

    1. 函数形式:void sql_manage(void)
    2. 功能:数据库处理函数,包括数据库的初始化、连接等行为。
    • 客户端:

     

    1_chat()

    1. 函数形式:void _chat(int);
    2. 功能:客户端聊天函数,实现和服务器的通信。
    3. 具体实现:

    使用了fork()函数创建一个子进程,当fork函数的返回值pid为0时,即代表子进程启动,这个子进程用于接受服务器端的数据,这里定义了一个字符数组缓冲区recvbuf来接受服务器端的数据,用read函数来将套接字缓冲区中的数据读入到recvbuf中,read函数还需要判断一下返回值的类型,返回-1代表客户端读取失败,返回0代表服务器关闭,返回其他值则代表正常接收数据。然后用fputs函数将缓冲区中的数据打印到客户端屏幕上。父进程用于发送客户端数据到服务器,这里定义一个发送缓冲区sendbuf,从客户端标准输入中接受数据到sendbuf中,然后在while循环中使用send函数将sendbuf中的数据复制到客户端与服务器连接的套接字缓冲区中。最后,如果while循环退出,则代表客户端的服务器父进程退出,这时因为父子进程中的变量和内存不共享,所以需要用一个信号函数通知子进程客户端已关闭。

     

    2_login()

    1. 函数形式:int _login(int,users_t);
    2. 功能:实现客户端登录。
    3. 具体实现:

    首先定义一个字符型的数组buf用于存储用户的登录信息,然后使用memcpy函数将用户信息结构体user中的信息拷贝到buf中,最后,需要用send将buf中的用户信息复制到客户端套接字缓冲区中,服务器端接收到后会与服务器的数据库中的已注册用户信息进行比较,如果数据库中有相应的用户,则服务器会将此用户信息做一系列处理,比如加入到数据库的在线用户表中,然后给客户端返回一个登陆成功的信息,客户端在login函数中使用recv函数接受此信息,并将信息打印出来反馈给客户,表示客户已经连接成功,这里服务器返回一个字符串:login successfully!,所以我们在登录成功后会在客户端看到服务器返回的字符串。

     

    3. mysystem()

    1. 函数形式:void mysystem(int);
    2. 功能:客户端界面函数。提示用户输入不同的命令。
    3. 具体实现:

    客户端定义了三个主界面功能,分别是login,register,quit。

    其中,login表示用户登录,要求用户输入已经注册过的用户名和密码进行登录。如果没有注册的话,需要用户自己用register注册一个新的用户名和密码。register表示用户注册,进入后会要求用户输入相应的用户名和密码进行新用户的注册,如果服务器的数据库中没有注册过这个用户,就会将这个新用户的信息存储在用户索引数据库中。quit表示客户端用户退出。

    在用户使用login后,客户顿会进入另外一个界面,即用户登录二级界面,里面包括四个二级界面功能,分别是:show users_online, chat, goback, write off, file translation。 其中show users­_online表示显示在线用户列表,会将当前所有的在线用户打印到客户端的屏幕上,chat表示与其他客户端进行通信,goback对返回到上一级目录,write off会将当前登录用户注销,file translation表示文件传输功能,可以实现两个客户端之间的文件传输,或者可以将文件上传到服务器端。客户端使用这个功能后,会进入另外一个文件传输界面,主要包括一下功能:list,get,put,quit。其中list表示显示当前目录的所有文件,get表示从服务器获取文件,put表示传送文件到服务器端,quit表示离开当前界面。这里每一个界面功能都有一个相应的函数与之对应,如chat功能会对应一个函数,为chat函数,这样就可以实现界面与相应功能的连接。

     

    4_register()

    1. void _register(int);
    2. 功能:新用户注册函数。
    3. 具体实现:

    首先定义一个buf缓冲区,用于存储用户注册信息,之后由用户输入注册的用户名和密码,再使用send函数将用户名和密码发送到服务器端,和登录操作一样,服务器接收到用户的注册请求后会做相关的处理,然后返回一个套接字描述符给客户端,表示用户注册成功,之后客户端将这个文件描述符答应道屏幕上呈现给用户。

     

    5.文件传输函数---translation file();

    子函数1:int list_handler();

    功能:list指令函数,打印服务端的文件

    具体实现:

    判断用户输入的字符串是否为“list”,如果是那么就使用recv函数接受服务器端的文件列表,然后打印到客户端程序。

    子函数2int get_file(char *filename)

    功能:下载文件,传入文件名,文件不存在则报错

    具体实现:

    • 变量定义

    char buff[MAXSENDSIZE];   接受文件缓冲区

       Message msg;     消息结构体,存储用户输入命令

       int nrd = 0;      

       int nwr = 0;

       int fdwr;        套接字

       char flag;      

    • 流程

    首先使用access函数检测文件是否存在,如果存在进行下一步,使用open函数打开文件准备写入,发送指令。

     

    子函数3int put_file(char *filename)

    功能:向服务端发送文件

    具体实现:

    • 变量定义:

    char buff[MAXSENDSIZE];发送文件缓冲区

    Message msg;    发送的消息结构体、

    int fdrd;      文件描述符

    int nrd = 0;     

    • 流程:

    首先比对用户输入是否和put一致,若是一致,则进行下一步比对文件是否存在,不存在就退出,如果存在,进行下一步,使用open打开文件,将文件内容通过send发行到服务器具体代码见文件传输文件。


     

    【六】项目文件列表.文件功能说明及项目编译步骤

    • 服务器:server.c 

      1. 编译:在Linux环境下编译,需要引用mysqlclient和pthread库,因此编译命令为:

    gcc server.c -o server -lpthread -lmysqlclient1
    

      2. 执行时,MySQL可能需要root权限,故执行命令为:

    sudo ./server1
    

     

    • 客户端:client.c 

      1. 编译:仅有一个文件client.c,在当前目录下,故直接使用命令:

    gcc client.c -o client -lpthread1
    

      2. 进行编译.生成可执行文件:client, 再使用命令:

    ./client 

      3. 运行客户端程序即可.


     

    【七】项目演示步骤

    一、注册

    • 注册之前的用户信息表:

     

    • 客户端选择功能“2”,然后输入注册ID和密码。

     

    • 按下回车,可见结果已注册成功;服务器上也显示出了新建用户的信息,同时查看MySQL的表“user_info,可见已多出了用户“1618012”的信息。

     

     

     

    二、登录

    • 登录之前的”user_online”表:

     

     

     

    • 客户端登录:

     

     

    • 服务器上显示出了用户的信息,同时MySQL”user_online”表里多出了用户”1618012”的信息

     

     

    三、聊天

    • 打开两个客户端,并同时选择‘chat’选项:

     

     

    • 发送消息如图:

     

     

    四、显示在线用户:

    1. MySQL中的”user_online”表:

     

    • 用户选择“show users_online”选项后:

     

    五、用户退出

    • 用户退出前的”user_online”表:

     

    • 用户选择主界面的‘quit’选项:

     

    • 退出后的”user_online”如图(退出的是1618012用户)

     

    六、用户注销

    • 用户注销前的”user_info”表:

     

    • 用户选择“注销“:

     

    • 注销后的”user_info”表(注销的是1992用户)

     

    七、文件传输:

    • 假设从客户端“client”给客户端“client1”传输文件。传输前文件夹里的文件列表如图:

     

     

    • 分别登录客户端、服务器,client选择“put”文件,client1选择“get”文件:

     

     

    • 点击回车后,结果如图:

    • 此时查看服务器状态如图,同时可见client1文件夹里已经有了a.txt文件。

     

     

     

    展开全文
  • Swoole 实现在线聊天

    千次阅读 2019-10-24 11:07:37
    Swoole 跟 thinkphp5 结合开发 WebSocket 在线聊天通讯系统 ThinkPHP 使用 Swoole 需要安装 think-swoole Composer 包,前提系统已经安装 好了 Swoole PECL 拓展 tp5 的项目根目录下执行 composer 命令安装 think...
  • 微信小程序开发交流qq群 173683895 承接微信小程序开发。... ---1兼容情况:1.1 正常聊天过一段时间 WebSocket 自动断开后重新链接,并且保存之前的聊天记录 ---1兼容情况:1.2 在用户黑屏但是没退出小程序过一...
  • 首先要获取一个聊天窗口,getConnection()获取连接connection的方法,调用getFriendChat()获取 [java] view plaincopyprint? private Map chatManage = new HashMap();// 聊天...
  • Android开发笔记(一百一十一)聊天室中的Socket通信

    千次阅读 热门讨论 2016-07-14 14:09:30
    计算机网络有个大名鼎鼎的TCP/IP协议,普通用户在电脑上设置本地连接的ip时,便经常看到下图的弹窗,注意红框部分已经很好地描述了TCP/IP协议的作用。 TCP/IP是个协议组,它分为三个层次:网络层、传输层和应用层...
  • Java实现Socket网络聊天

    千次阅读 2019-12-09 20:41:50
    最近在学Socket编程,为了巩固知识,简单实现了一个网络聊天室;目前只实现了个群聊功能,有时间继续更新和完善,下面附上代码截图,代码上都有详细的注释,如果有看不懂的地方,欢迎留言或私信我。 一、源代码...
  • 小程序即时通讯聊天控件(一)

    万次阅读 多人点赞 2017-11-06 12:21:52
    控件样式我们先来看下效果目前的功能就是动态图中展示的,我们可以使用这个控件来切换输入方式(文本或语音)、获取到输入的信息、取消语音输入、语音消息录制过短过长的判断(该接口暂时还未开放),支持发送图片和
  • 高德之微信聊天定位

    千次阅读 2018-01-23 15:32:41
     因为国内很多基于app使用到了定位功能,因此写一篇仿照微信聊天定位的博客供大家参考。此次定位主要选用高德api进行代码开发。也许小伙伴会问定位什么选用高德api 而不选用百度或者使用原生态android 定位功能!...
  • 以 DDD 领域驱动设计的分层模型结合 Netty,编写清晰简洁可扩展的框架结构,完成仿微信聊天核心功能项目开发。 使用 JavaFX 做 UI 窗体并且与业务代码分离的方式实现桌面版程序功能。在事件驱动与接口调用的方式中,...
  • 首先要获取一个聊天窗口,getConnection()获取连接connection的方法,调用getFriendChat()获取 private Map chatManage = new HashMap();// 聊天窗口管理map集合 /** * 获取或创建聊天窗口 * @par
  • 使用 Groovy 合并 MSN 聊天记录

    千次阅读 2011-05-05 15:57:00
    介绍如何用 Groovy 脚本考察简单的 xml 文件结构;合并 MSN 聊天记录;设置全自动的记录同步
  • Web端即时聊天项目实现(基于WebSocket)

    万次阅读 多人点赞 2018-02-28 16:06:53
    Web端即时聊天项目实现 项目背景  其实这个项目算是我做过的花时间最长也投入心血最多的一个项目了,当时决定开始做这个的时候我几乎什么都不会,那时我个人的情况是: - JavaEE方面: 会jsp+servlet,也简单...
  • Linux 嵌入式 C 实现简单的网络聊天室 简单功能如下 服务器端接收用户信息,处理后转发给其他用户,如有用户登录...保存和查看聊天记录,可查看自己保存的聊天记录,保存和读取聊天记录时需要使用文件锁; 保...
  • 第一个PWA程序-聊天

    万次阅读 多人点赞 2017-03-05 21:46:34
    来体验一下, 这里的聊天室功能我们主要使用了Google Firebase的推送功能, 所以在使用的过程中还需要你全程准备梯子~~ 对于暂时还没有梯子的朋友, 一下准备了两张截图, 先来大致了解一下. 在电脑上的运行效果: ...
  • Web网络聊天

    2017-03-19 21:28:25
    要做好网页聊天室,需要用到域对象,还记得九大内置对象吗? 域对象有四个,分别是pageContext、request、session、application 四个域对象的作用域: pageContext:在同一个页面有效 request:同一次请求(转发...
  • Pomelo聊天服务搭建(二)

    千次阅读 2018-08-11 17:11:39
    development和production启动时设置的环境,根据 pomelo start -e|-env development|production 中的参数,启动后选择不同的服务设置。 id:表示对应服务器的名字(同一类服务器命名应易于辨别) host:...
  • Android 基于XMPP Smack Openfire 单人聊天...首先要获取一个聊天窗口,getConnection()获取连接connection的方法,调用getFriendChat()获取 private Map chatManage = new HashMap();// 聊天窗口管理map集合 /**
  • iOS关于聊天消息提醒铃声

    千次阅读 2014-11-17 16:28:42
    今天让给聊天来信息的时候添加铃声提醒功能,百度了一下发现下面写的是最全的,刚开始不理解,以为还要通过方法判断一下当前设备的一个状态,花费了半个小时无功而返,最终在一个帖子下面发现了,原来下面这个方法是...
  • AIops 智能聊天机器人

    千次阅读 2019-12-19 22:47:42
    文章目录移动交流的核心问题知识图谱问答问答系统基础0. AIopsAIOPS需要三方面的知识运维... 智能客服的工作原理2.1 自然语言理解2.2 意图识别2.3 知识库2.4 知识图谱2.5 对话技术2.6 聊天机器人3. 整体架构4. 评...
  • 本人四年前开始上网,聊QQ,四年以来聊过上万个网友,和上千个网友有过电话邮件书信上的联系,见过上百个网友,加上我比较喜欢思考总结,所以有一些关于QQ聊天泡女的心得在这里写出来,QQ聊天有很多技巧,这个帖子就...
  • 很久没有写博客了,回顾了一下原先学习的,接着没有写完的继续写咯。(ps:告诉大家一个秘密,...首先要获取一个聊天窗口,getConnection()获取连接connection的方法,调用getFriendChat()获取 [java] vi
  • // 设置聊天室是持久聊天室,即将要被保存下来 submitForm.setAnswer( "muc#roomconfig_persistentroom" , true ); // 房间对成员开放 submitForm.setAnswer( "muc#roomconfig_membersonly" , false ); ...
  • AJAX聊天室实现原理 JQuery+PHP

    万次阅读 热门讨论 2009-05-30 13:31:00
    AJAX 聊天室实现原理终极解析闲来无事,做了一个AJAX聊天室,以前一直想做一个,因为我和几个朋友是Linux机子,尽管我们的机子上都有apache服务器,但要发送一个信息却不是很容易,老是要借助客户端,有时候吧Linux...
  • Java开源-Talk:一个聊天系统

    千次阅读 2016-12-26 10:00:30
    这是一个Java聊天系统,作为Java实验课的内容,目前已基本完成,支持如下功能: 群聊私聊消息提醒用户状态标记聊天记录保存表情支持 效果如下图: 这是私聊的界面,其中可以看到Master,表示群聊大厅,选中...
  • 比较突出的就是即时通讯在线聊天; 今个;我们要打造的就是类似于网页版微信的功能; 示例项目:https://github.com/baijunyao/thinkphp-bjyadmin 一:注册融云账号 如果我们不是以即时通讯主营业务;
  • 加密聊天软件(技术文档)

    千次阅读 2011-08-28 20:02:11
    一、 通信模型 1.1 信息传输 软件由两个主要部分组成:客户端和服务器端。两个客户端之间不是直接连接的,而是通过服务器沟通,一个...服务器对客户端接收的信息处理并派发给接收信息的客户端,服务器每个客户

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,610
精华内容 5,044
关键字:

如何判断设置为仅聊天