精华内容
下载资源
问答
  • JS websocket 实战——好友聊天 原文地址:websocket 实战——好友聊天 还不了解 websocket 的同鞋,请先学习阮一峰老师的 WebSocket 教程 websocket websocket 在实际项目中有着很广的应用,如好友聊天,异步请求...

    JS websocket 实战——好友聊天

    原文地址:websocket 实战——好友聊天

    还不了解 websocket 的同鞋,请先学习阮一峰老师的 WebSocket 教程

    websocket

    1. websocket 在实际项目中有着很广的应用,如好友聊天,异步请求,react-hot-loader 的热更新等等

    2. 本文前端采用原生 WebSocket,后端采用 express-ws 库 实现聊天通信

    3. 后端 mongodb 数据存储采用 mongoose 操作,不了解的可以先看看 文档

    4. 聊天原理很简单,如下图:

    websocket 聊天原理图

    简单版本

    先撸个简单版本,能够实现用户与服务器之间的通信

    1. 前端:WsRequest 封装类
    class WsRequest {
      ws: WebSocket
    
      constructor(url: string) {
        this.ws = new WebSocket(url)
    
        this.initListeners()
      }
    
      initListeners() {
        this.ws.onopen = _event => {
          console.log('client connect')
        }
    
        this.ws.onmessage = event => {
          console.log(`来自服务器的信息:${event.data}`)
        }
    
        this.ws.onclose = _event => {
          console.log('client disconnect')
        }
      }
    
      send(content: string) {
        this.ws.send(content)
      }
    
      close() {
        this.ws.close()
      }
    }
    
    // 使用
    const ws = new WsRequest('your_websocket_url') // 如: ws://localhost:4000/ws
    ws.send('hello from user')
    
    1. 服务端:WsRouter 封装类,使用单例模式
    import expressWs, { Application, Options } from 'express-ws';
    import ws, { Data } from 'ws';
    import { Server as hServer } from 'http';
    import { Server as hsServer } from 'https';
    
    class WsRouter {
      static instance: WsRouter;
    
      wsServer: expressWs.Instance;
    
      clientMap: Map<string, ws>; // 保存所有连接的用户 id
    
      constructor(
        private path: string,
        private app: Application,
        private server?: hServer | hsServer,
        private options?: Options
      ) {
        this.wsServer = expressWs(this.app, this.server, this.options);
    
        this.app.ws(this.path, this.wsMiddleWare);
    
        this.clientMap = new Map();
      }
    
      static getInstance(path: string, app: Application, server?: hServer | hsServer, options: Options = {}) {
        if (!this.instance) {
          this.instance = new WsRouter(path, app, server, options);
        }
    
        return this.instance;
      }
    
      wsMiddleWare = (wServer: any, _req: any) => {
        this.clientMap.set(id, wServer);
    
        this.broadcast('hello from server'); // send data to users
    
        wServer.on('message', async (data: Data) => {
          console.log(`来自用户的信息:${data.toString()}`);
        });
    
        wServer.on('close', (closeCode: number) => {
          console.log(`a client has disconnected: ${closeCode}`);
        });
      }
    
      broadcast(data: Data) { // 全体广播
        this.clientMap.forEach((client: any) => {
          if (client.readyState === ws.OPEN) {
            client.send(data);
          }
        });
      }
    }
    
    export default WsRouter.getInstance;
    
    // 使用:bootstrap.ts
    const server = new InversifyExpressServer(container);
    // 注:本项目后端使用的是 [Inversify](https://github.com/inversify) 框架
    // 具体传的 private server?: hServer | hsServer 参数值,请类比改变
    server.setConfig((app: any) => WsRouter('/ws/:id', app))
    server.build().listen(4000);
    

    升级版本

    要实现好友通信,在前后端的 send 方法中,当然要指定 fromto 的用户

    再者,后台要记录发送的消息,也必须要有好友表的主键 friendId,表示为这两个人之间的消息

    1. mongoose 数据存储
    // user.ts
    const userSchema = new Schema(
      {
        name: { type: String, required: true, unique: true }
      }
    );
    
    export default model('User', userSchema);
    
    // friend.ts 两个用户之间的好友关系
    import { Schema, model, Types } from 'mongoose';
    
    const FriendSchema = new Schema(
      {
        user1: { type: Types.ObjectId, ref: 'User', required: true }, // user1Id < user2Id
        user2: { type: Types.ObjectId, ref: 'User', required: true }
      }
    );
    
    export default model('Friend', FriendSchema);
    
    // message.ts
    const MessageSchema = new Schema(
      {
        friend: { type: Types.ObjectId, ref: 'Friend', required: true }, // 关联 Friend 表
        from: String,
        to: String,
        content: String,
        type: { type: String, default: 'text' },
      }
    );
    
    export default model('Message', MessageSchema);
    
    1. 接口说明
    type msgType = 'text' | 'emoji' | 'file'
    
    interface IMessage {
      from: string
      to: string
      content: string
      type: msgType
    }
    
    1. 前端:WsRequest 封装类
    import { IMessage, msgType } from './interface'
    
    export default class WsRequest {
      ws: WebSocket
    
      constructor(url: string, private userId: string) {
        this.ws = new WebSocket(`${url}/${this.userId}`)
    
        this.initListeners()
      }
    
      initListeners() {
        this.ws.onopen = _event => {
          console.log('client connect')
        }
    
        this.ws.onmessage = event => {
          const msg: IMessage = JSON.parse(event.data)
    
          console.log(msg.content)
        }
    
        this.ws.onclose = _event => {
          console.log('client disconnect')
        }
      }
    
      // friendId 指 Friend Model 的 _id
      async send(friendId: string, content: string, receiverId: string, type: msgType = 'text') {
        const message: IMessage = { from: this.userId, to: receiverId, content, type }
    
        await this.ws.send(JSON.stringify({ friend: friendId, ...message }))
      }
    
      close() {
        this.ws.close()
      }
    }
    
    // 使用
    const ws = new WsRequest('your_websocket_url', 'your_user_id') // example: ws://localhost:4000/ws
    await wsRequest.send('Friend_model_id', '你好啊,jeffery', 'jeffery_id')
    
    1. 服务端:WsRouter 封装类,使用单例模式
    import expressWs, { Application, Options } from 'express-ws';
    import ws, { Data } from 'ws';
    import { Server as hServer } from 'http';
    import { Server as hsServer } from 'https';
    import Message, { IMessage } from 'models/message';
    import Friend from 'models/friend';
    
    class WsRouter {
      static instance: WsRouter;
    
      wsServer: expressWs.Instance;
    
      clientMap: Map<string, ws>; // 保存所有连接的用户 id
    
      constructor(
        private path: string,
        private app: Application,
        private server?: hServer | hsServer,
        private options?: Options
      ) {
        this.wsServer = expressWs(this.app, this.server, this.options);
    
        this.app.ws(this.path, this.wsMiddleWare);
    
        this.clientMap = new Map();
      }
    
      static getInstance(path: string, app: Application, server?: hServer | hsServer, options: Options = {}) {
        if (!this.instance) {
          this.instance = new WsRouter(path, app, server, options);
        }
    
        return this.instance;
      }
    
      wsMiddleWare = (wServer: any, req: any) => {
        const { id } = req.params; // 解析用户 id
    
        wServer.id = id;
        this.clientMap.set(id, wServer);
    
        wServer.on('message', async (data: Data) => {
          const message: IMessage = JSON.parse(data.toString());
    
          const { _id } = await new Message(message).save(); // 更新数据库
    
          this.sendMsgToClientById(message);
        });
    
        wServer.on('close', (closeCode: number) => {
          console.log(`a client has disconnected, closeCode: ${closeCode}`);
        });
      };
    
      sendMsgToClientById(message: IMessage) {
        const client: any = this.clientMap.get(message.to);
    
        if (client) {
          client!.send(JSON.stringify(message));
        }
      }
    
      broadcast(data: Data) {
        this.clientMap.forEach((client: any) => {
          if (client.readyState === ws.OPEN) {
            client.send(data);
          }
        });
      }
    }
    
    export default WsRouter.getInstance;
    
    // 使用:bootstrap.ts
    const server = new InversifyExpressServer(container);
    // 注:本项目后端使用的是 [Inversify](https://github.com/inversify) 框架
    // 具体传的 private server?: hServer | hsServer 参数值,请类比改变
    server.setConfig((app: any) => WsRouter('/ws/:id', app))
    server.build().listen(4000);
    
    1. 心跳检测

    参考:

    // 服务端
    wsMiddleWare = (wServer: any, req: any) => {
      const { id } = req.params;
    
      wServer.id = id;
      wServer.isAlive = true;
      this.clientMap.set(id, wServer);
    
      wServer.on('message', async (data: Data) => {...});
    
      wServer.on('pong', () => {
        wServer.isAlive = true;
      });
    }
    
    initHeartbeat(during: number = 10000) {
      return setInterval(() => {
        this.clientMap.forEach((client: any) => {
          if (!client.isAlive) {
            this.clientMap.delete(client.id);
    
            return client.terminate();
          }
    
          client.isAlive = false;
          client.ping(() => {});
        });
      }, during);
    }
    

    在线测试

    一、准备

    1. 为方便大家测试,可以访问线上的服务端接口: wss://qaapi.omyleon.com/ws,具体使用要附上用户 id,如:wss://qaapi.omyleon.com/ws/asdf...,参见 升级版本的 websocket

    2. 客户端:可以使用谷歌插件:Simple WebSocket Client;也可以访问在线项目,使用项目提供的客户端,具体参见:qa-app

    3. 使用线上的一对好友信息

    • friend: jeffery 与 testuser => _id: 5d1328295793f14020a979d5

    • jeffery => _id: 5d1327c65793f14020a979ca

    • testuser => _id: 5d1328065793f14020a979cf

    2019.11.15 更新

    线上项目数据已重置,请使用新的 id 测试

    friend: jeffery 与 testuser => _id: 5d1328295793f14020a979d5

    jeffery => _id: 5dce50cbb869af711db528f1

    testuser => _id: 5dce5119b869af711db528fc

    二、实际演示

    1. 打开 WebSocket Client 插件,输入测试链接,如下图:
    wss://qaapi.omyleon.com/ws/5d1327c65793f14020a979ca
    

    websocket-client

    1. 点击 Open,下方 Status 显示 Opened 表示连接成功

    connection-opened

    1. 发送消息,请根据 IMessage 接口来发送,当然还要附上 friendId,否则不能对应到相应的用户上
    {
      "friend": "5d1328295793f14020a979d5",
      "from": "5d1327c65793f14020a979ca",
      "to": "5d1328065793f14020a979cf",
      "content": "你好呀,testuser,这是通过 simple websocket client 发送的消息",
      "type": "text"
    }
    

    message-send

    1. 同时用 simple websocket client 连接另一个用户,会收到发来的消息
    wss://qaapi.omyleon.com/ws/5d1328065793f14020a979cf
    

    message-get

    1. 同理,在另一个 client 中改变 from 和 to,就能回复消息给对方
    {
      "friend": "5d1328295793f14020a979d5",
      "from": "5d1328065793f14020a979cf",
      "to": "5d1327c65793f14020a979ca",
      "content": "嗯嗯,收到了 jeffery,这也是通过 simple websocket client 发送的",
      "type": "text"
    }
    

    send-back

    1. 补充,在线上项目 qa-app 中,也是用的是个原理,只不过在显示时候做了解析,只显示了 content 字段,而在这个简易的测试客户端中没有做其他处理

    源码获取

    1. 前端:qa-app app/websocket/index.ts

    2. 后端:qa-app-server server/wsRouter/index.ts

    3. 线上地址,去看看 -> https://qa.omyleon.com

    展开全文
  • 先给toy 添加第一个好友 app 注意 friend_chat 的写法 - "friend_chat": str(chat_id.inserted_id) 追加 -toy_info["friend_list"].append(toy_add_user) toy_id = MDB.Toys.insert_one(toy_...

    项目 智能玩具


    一、需求分析
    1外形美观大方
    2功能:1语音的实时通讯
    2积极正面内容
    -播放音频(播放 父母选定的儿歌,故事,百科,英语)
    -语音识别 (通过语音识别内容名称 玩具可以主动点播内容)
    3陪孩子聊天
    4解答问题(十万个为什么)
    5家长通过玩具查看社交圈

     

    二、具体实施
    第一天:
    获取音频资源
    1从喜马拉雅采集幼教内容,存入文件
    分别获取封面/音乐 文件
    将数据写入数据库

    app 获取内容 /content_list
    -信息以列表的形式返回

    接口功能实现
    根据前端的需求返回相应的数据
    App /get_cover 获取内容的图片信息
    App /get_music 获取音频信息


    2用户的注册 、登录 、自动登录
    App /reg 注册用户
    App /login 用户登录 返回的用户信息
    App /auto_login 用户打开APP后自动登录 返回的用户信息

    3创建设备的DeviceKey(联图网创建二维码)
    保证唯一性
    -hashlib.md5((f"{uuid4()}{time.time()}{uuid4()}".encode("utf8")).hexdigest()

    第二天:
    App 绑定一个设备 扫描二维码,设备必须有一个符合我们数据库中的二维码
    设备 一旦和App进行绑定 由App创建玩具

    1扫码
    通过 {device_key:""} 去数据库中查找 是否有该二维码信息

    1.二维码扫描成功并且设备未进行绑定
    2.二维码扫描失败,扫描的条码不是设备库中存在的

    3.二维码扫描成功, 但设备已经进行绑定
    (还未实现)

    2绑定玩具
    按照数据库要求 构建玩具的基本信息

    先创建一个空的Chats 表
    -chat_id = MDB.Chats.insert_one({"user_list": [], "chat_list": []})


    1创建toy (结合数据库结构和API文档,获取信息)
    toy 绑定 user
    先给toy 添加第一个好友 app

    注意 friend_chat 的写法
    - "friend_chat": str(chat_id.inserted_id)


    追加
    -toy_info["friend_list"].append(toy_add_user)
    toy_id = MDB.Toys.insert_one(toy_info)

    2.User表数据补充
    app 也要同时 增加toy为好友

    user_add_toy = {
    "friend_id": toy_id.inserted_id, # toy_id str
    "friend_nick": toy_info.get("baby_name"), # baby_name
    "friend_remark": toy_info.get("toy_name"), # toy_name
    "friend_avatar": "toy.jpg", #
    "friend_chat": str(chat_id.inserted_id), # chat_id
    "friend_type": "toy" # 好友的类型 toy
    }

    添加到表结构中
    user_info["bind_toys"].append(str(toy_id.inserted_id))
    user_info["friend_list"].append(user_add_toy)

    整体修改数据
    # 修改 Users 的全部数据
    MDB.Users.update_one({"_id": ObjectId(user_id)}, {"$set": user_info})

    # Chats 数据也会变化 user_list 将 toy_id 和 user_id 加入
    MDB.Chats.update_one({"_id":chat_id.inserted_id}, {"$set": {"user_list": [user_id,str(toy_id.inserted_id)]}})


    数据结构

    3 toy_list 展示
    从toys 表中查找对应数据,返回(参考表中的数据)

     

    第三天:
    1 智能玩具WebSocket通讯
    Websocket 连接 OpenToy 开机
    按照格式 开启wevbsocket

    解决跨域问题
    App通讯 向toy 发送音乐
    ws://10.0.0.1:9528/app/<user_id>
    Toys通讯 接收App推送音乐
    ws://10.0.0.1:9528/toy/<toy_id>

    1 /get_qr 接口 获取QRCODE 图片
    -获取图片/音乐/二维码图片等原理是相通的
    2 /friend_list 查询好友列表

    3 /chat_list 查询聊天记录

    4 /app_uploader app 上传语音消息

    5 /toy_uploader toy 上传语音消息


    注意事项:
    前端发送请求所携带的数据 和后端响应的数据

    app/toy 上传语音消息 注意 文件格式的转化
    跨域问题的解决(安装包)

    获取数据--保存文件--转化格式--聊天记录--更新聊天记录--返回RET

     

    /get_qr 接口

    # 返回二维码信息图片
    @content_bp.route("/get_qr/<filename>",methods=["GET"])
    def get_qr(filename):
        qr_path=os.path.join(QRCODE_PATH,filename)
        return send_file(qr_path)

     /friend_list 接口          /chat_list 接口

    from bson import ObjectId
    from flask import Blueprint, request, jsonify
    
    from settings import MDB, RET
    
    friend_bp = Blueprint("friend_bp", __name__)
    
    
    @friend_bp.route("/friend_list", methods=["POST"])
    def friend_list():
        user_id = request.form.get("_id")
        user_info = MDB.Users.find_one({"_id": ObjectId(user_id)})
    
        RET["CODE"] = 0
        RET["MSG"] = "好友查询"
        RET["DATA"] = user_info.get("friend_list")
    
        print(user_info, "+++++++++")
        return jsonify(RET)
    
    
    @friend_bp.route("/chat_list", methods=["POST"])
    def chat_list():
        chat = request.form.to_dict()
        chat_window = MDB.Chats.find_one({"_id": ObjectId(chat.get("chat_id"))})
    
        RET["CODE"] = 0
        RET["MSG"] = "查询聊天记录"
        RET["DATA"] = chat_window.get("chat_list")
    
        return jsonify(RET)

    实现 /app_uploader 接口 App上传语音消息
    实现 /toy_uploader 接口 Toy上传语音消息

     

    import os
    import time
    
    from bson import ObjectId
    from flask import Blueprint, request, jsonify
    
    from settings import MDB, RET, CHAT_PATH
    
    uploader_bp = Blueprint("uploader_bp", __name__)
    
    
    @uploader_bp.route("/app_uploader", methods=["POST"])
    def app_uploader():
        to_user = request.form.get("to_user")
        from_user = request.form.get("user_id")
        reco_file = request.files.get("reco_file")
    
        reco_file_path = os.path.join(CHAT_PATH, reco_file.filename)
        # 保存的文件格式为amr
        reco_file.save(reco_file_path)
        # 转化格式
        os.system(f"ffmpeg -i {reco_file_path} {reco_file_path}.mp3")
    
        user_list = [to_user, from_user]
        # 聊天信息记录
        chat_info = {
            "from_user": from_user,
            "to_user": to_user,
            "chat": f"{reco_file.filename}.mp3",
            "createTime": time.time()
        }
        # 更新聊天数据
        MDB.Chats.update_one({"user_list": {"$all": user_list}}, {"$push": {"chat_list": chat_info}})
    
        RET["CODE"] = 0
        RET["MSG"] = "上传成功"
        RET["DATA"] = {
            "filename": f"{reco_file.filename}.mp3",
            "friend_type": "app"
        }
        return jsonify(RET)
    
    
    from uuid import uuid4
    
    
    @uploader_bp.route("/toy_uploader", methods=["POST"])
    def toy_uploader():
        to_user = request.form.get("to_user")
        from_user = request.form.get("user_id")
        reco_file = request.files.get("reco")
         # 与app_uploader 不同    文件名   blob   reco_file.filename  是文件类型
        # print(reco_file.filename, "++++++")
    
        filename = f"{uuid4()}.wav"
        reco_file_path = os.path.join(CHAT_PATH, filename)
    
        reco_file.save(reco_file_path)
    
        user_list = [to_user, from_user]
        # 聊天信息记录
        chat_info = {
            "from_user": from_user,
            "to_user": to_user,
            "chat": filename,
            "createTime": time.time()
        }
        # 更新聊天数据
        MDB.Chats.update_one({"user_list": {"$all": user_list}}, {"$push": {"chat_list": chat_info}})
    
        RET["CODE"] = 0
        RET["MSG"] = "上传成功"
        RET["DATA"] = {
            "filename": filename,
            "friend_type": "toy"
        }
        return jsonify(RET)

     wap_ws   websocket 连接

    import json
    from flask import Flask, request, render_template
    
    from geventwebsocket.handler import WebSocketHandler
    
    from geventwebsocket.server import WSGIServer
    
    from geventwebsocket.websocket import WebSocket
    
    ws_app = Flask(__name__)
    user_socket_dict = {}
    
    
    # 建立websocket连接
    @ws_app.route("/app/<user_id>")
    def app(user_id):
        # print(user_id,"user_id")
        # 获得wesocket 连接
        app_socket = request.environ.get("wsgi.websocket")  # type: WebSocket
        if app_socket:
            user_socket_dict[user_id] = app_socket
    
        while True:
            app_data = app_socket.receive()
            app_data_dict = json.loads(app_data)
            to_user = app_data_dict.get("to_user")
            usocket = user_socket_dict.get(to_user)
            try:
                usocket.send(app_data)
            except:
                continue
    
    
    @ws_app.route("/toy/<toy_id>")
    def toy(toy_id):
        # 获得wesocket 连接
        toy_socket = request.environ.get("wsgi.websocket")  # type: WebSocket
        if toy_socket:
            user_socket_dict[toy_id] = toy_socket
    
        while True:
            toy_data = toy_socket.receive()
            toy_data_dict = json.loads(toy_data)
            to_user = toy_data_dict.get("to_user")
            usocket = user_socket_dict.get(to_user)
            # usocket.send(toy_data)
            try:
                usocket.send(toy_data)
            except:
                continue
    
    
    @ws_app.route("/get_toy")
    def get_toy():
        return render_template("WebToy.html")
    
    
    if __name__ == '__main__':
        http_serve = WSGIServer(("0.0.0.0", 9528), ws_app, handler_class=WebSocketHandler)
        http_serve.serve_forever()

     

    转载于:https://www.cnblogs.com/XLHIT/p/11234397.html

    展开全文
  • 我是基于springboot+websocket,首先,简历websocket服务端,然后在页面进行调用,通过传参告诉服务端你要在哪个聊天室聊天,等等 ,具体的 看我的实现代码。这还是最基础的,中间可以一些消息件
  •  之前在写“一套简单的web即时通讯”,写到第三版的时候没什么思路,正好微信公众号看到一篇讲API交互加密,于是就自己搞了一套AES与RSA混合加密,无意中产生应用在WebSocket想法,好在思路都差不多,稍微改动一下...

    前言

      之前在写“一套简单的web即时通讯”,写到第三版的时候没什么思路,正好微信公众号看到一篇讲API交互加密,于是就自己搞了一套AES与RSA混合加密,无意中产生应用在WebSocket想法,好在思路都差不多,稍微改动一下就能实现,特意写这篇博客记录下来

      WebSocket是HTML5 开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于 TCP 传输协议,并复用 HTTP 的握手通道。

      1、建立连接,客户端通过 HTTP 请求与服务端协商升级协议。协议升级完成后,后续的数据交换则遵照 WebSocket 的协议。

      2、数据交换,建立连接后,后续的操作通过TCP协议以数据帧的格式传输交换。

      通过TCP进行数据交换,不像http请求,websocket数据交换在浏览器上使用开发者工具(F12)是看不到数据,但使用抓包软件仍然可以获取到这些TCP传输数据,例如Charles和Fiddler等,以明文的方式直接传输很容易被第三方监听,因此,我觉得是有必要的

      前面我们实现了一套AES与RSA混合加密(详情请戳:前后端API交互数据加密——AES与RSA混合加密完整实例),我们现在用它实现一下WebSocket数据加密

    思路、代码

      工具类我们直接用之前API加密那一套就行,操作也与之前的API加密类似,发送前加密、收到数据后解密再交给业务处理,有个地方要注意的是,我们在进行消息转发时,要用的是接收方的前端公钥进行加密

      按照我们目前的规则,项目启动后生成后端密钥对,访问登录页面时响应后端公钥给前端,前端保存到后端RSA公钥并存到sessionStorage,每个页面都引入头部head.html(使用thymeleaf的<script th:replace="head::static"></script>),在head里面获取前端RSA公钥密码、AES的key,并放到window,这些都跟之前的一样,不需要改变,需要改动的是下面这些:

      建立WebSocket连接时,将当前用户的前端公钥发送到后端,后端进行Map保存

     

    640?wx_fmt=png

     

      前端发送前加密

     

    640?wx_fmt=png

      后端收到后先解密

     

    640?wx_fmt=png

     

      后端发送之前先加密,这里要用消息接收方的前端公钥进行加密

     

    640?wx_fmt=png

     

      前端收到消息后先解密

     

    640?wx_fmt=png

     

      效果演示  

      按照程序流程:

      前端加密前、加密后

    640?wx_fmt=png 640?wx_fmt=png

      后端解密前、解密后

    640?wx_fmt=png

    640?wx_fmt=png

      后端加密前、加密后

    640?wx_fmt=png

    640?wx_fmt=png

      前端解密前、解密后

    640?wx_fmt=png

    640?wx_fmt=png

       完整页面演示:

      好友上线在线系统通知没有问题

    640?wx_fmt=gif

      聊天没有问题

    640?wx_fmt=gif

      后记

      WebSocket的加密就先记录到这里,其他的后面再补充

    展开全文
  • websocket

    2018-05-24 15:32:34
    上边的实例我们已经实现了连接或者断开服务器做记录了,但是我们要是想要推送消息怎么办,例如我们好友的QQ上线了,腾讯都会咳嗽一下来提醒我们有好友上线。下面我们来做一下这个功能功能。 发送给单个用户 io ....

    动态web

    在html5以前,web的设计上并没有考虑过动态,他一直是围绕着文档设计的,我们看以前比较老的网站,基本上都是某一刻用来显示单一的文档的,用户请求一次web页面,获取一个页面,但是随着时间的推移,人们想要web做更多的事情了,而不是简单的要显示文档,而javaScript一直处于开发人员推动web页面功能的发展中心。 
    Ajax无疑是动态Web页面的一个重大发展,他不再需要我们即使更新一点内容,也需要刷新整个页面了,但是有些方面,又体现了他的不足。如果从服务器请求数据,他固然号,但是如果服务器想要将数据推送到浏览器呢。Ajax技术无法很容易的支持将数据推送到客户,虽然可以,但是需要跨国很多的障碍才行,而且不同的浏览器工作方式也不同,例如IE和FireBox他们的内核就不一样,从而工作方式也不一样。 
    WebSocket是在对服务器和客户端之间实现双向通信问题的相应。他的思想是,从头开始,设计一个开发人员可以使用的标准以便以一直的方式创建应用程序,而不是通过复杂的,并不总能设置所有浏览器的工作。他的思想是Web服务器和浏览器之间保持持久打开,这就使得不管是服务器还是浏览器都可以在想要的时候推送数据。因为连接是持久的,所以数据的交换非常的快,也就成了实时的了。

    Socket.IO

    说了那么多,我们介绍一下正主,Socket.IO是Node.js的一个模块,他提供通过WebSocket进行通信的一种简单方式,WebSocket协议很复杂,但是Socket.IO提供了服务器和客户端双方的组件,所以只需要一个模块就可以给应用程序加入对WebSocket的支持。而且还能支持不同的浏览器。

    基础的Socket.IO

    Socket.IO既能在服务端也能在客户端工作,要使用它,必须将其添加到服务器端的JavaScript(Node.js)和客户端的JavaScript(JQuery)中,这是以为内通信通常是双向的,所以Sokcet.IO需要能在两边工作。

    var server = http.createServer(function (req,res){
        fs.readFile('./index.html',function(error,data){
            res.writeHead(200,{'Content-Type':'text/html'});
            res.end(data,'utf-8');
        });
    }).listen(3000,"127.0.0.1");
    console.log('Server running at http://127.0.0.1:3000/');
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    而且必须将Socket.IO库包含起来,才能加入Socket.IO的功能。

    var io = require('socket.io').listen(server);
    • 1

    然后加入一个事件来响应客户端到底是连接了,还是断开了。事件如下:

    io.sockets.on('connection',function(socket){
        console.log('User connected');
        socket.on('disconnect',function(){
            console.log('User disconnected');
        });
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    是不是觉得非常的简单,下面我们看一下完整的代码实现是如何实现的吧:

    简单Socket.IO应用

    新建app.js

    新建文件夹socket.io,在该文件夹下新建app.js,写如下代码:

    var http = require('http');
    var fs = require('fs');
    
    var server = http.createServer(function (req,res){
        fs.readFile('./index.html',function(error,data){
            res.writeHead(200,{'Content-Type':'text/html'});
            res.end(data,'utf-8');
        });
    }).listen(3000,"127.0.0.1");
    console.log('Server running at http://127.0.0.1:3000/');
    
    var io = require('socket.io').listen(server);
    
    io.sockets.on('connection',function(socket){
        console.log('User connected');
        socket.on('disconnect',function(){
            console.log('User disconnected');
        });
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    新建index.html

    新建index.html文件,代码如下:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Socket.IO Example</title>
      </head>
      <body>
        <h1>Socket.IO Example</h1>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          var socket = io.connect('http://127.0.0.1:3000');
        </script>
    
      </body>
    </html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    新建package.json

    新建package.json来引入模块。

    {
        "name":"socketio_example",
        "version":"4.13.2",
        "private":true,
        "dependencies":{
            "socket.io":"1.4.5"
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    版本号大家可以输入自己的nodejs -V,或者socket.io -v来查看自己的版本号。

    运行

    如果大家没有安装Socket.IO,可以运行如下代码,如果安装了,自动跳过这一步。 
    npm install socket.io 
    从终端运行如下命令安装模块 
    npm install 
    运行如下命令启动服务器 
    node app.js 
    打开浏览器,输入http://127.0.0.1:3000/,多打开几个页签,都输入该网址,再任意关闭一个页签,然后看看我们的cmd命令窗口是不是如下: 
    这里写图片描述 
    这里会详细的记录又多少个用于连接了,也有多少个用户断开连接了,这样就能统计我们网页的访问量了。

    从服务器发送数据到客户端

    上边的实例我们已经实现了连接或者断开服务器做记录了,但是我们要是想要推送消息怎么办,例如我们好友的QQ上线了,腾讯都会咳嗽一下来提醒我们有好友上线。下面我们来做一下这个功能功能。

    发送给单个用户

    io.sockets.on('connection',function(socket){
            socket.emit('message',{text:'你上线了'});
    });
    • 1
    • 2
    • 3

    发给所有用户

    io.sockets.on('connection',function(socket){
            socket.broadcast.emit('message',{'你的好某XXX上线了'});
    });
    
    • 1
    • 2
    • 3
    • 4

    无论是发送给单个用户还是所有用户,这个message是自己写的,但是要在客户端用,所以命名要注意。

    客户端

    在客户端我们可以添加侦听事件来接收数据。

    var socket = io.connect('http://127.0.0.1:3000');
    socket.on('message',function(data){
        alert(data.text);
    })
    • 1
    • 2
    • 3
    • 4

    通过这些功能,我们就在第一个例子的基础上,实现用户数量的统计。这里只需要在服务端设置一个变量,count,如果有一个上线,那么就数量+1,并通知所有用户,最新的在线人数。

    新建app.js

    var http = require('http');
    var fs = require('fs');
    var count = 0;
    
    var server = http.createServer(function (req,res){
        fs.readFile('./index.html',function(error,data){
            res.writeHead(200,{'Content-Type':'text/html'});
            res.end(data,'utf-8');
        });
    }).listen(3000,"127.0.0.1");
    console.log('Server running at http://127.0.0.1:3000/');
    
    var io = require('socket.io').listen(server);
    
    io.sockets.on('connection',function(socket){
        count++;
        console.log('User connected' + count + 'user(s) present');
        socket.emit('users',{number:count});
        socket.broadcast.emit('users',{number:count});
        socket.on('disconnect',function(){
            count--;
            console.log('User disconnected');
            socket.broadcast.emit('users',{number:count});  
        });
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    创建index.html文件

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Socket.IO Example</title>
      </head>
      <body>
        <h1>Socket.IO Example</h1>
        <h2>How many users are here?</h2>
        <p id="count"></p>
         <script src="http://***.***.**.***:9001/jquery.min.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          var socket = io.connect('http://127.0.0.1:3000');
          var count = document.getElementById('count');
          socket.on('users',function(data){
            console.log('Got update from the server');
            console.log('There are ' + data.number + 'users');
            count.innerHTML = data.number
          });
        </script>
    
      </body>
    </html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    创建package.json文件

    {
        "name":"socketio_example",
        "version":"4.13.2",
        "private":true,
        "dependencies":{
            "socket.io":"1.4.5"
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    安装模块npm install 
    启动服务器node app.js 
    打开浏览器,输入http://127.0.0.1:3000,可以看到如下图片: 
    一个用户连接 
    再打开一个连接http://127.0.0.1:3000,可以看到如下结果: 
    第二个连接 
    可以看到如果我们打开两个连接,那么两个页签都会显示当前又两个用户在线,如果关闭其中一个,我们可以看到又显示只有一个用户在线。

    将数据广播给客户端

    接下来我们来看看Socket.IO是如何实现客户端与客户端的通信呢。 
    要想实现该功能,首先需要客户端将消息发送到服务端,·然后服务端发送给除自己之外的其他客户。服务器将消息发送给客户端的方法在上一个例子中我们已经实现了,那么我们需要的是客户端把接收到的消息发送给服务器。 
    下边的代码思想是利用表单来实现的。

      <form id="message-form" action="#">
          <textarea id="message" rows="4" cols="30"></textarea>
          <input type="submit" value="Send message" />
        </form>
        <script src="http://***.***.***.**:9001/jquery.min.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          var socket = io.connect('http://127.0.0.1:3000');
          var message = document.getElementById('message');
          $(message.form).submit(function() {
            socket.emit('message', { text: message.value });
            return false;
          });
          socket.on('push message', function (data) {
            $('form').after('<p>' + data.text + '</p>');
          });
        </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实现的思想是,将JQuery和SocketIO库包含进来,只是浏览器拦截127.0.0.1:3000的服务,使用Jquery的submit方法加入侦听期,等候用户提交表单。 
    发送消息给Socket>IO服务器,文本区域的内容位消息发送。 
    添加return false ,防止表单在浏览器窗口提交。 
    在上边已经说过服务器如何广播消息,下边我们说一下客户端如何显示客户端发送的消息。

    socket.on('push message', function (data) {
            $('form').after('<p>' + data.text + '</p>');
          })
    • 1
    • 2
    • 3

    实例实现

    创建messaging的新文件夹 
    在文件夹下创建package.json文件,代码如下:

    {
        "name":"socketio_example",
        "version":"4.13.2",
        "private":true,
        "dependencies":{
            "socket.io":"1.4.5"
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    创建app.js文件,代码如下:

    var http = require('http');
    var fs = require('fs');
    
    var server = http.createServer(function (req,res){
        fs.readFile('./index.html',function(error,data){
            res.writeHead(200,{'Content-Type':'text/html'});
            res.end(data,'utf-8');
        });
    }).listen(3000,"127.0.0.1");
    console.log('Server running at http://127.0.0.1:3000/');
    
    var io = require('socket.io').listen(server);
    
    io.sockets.on('connection',function(socket){
        socket.on('message',function(data){
            socket.broadcast.emit('push message',data);
    
        });
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    创建index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>Socket.IO Example</title>
      </head>
      <body>
        <h1>Socket.IO Example</h1>
        <form id="message-form" action="#">
          <textarea id="message" rows="4" cols="30"></textarea>
          <input type="submit" value="Send message" />
        </form>
        <script src="http://222.222.124.77:9001/jquery.min.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          var socket = io.connect('http://127.0.0.1:3000');
          var message = document.getElementById('message');
          $(message.form).submit(function() {
            socket.emit('message', { text: message.value });
            return false;
          });
          socket.on('push message', function (data) {
            $('form').after('<p>' + data.text + '</p>');
          });
        </script>
      </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    加载模块npm install 
    启动服务器node app.js 
    然后打开浏览器的多个页签,都输入http://127.0.0.1:3000 

    展开全文
  • 借鉴文章所用websocket 第三方是:nv-websocket-client:2.2,而这里所用的是:Java-WebSocket:1.3.0核心代码如下:public abstract class MyWebSocketService extends Service implements IWebSocket {private static ...
  • 做项目的时候涉及到即时通信了,所以在 gladuo 的建议下看了一篇教程,【转】Tornado 搭建基于 WebSocket 的聊天服务,经过一番修改调试实现了功能,在此总结分享一下。按思路来聊:类似微信,点击用户可以进入一对...
  • 5.添加好友上线提醒和下线提醒的功能,当有好友上线或下线时自动通知所有其他在线人,不要刷新页面可看到实时在线用户列表。 效果图: 附录:中央技术储备仓库(Central Technique Reserve Repositor
  • WebSocket分享

    2019-04-12 12:52:26
    以下为我之前給组内分享的有关WebSocket的话题,略有删减,未完待续。。。 长期以来,在web领域中,若想让客户端与服务端交互,我们首(或者说唯一的)选的肯定是Http。而随着web应用的快速发展,数据的消费量和...
  • WebSocket应用

    2018-03-06 16:43:24
    来源:知乎Web领域的实时推送技术,也被称作Realtime...WebSocket简介谈到Web实时推送,就不得不说WebSocket。在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询(Polling)和Comet技术,Com...
  • Spring-websocket 传统的HTTP协议,一般通过向服务器发送请求,拉取数据实现半双工通信,...5.添加好友上线提醒和下线提醒的功能,当有好友上线或下线时自动通知所有其他在线人,不要刷新页面可看到实时在线用户列表。
  • WebSocket相关方法类

    热门讨论 2015-12-01 17:59:30
    WebSocket相较于socket更易用,可以使用WebSocket使Android客户端与服务器端进行长连接以便于实现实时交互,微信好友添加通知便可使用其实现
  • 模仿微信做的通讯软件,主要功能:一对一聊天、群聊天、添加好友、扫一扫添加好友、创建群聊、管理群成员、聊天支持文字,图片和语音、朋友圈功能,可发动文字,图片或图文形式的动态,好友可见
  • Springboot: MyWebsocket.javapackage ...import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.webso...
  • 添加好友 单聊 搜索好友 热门好友推荐 启动项目 开发环境:MongoDB,Node 8.5.0 +,Npm 5.3.0+ 产品环境:Redis,MongoDB,Node 8.5.0 +,Npm 5.3.0+ 启动客户端 $webchat cd client $client npm install -----安装...
  • 在pom文件中添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> java后台 @...
  • websocket 即时通讯案例 ,一对一聊天,多人聊天 java 版本
  • server: port: 8018 netty: #监听websocket连接的端口 port: 11111 #websocket连接地址 (此处要用电脑的ip,不然手机访问会出现问题) ws: ws://192.168.3.175:${netty.port}/ws 3.3.测试demo 3.3.1.消息内容实体类 ...
  • websocket实现分组聊天 spring框架整合websocket的教程已经很多了,其中的例子都是简单的多人聊天,可以将消息发给指定的人或者全部人。不过我想发的消息只有部分人得到,但又不想挨个儿去发送,那么分组就有必要了...
  • 即时通讯框架,基于websocket,整合springMVC,后台代码实现,及前端测试代码,
  • SpringCloud Gateway 集成 WebSocket服务

    千次阅读 热门讨论 2021-01-27 11:07:34
    分了很多模块子项目,包括基础服务的common模块(Redis、MongoDB、RocketMQ组件集成,UUID、DateUtil、解密工具类等),服务的提供者demo-provider,服务的消费者demo-consumer,websocket服务等,demo项目的结构...
  • NET WebSocket

    2019-09-12 17:46:56
    WebSocket是什么我就不多说了,去百度百科,搜索一下,他会比我写的更全面,更细致 这几天需要socket,踩了不少坑,所以把代码直接拿出来,让大家看看,话说net socket这方面的代码挺少的,一搜索都是java的,...
  • 3、设置cmd可以运行php文件 在“我的计算机->属性->高级系统设置->高级->环境变量”,在用户变量的PATH添加一条,指向php的路径(注意版本要一致),在环境变量里的Path也需要添加一条,跟上面一样 4、测试socket和php...
  • 一、socket协议的简介WebSocket是什么,有什么优点WebSocket是一个持久化的协议,这是相对于http非持久化来说的。应用层协议举个简单的例子,http1.0的生命周期是以request作为界定的,也就是一个request,一个...
  • 文章目录零、技术选型一、项目功能二、功能展示1、注册页面2、登录页面3、...好友列表请求地址请求方式请求示例响应示例4、获取非好友列表请求地址请求方式请求示例响应示例5、获取新朋友(好友请求)列表请求地址请求...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,674
精华内容 669
关键字:

websocket添加好友