精华内容
下载资源
问答
  • 打印message发送一条信息给所有 私人session-id message发送消息给特定的客户端 退出关闭服务器 结构 在该项目中只有2个班级 运行服务器并处理控制台命令的主类 连接,接收消息和断开连接时客户端的侦听器类
  • 创建 WebSocket 服务

    2019-07-22 18:22:10
    Web Socket 看完让你彻底搞懂Websocket原理

    1. pom配置

    添加spring-boot-starter-websocket起步依赖,继承spring-boot-starter-parent。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <groupId>com.tm.sbia</groupId>
        <version>1.0-SNAPSHOT</version>
        <artifactId>sbia-feature-websocket</artifactId>
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.5.RELEASE</version>
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
        </dependencies>
    
    </project>
    

    2. 创建WebSocketHandler

    接收Socket客户端传递的文本信息,打印日志,等待2s后向客户端发送信息polo!
    Socket连接发布和关闭时打印日志。

    package com.tm.sbia.feature.websocket;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.handler.AbstractWebSocketHandler;
    
    /**
     * 消息处理器
     */
    public class MarcoHandler extends AbstractWebSocketHandler {
        private Logger LOGGER = LoggerFactory.getLogger(MarcoHandler.class);
    
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            LOGGER.info("Received message: " + message.getPayload());
    
            Thread.sleep(2000);
    
            session.sendMessage(new TextMessage("polo!"));
        }
    
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            LOGGER.info("Connection established!");
        }
    
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
            LOGGER.info("Connection closed!");
        }
    }
    

    3. 在Java配置中启用WebSocket并映射消息处理器

    通过@EnableWebSocket启用WebSocket,注册路径/marco的处理器为MarcoHandler。

    package com.tm.sbia.feature.websocket;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    
    /**
     * WebSocket Handler configuration
     */
    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(marcoHandler(), "/marco");
        }
    
        @Bean
        public MarcoHandler marcoHandler() {
            return new MarcoHandler();
        }
    }
    

    4. 客户端实现

    客户端建立到WebSocket服务器的连接,发送文本Marco,收到信息后打印信息,然后重新发送Marco,总共发送10次。

      <!DOCTYPE html>
      <meta charset="utf-8" />
      <title>WebSocket Test</title>
      <script language="javascript" type="text/javascript">
    
      var wsUri = "ws://" + window.location.host + "/marco";
      var output;
      var count = 0;
    
      function init()
      {
        output = document.getElementById("output");
        testWebSocket();
      }
    
      function testWebSocket()
      {
        websocket = new WebSocket(wsUri);
        websocket.onopen = function(evt) { onOpen(evt) };
        websocket.onclose = function(evt) { onClose(evt) };
        websocket.onmessage = function(evt) { onMessage(evt) };
        websocket.onerror = function(evt) { onError(evt) };
      }
    
      function onOpen(evt)
      {
        writeToScreen("CONNECTED");
        doSend("Marco!");
      }
    
      function onClose(evt)
      {
        writeToScreen("DISCONNECTED");
      }
    
      function onMessage(evt)
      {
        writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
    
        if(count++ > 8) {
          websocket.close();
        } else {
          setTimeout(function(){doSend("Marco!")}, 2000);
        }
      }
    
      function onError(evt)
      {
        writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
      }
    
      function doSend(message)
      {
        writeToScreen("SENT: " + message);
        websocket.send(message);
      }
    
      function writeToScreen(message)
      {
        var pre = document.createElement("p");
        pre.style.wordWrap = "break-word";
        pre.innerHTML = message;
        output.appendChild(pre);
      }
    
      window.addEventListener("load", init, false);
    
      </script>
    
      <h2>WebSocket Test</h2>
    
      <div id="output"></div>
    

    5. 客户端访问

    浏览器发送upgrade请求,建立Socket连接,执行socket通信。
    在这里插入图片描述
    展示所有的messag信息。
    在这里插入图片描述

    参考

    看完让你彻底搞懂Websocket原理
    inspecting-websocket-traffic-with-chrome-developer-tools
    Echo Test
    Web sockets
    学习WebSocket协议—从顶层到底层的实现原理
    使用WebSocket和STOMP实现消息功能
    Using WebSocket to build an interactive web application
    Socket.IO打造基础聊天室
    SockJS实践:即时通信关键点
    java 实现websocket的两种方式
    github - sockjs-client#sockjs-family
    Tomcat的WebSocket实现

    展开全文
  • ESP32 的WebSocket 服务器

    2019-05-07 15:25:27
    ESP32模块通过WebSocket 连接服务器,服务器通过发送"OFF"打开灯,发送“ON”关闭灯,服务器来的数据打印出来,同时返回给服务器。
  • WebSocket服务器因某些原因无法正常工作(WebSocket server not working for some reasons)我尝试使用ws创建一个非常简单的服务器,当我运行服务器node index.js并且我在我的浏览器中午餐localhost:8080时,我的...

    WebSocket服务器因某些原因无法正常工作(WebSocket server not working for some reasons)

    我尝试使用ws创建一个非常简单的服务器,当我运行服务器node index.js并且我在我的浏览器中午餐localhost:8080时,我的控制台中没有任何内容。

    我应该看到client connected on localhost:8080打印到控制台

    -index.js

    const WebSocketServer = require('ws').Server;

    const wss = new WebSocketServer({port: 8080});

    const onConnect = wss => console.log('client connected on localhost:8080');

    Rx.Observable

    .fromEvent(wss, 'connection')

    .subscribe(onConnect);

    I tried to create a very simple server using ws, When i run the server node index.js and i lunch localhost:8080 in my browser nothing appear in my console.

    i should see client connected on localhost:8080 printed to the console

    -index.js

    const WebSocketServer = require('ws').Server;

    const wss = new WebSocketServer({port: 8080});

    const onConnect = wss => console.log('client connected on localhost:8080');

    Rx.Observable

    .fromEvent(wss, 'connection')

    .subscribe(onConnect);

    原文:https://stackoverflow.com/questions/37480475

    更新时间:2020-09-13 19:09

    最满意答案

    您无法通过直接在浏览器中打开它来连接到WebSocket。 您应该使用某个HTML页面创建HTTP服务器和响应。 在此HTML页面中,您应该包含连接到WebSocket服务器的javascript:

    var socket = new WebSocket("ws://localhost:8080");

    You can't connect to WebSocket by open it directly in a browser. You should create HTTP server and response with some HTML page. In this HTML page you should include javascript that connects to your WebSocket server:

    var socket = new WebSocket("ws://localhost:8080");

    相关问答

    为了证明接收到握手,服务器必须获取两条信息并将它们组合以形成响应。 第一条信息来自| Sec-WebSocket-Key | 客户端握手中的头字段: Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

    具体而言,如上例所示,| Sec-WebSocket-Key | 标题字段的值为“dGhlIHNhbXBsZSBub25jZQ ==”,服务器 将串联字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 形成字符串“dGhl

    ...

    我找到了解决方法。 我已经修改了我的wsgi.py,现在它可以工作: import os

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")

    # This application object is used by any WSGI server configured to use this

    # file. This includes Django's development server, if the WSGI

    ...

    好吧,就我而言, RewriteBase /元素解决了这个问题。 如果有人因为shauninmann视网膜代码而遇到这个问题,我就把它留在那里。

    Options -MultiViews

    RewriteEngine on

    RewriteBase /

    RewriteCond %{HTTP_COOKIE} HTTP_IS_RETINA [NC]

    RewriteCond %{REQUEST_FILENAME} !@2x

    RewriteRule ^(.*)\

    ...

    如果您的服务器正在侦听端口80上的连接,它是否在谈论http? 因为如果没有,不要在端口80上侦听:端口80已经建立为携带http流量。 下一步 - ipaddress和端口一起是端点的唯一标识符。 如果远程客户端通过端口80连接到您的服务器,而不是目标IP和端口,则没有其他信息表明网络层必须识别哪个应用程序(在端口80上侦听)应该获得该数据包。 鉴于配置多个IP地址非常困难 - 在NAT上是不可能的 - 将数据包路由到正确的侦听器的唯一信息就是端口。 所以你不能让两个应用程序在同一个端口上侦听。

    ...

    您无法通过直接在浏览器中打开它来连接到WebSocket。 您应该使用某个HTML页面创建HTTP服务器和响应。 在此HTML页面中,您应该包含连接到WebSocket服务器的javascript:

    var socket = new WebSocket("ws://localhost:8080");

    You can't connect to WebSocket by open it directly in a browser. You should crea

    ...

    所以我通过握手解决了我的特殊问题,而且非常无聊。 我需要两套“\ r \ n”才能完成握手。 所以为了解决我上面描述的握手问题(Javascript WebSocket没有进入OPEN状态)我需要对我的服务器端PHP进行以下更改(注意最后的\ r \ n \ r \ n,doh) : function dohandshake($user,$buffer){

    // getheaders and calcKey are confirmed working, can provide source

    ...

    是。 独立的WebSocket服务器通常可以在任何端口上运行。 浏览器客户端打开与非HTTP(S)端口上的服务器的WebSocket连接没有问题。 默认端口为80/443的主要原因是它们是最可靠的大规模使用端口,因为它们能够遍历阻止所有其他端口上所有流量的许多企业防火墙。 如果这对您的受众来说不是问题(或者您有基于HTTP的回退),那么为WebSocket服务器使用备用端口是完全合理的(并且更容易)。 另一种选择是使用80/443端口,但使用单独的IP地址/主机名。 Yes. A standalo

    ...

    Tyrus抱怨Connection: keep-alive, Upgrade header。 Firefox在这里没有做错任何事。 关于如何处理Connection标头,Tyrus过于严格,没有遵循WebSocket规范( RFC-6455 )。 RFC 4.1中的RFC规定: 6. The request MUST contain a |Connection| header field whose value

    MUST include the "Upgrade" tok

    ...

    说实话,我不能100%确定地说这是什么,但我有一个非常强烈的怀疑。 我的代码中包含了太多的命名空间,我相信在编译器等实际运行时会出现一些混乱。 显然,Microsoft.Web.Websockets和SignalR的命名空间都包含WebSocketHandler。 虽然我不知道SignalR的所有细节,但看起来THAT命名空间中的WebSocketHandler并不意味着在SignalR之外使用。 我相信这个类正在被引用,而不是Microsoft.Web.Websockets中的那个,因为它现在起

    ...

    您应该使用websocket处理程序,而不是请求处理程序,尝试使用此示例 You should use the websocket handler, not the request handler, try with this example

    展开全文
  • 通过websocket通知fastreport进行打印打印模板和数据由websocket消息进行传递
  • websocket

    2020-12-19 19:33:09
    websocket一、websocket简介二、flask-websocket的安装及初始化操作三、创建socket连接四、基于事件接受信息1、基于未定义事件进行通信2、基于自定义事件进行通信五、服务端响应信息六、基于房间管理分发信息七、...

    一、websocket简介

    一直以来,HTTP是无状态、单向通信的网络协议,即客户端请求一次,服务器回复一次,默认情况下,只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。如果想让服务器消息及时下发到客户端,需要采用类似于轮询的机制,大部分情况就是客户端通过定时器使用ajax频繁地向服务器发出请求。这样的做法效率很低,而且HTTP数据包头本身的字节量较大,浪费了大量带宽和服务器资源。

    为了提高效率,HTML5推出了WebSocket技术。

    WebScoket是一种让客户端和服务器之间能进行全双工通信(full-duplex)的技术。它是HTML最新标准HTML5的一个协议规范,本质上是个基于TCP的协议,它通过HTTP/HTTPS协议发送一条特殊的请求进行握手后创建了一个TCP连接,此后浏览器/客户端和服务器之间便可随时随地以通过此连接来进行双向实时通信,且交换的数据包头信息量很小。

    同时为了方便使用,HTML5提供了非常简单的操作就可以让前端开发者直接实现socket通讯,开发者只需要在支持WebSocket的浏览器中,创建Socket之后,通过onopen、onmessage、onclose、onerror四个事件的实现即可处理Socket的响应。

    注意:websocket是HTML5技术的一部分,但是websocket并非只能在浏览器或者HTML文档中才能使用,事实上在python或者C++等语言中只要能实现websocket协议报文,均可使用。

    客户端报文:

    GET /mofang/websocket HTTP/1.1
    Host: 127.0.0.1
    Origin: http://127.0.0.1:5000
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==      # Sec-WebSocket-Key 是随机生成的
    Sec-WebSocket-Version: 13
    

    服务端报文:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= # 结合客户端提供的Sec-WebSocket-Key基于固定算法计算出来的
    Sec-WebSocket-Protocol: chat
    

    在本文中,因为我们使用的是flask框架,所以我们使用flask-websocket

    • 注意:websocket存在版本对应关系,关系表如下:

    关系表

    二、flask-websocket的安装及初始化操作

    安装

    pip install flask-socketio
    pip install gevent-websocket
    

    初始化:

    在application/__init__文件中进行初始化操作-socketio(部分模块是项目其他模块,请忽略):

    from flask import Flask
    from flask_script import Manager
    from flask_sqlalchemy import SQLAlchemy
    from flask_redis import FlaskRedis
    from flask_session import Session
    from flask_migrate import Migrate,MigrateCommand
    from application.utils.logger import Log
    from flask_jsonrpc import JSONRPC
    from flask_marshmallow import Marshmallow
    from flask_jwt_extended import JWTManager
    from flask_admin import Admin
    from flask_babelex import Babel
    from faker import Faker
    from flask_pymongo import PyMongo
    from flask_qrcode import QRcode
    from flask_socketio import SocketIO
    import os,sys
    
    from application.utils.config import load_config
    from application.utils.session import init_session
    from application.utils.commands import load_command
    from application.utils import init_blueprint
    
    
    manager=Manager()
    
    db=SQLAlchemy()
    
    redis=FlaskRedis()
    
    session_store = Session()
    
    migrate=Migrate()
    
    log=Log()
    
    jwt=JWTManager()
    
    admin = Admin()
    babel = Babel()
    
    mongo=PyMongo()
    
    QRcode=QRcode()
    # 初始化jsonrpc模块
    jsonrpc = JSONRPC(service_url="/api")
    
    # 数据转换器的数据创建
    ma = Marshmallow()
    
    # socketio
    socketio = SocketIO()
    
    def init_app(config_path):
    	# 创建app应用对象
    	app = Flask(__name__)
    
    	# 项目根目录
    	app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    	# 加载配置
    	Config=load_config(config_path)
    	app.config.from_object(Config)
    
    	# session存储初始化
    	init_session(app)
    	session_store.init_app(app)
    
    	# 数据转换器初始化
    	ma.init_app(app)
    
    	# 数据库初始化
    	db.init_app(app)
    	redis.init_app(app)
    	app.db = db
    	mongo.init_app(app)
    
    	# 数据迁移初始化
    	migrate.init_app(app,db)
    	# 添加数据迁移命令到终端脚本工具中
    	manager.add_command("db",MigrateCommand)
    
    	# 日志初始化
    	app.log=log.init_app(app)
    
    	# 蓝图注册
    	init_blueprint(app)
    
    	#  初始化jsonrpc
    	# jsonrpc.service_url="/api"
    	jsonrpc.init_app(app)
    
    	# jwt初始化
    	jwt.init_app(app)
    
    	#初始化终端脚本工具
    	manager.app=app
    
    
    	# admin站点
    	admin.init_app(app)
    
    	# 项目语言
    	babel.init_app(app)
    
    	# qrcode初始化配置
    	QRcode.init_app(app)
    
    	# 数据种子生成器
    	app.faker = Faker(app.config.get('LANGUAGE'))
    
    	# socketio
    	socketio.init_app(app,cors_allowed_origins=app.config["CORS_ALLOWED_ORIGINS"],async_mode=app.config["ASYNC_MODE"],debug=app.config["DEBUG"])
    
    	# 改写runserver
    	if sys.argv[1] == "runserver":
    		manager.add_command("run",socketio.run(app,host=app.config["HOST"],port=app.config["PORT"]))
    
    	# 注册自定义命令
    	load_command(manager)
    
    	return manager
    
    

    如代码中所示,sockio的配置参数需要在在dev文件中取:

    	# socketio配置信息
    	DEBUG = True
    	CORS_ALLOWED_ORIGINS = "*"
    	ASYNC_MODE = None
    	HOST = "0.0.0.0"
    	PORT = 5000
    

    在加载蓝图的过程中,自动加载socket的api:

    from flask import Blueprint
    from importlib import import_module
    
    def init_blueprint(app):
    	# 自动注册蓝图
    	blueprint_path_list = app.config.get("INSTALLED_APPS")
    	print(blueprint_path_list)
    
    	# 加载admin站点总配置文件
    	try:
    		import_module(app.config.get("ADMIN_PATH"))
    	except:
    		pass
    
    	for blueprint_path in blueprint_path_list:
    		blueprint_name = blueprint_path.split(".")[-1]
    		# 自动创建蓝图对象
    		blueprint = Blueprint(blueprint_name,blueprint_path)
    		# 蓝图自动注册和绑定视图的子路由
    		url_module=import_module(blueprint_path+".urls") # 加载蓝图下子路由文件
    		for url in url_module.urlpatterns: #遍历子路由的所有路由关系
    			blueprint.add_url_rule(**url) #注册到蓝图下
    
    		# 读取路由总文件
    		url_path=app.config.get("URL_PATH")
    		urlpatterns = import_module(url_path).urlpatterns
    		url_prefix = ""
    		for urlpattern in urlpatterns:
    			if urlpattern["blueprint_path"] == blueprint_name + ".urls":
    				url_prefix = urlpattern["url_prefix"]
    				break
    
    		# 注册模型
    		import_module(blueprint_path + ".models")
    		try:
    			import_module(blueprint_path + ".admin")
    		except:
    			pass
    
    		# 加载蓝图内部的socket接口
    		try:
    			import_module(blueprint_path+".socket")
    		except:
    			pass
    
    		# 注册蓝图对象到app应用对象中
    		app.register_blueprint(blueprint,url_prefix=url_prefix)
    def path(rule,func_view):
    	# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
    	return {"rule":rule,"view_func":func_view}
    
    def include(url_prefix, blueprint_path):
    	return {"url_prefix":url_prefix,"blueprint_path":blueprint_path}
    
    

    生成蓝图目录:

    cd application/apps/
    python ../../manage.py blue -n=orchard
    

    dev文件中注册蓝图:

    # 注册蓝图
        INSTALLED_APPS = [
            "application.apps.home",
            "application.apps.users",
            "application.apps.marsh",
            "application.apps.orchard",
        ]
    

    前端我们使用vue,新建一个orchard.html文件作为测试html文件,因为我们是基于python-socketio模块提供的服务端,所以此文件中(客户端)必须引入socketIO.js才可通过js与服务端通信:

    <!DOCTYPE html>
    <html>
    <head>
    	<title>用户中心</title>
    	<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    	<meta charset="utf-8">
    	<link rel="stylesheet" href="../static/css/main.css">
    	<script src="../static/js/vue.js"></script>
    	<script src="../static/js/axios.js"></script>
    	<script src="../static/js/main.js"></script>
    	<script src="../static/js/uuid.js"></script>
    	<script src="../static/js/settings.js"></script>
    	<script src="../static/js/socket.io.js"></script>
    </head>
    <body>
    	<div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
    			<img src="../static/images/bg2.png">
    			<img class="board_bg2" src="../static/images/board_bg2.png">
    		</div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
    
    	</div>
    	<script>
    	apiready = function(){
    		init();
    		new Vue({
    			el:"#app",
    			data(){
    				return {
              music_play:true,
              namespace: '/mofang_orchard',
              token:"",
              socket: null,
              timeout: 0,
    					prev:{name:"",url:"",params:{}},
    					current:{name:"orchard",url:"orchard.html",params:{}},
    				}
    			},
          created(){
            this.checkout();
    
          },
    		methods:{
            go_index(){
              this.game.outWin("orchard");
            },
    			}
    		});
    	}
    	</script>
    </body>
    </html>
    

    css样式:

    .app .orchard-bg{
    	margin: 0 auto;
    	width: 100%;
    	max-width: 100rem;
    	position: absolute;;
    	z-index: -1;
      top: -6rem;
    }
    .app .orchard-bg .board_bg2{
      position: absolute;
      top: 1rem;
    }
    .orchard .back{
    	position: absolute;
    	width: 3.83rem;
    	height: 3.89rem;
      z-index: 1;
      top: 2rem;
      left: 2rem;
    }
    .orchard .music{
      right: 2rem;
    }
    .orchard .header{
      position: absolute;
      top: 0rem;
      left: 0;
      right: 0;
      margin: auto;
      width: 32rem;
      height: 19.28rem;
    }
    
    .orchard .info{
      position: absolute;
      z-index: 1;
      top: 0rem;
      left: 4.4rem;
      width: 8rem;
      height: 9.17rem;
    }
    .orchard .info .avata{
      width: 8rem;
      height: 8rem;
      position: relative;
    }
    .orchard .info .avatar_bf{
      position: absolute;
      z-index: 1;
      margin: auto;
      width: 6rem;
      height: 6rem;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
    }
    .orchard .info .user_avatar{
      position: absolute;
      z-index: 1;
      width: 6rem;
      height: 6rem;
      margin: auto;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      border-radius: 1rem;
    }
    .orchard .info .avatar_border{
      position: absolute;
      z-index: 1;
      margin: auto;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      width: 7.2rem;
      height: 7.2rem;
    }
    .orchard .info .user_name{
      position: absolute;
      left: 8rem;
      top: 1rem;
      width: 11rem;
      height: 3rem;
      line-height: 3rem;
      font-size: 1.5rem;
      text-shadow: 1px 1px 1px #aaa;
      border-radius: 3rem;
      background: #ff9900;
      text-align: center;
    }
    
    .orchard .wallet{
      position: absolute;
      top: 3.4rem;
      right: 4rem;
      width: 16rem;
      height: 10rem;
    }
    .orchard .wallet .balance{
      margin-top: 1.4rem;
      float: left;
      margin-right: 1rem;
    }
    .orchard .wallet .title{
      color: #fff;
      font-size: 1.2rem;
      width: 6.4rem;
      text-align: center;
    }
    .orchard .wallet .title img{
      width: 1.4rem;
      margin-right: 0.2rem;
      vertical-align: sub;
      height: 1.4rem;
    }
    .orchard .wallet .num{
      background: url("../images/btn3.png") no-repeat 0 0;
      background-size: 100%;
      width: 6.4rem;
      font-size: 0.8rem;
      color: #fff;
      height: 2rem;
      line-height: 1.8rem;
      text-indent: 1rem;
    }
    .orchard .header .menu-list{
      position: absolute;
      top: 9rem;
      left: 2rem;
    }
    .orchard .header .menu-list .menu{
      color: #fff;
      font-size: 1rem;
      float: left;
      width: 4rem;
      height: 4rem;
      text-align: center;
      margin-right: 2rem;
    }
    .orchard .header .menu-list .menu img{
      width: 3.33rem;
      height: 3.61rem;
      display: block;
      margin: auto;
      margin-bottom: 0.4rem;
    }
    .orchard .footer{
      position: absolute;
      width: 100%;
      height: 6rem;
      bottom: -2rem;
      background: url("../images/board_bg3.png") no-repeat -1rem 0;
      background-size: 110%;
    }
    .orchard .footer .menu-list{
      width: 100%;
      height: 4rem;
      display: flex;
      position: absolute;
      top: -1rem;
    }
    .orchard .footer .menu-list .menu,
    .orchard .footer .menu-list .menu-center{
      float: left;
      width: 4.44rem;
      height: 5.2rem;
      font-size: 1.5rem;
      color: #fff;
      line-height: 4.44rem;
      text-align: center;
      background: url("../images/btn5.png") no-repeat 0 0;
      background-size: 100%;
      flex: 1;
      margin-left: 4px;
      margin-right: 4px;
    }
    .orchard .footer .menu-list .menu-center{
      background: url("../images/btn6.png") no-repeat 0 0;
      background-size: 100%;
      flex: 2;
    }
    

    三、创建socket连接

    先在后端蓝图下面创建socket.py文件,并提供连接接口, orchard/socket.py:

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    
    @socketio.on("connect",namespace="/mofang")
    def user_connect():
    	# request.sid socketIO基于客户端生成唯一会话id
    	print("用户%s连接过来了" % request.sid)
    
    @socketio.on("disconnect",namespace="/mofang")
    def user_disconnect():
    	print("用户%s离开了" % request.sid)
    

    前端vue连接后端socketio,我们只截取相关函数:

    checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
    connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                    })
                },
    

    注意:checkout函数为token检测机制,checkout函数需要使用created方法挂载才能使页面加载时自动执行相关函数,这里不再展示

    效果展示:
    app登陆后进入相应界面:

    服务端:
    进入

    离开相应界面:

    服务端:
    离开
    如图,连接是成功的。

    四、基于事件接受信息

    1、基于未定义事件进行通信

    客户端:

    js可通过通过send方法可以直接发送数据给后端,数据格式为json格式

    checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
    connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                            this.login()
                    })
                },
                login(){
                    var id = this.game.get("id")
                    this.socket.send({"uid":id})
                },
    

    服务端:

    后端通过形参来接收数据

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    
    
    @socketio.on("message",namespace="/mofang")
    def user_message(data):
    	id = data.get("uid")
    	print("用户id为%s" % id)
    

    后端输出结果:
    接收

    2、基于自定义事件进行通信

    客户端:

    前端通过emit方法可触发自定义事件进行通信

    checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
    connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                            this.login()
                    })
                },
                login(){
                    var id = this.game.get("id")
                    this.socket.emit("login",{"uid":id})
                },
    

    服务端:
    服务端装饰器第一个参数需和emit方法定义的事件名一致

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    
    
    @socketio.on("login",namespace="/mofang")
    def user_message(data):
    	id = data.get("uid")
    	print("自定义通信用户id为%s" % id)
    

    五、服务端响应信息

    服务端:
    服务端使用emit方法向客户端响应数据:

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    
    
    @socketio.on("login",namespace="/mofang")
    def user_message(data):
    	id = data.get("uid")
    	print("自定义通信用户id为%s" % id)
    	# 主动响应数据给客户端(例:从数据库中统计所有用户数量返回给客户端)
    	count = User.query.count()
    	print(count)
    	socketio.emit("server_response",{"count":count},namespace="/mofang")
    

    客户端:
    客户端通过对应事件名来接收服务端传过来的数据

    checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
    connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                            this.login();
                            this.get_count()
                    })
                },
       login(){
                    var id = this.game.get("id")
                    this.socket.emit("login",{"uid":id})
                },
    get_count(){
                    this.socket.on("server_response",(res)=>{
                        this.game.print(res.count);
                        alert(`共有${res.count}人在种植园忙碌着~`)
                    });
                },
    

    app展示效果:

    app

    六、基于房间管理分发信息

    使用room方法,可通过一定条件,如用户id,将不同的响应信息私有化分发给对应用户,不会进行广播操作

    服务端:

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    
    
    @socketio.on("login",namespace="/mofang")
    def user_message(data):
    	id = data.get("uid")
    	print("自定义通信用户id为%s" % id)
    	# 基于用户id分配不同房间
    	join_room(id)
    	socketio.emit("server_response",{"login":"登陆成功"},namespace="/mofang",room=id)
    

    客户端:

    checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
     connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                            this.login();
                            this.get_count()
                    })
                },
        login(){
                    var id = this.game.get("id")
                    this.socket.emit("login",{"uid":id})
                },
    get_count(){
                    this.socket.on("server_response",(res)=>{
                        this.game.print(res.login);
                        alert(res.login)
                    });
                },
    

    七、服务端定时推送数据

    我们需要开启一个线程来让数据定时变化,使用random模块每隔一秒页面打印随机数

    服务端:

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    from threading import Lock
    import random
    
    
    thread = None
    thread_lock=Lock()
    
    @socketio.on("login",namespace="/mofang")
    def user_message(data):
    	global thread
    	with thread_lock:
    		if thread is None:
    			thread = socketio.start_background_task(target=background_thread)
    def background_thread():
    	while True:
    		# 定时1秒
    		socketio.sleep(1)
    		count=random.randint(1,100)
    		socketio.emit("server_response",{"count":count},namespace="/mofang")
    

    客户端:

    因为需要vue数据驱动视图,故将前端代码截全

    <!DOCTYPE html>
    <html>
    <head>
    	<title>用户中心</title>
    	<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    	<meta charset="utf-8">
    	<link rel="stylesheet" href="../static/css/main.css">
    	<script src="../static/js/vue.js"></script>
    	<script src="../static/js/axios.js"></script>
    	<script src="../static/js/main.js"></script>
    	<script src="../static/js/uuid.js"></script>
    	<script src="../static/js/settings.js"></script>
    	<script src="../static/js/socket.io.js"></script>
    </head>
    <body>
    	<div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
    			<img src="../static/images/bg2.png">
    			<img class="board_bg2" src="../static/images/board_bg2.png">
    		</div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <h1 style="position:absolute;top:20rem;">{{num}}</h1>
    
    	</div>
    	<script>
    	apiready = function(){
    		init();
    		new Vue({
    			el:"#app",
    			data(){
    				return {
              music_play:true,
              token:"",
              num:"",
              socket: null,
              timeout: 0,
    					prev:{name:"",url:"",params:{}},
    					current:{name:"orchard",url:"orchard.html",params:{}},
    				}
                },
            created(){
                this.checkout()
            },
    		methods:{
                checkout(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                    this.game.checkout(this,token,(new_access_token)=>{
                        this.connect();
                    })
                },
                connect(){
                    // socket连接
                    this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace,{transports:["websocket"]});
                    this.socket.on("connect",()=>{  
                            this.game.print("开始连接服务器");
                            this.login();
                            this.get_count()
                    })
                },
                login(){
                    var id = this.game.get("id")
                    this.socket.emit("login",{"uid":id})
                },
                get_count(){
                    this.socket.on("server_response",(res)=>{
                        
                        this.num = res.count
                    });
                },
                go_index(){
                    this.game.outWin("orchard")
                }
    			}
    		});
    	}
    	</script>
    </body>
    </html>
    

    app展示效果:

    每隔1秒h1标签中的数字会发生变化,此处我们截不同时刻的两张图

    app1
    app2

    展开全文
  • Netty案例介绍(websocket服务)

    千次阅读 2019-12-30 23:13:04
      本文我们来实现一个基于WebSocket协议的案例。 WebSocket案例 1.需求分析   Http协议是无状态的, 浏览器和服务器间的请求响应一次,下一次会重新创建连接.所有在有些情况下并不是太适用。这时websocket就是...

      本文我们来实现一个基于WebSocket协议的案例。

    WebSocket案例

    1.需求分析

      Http协议是无状态的, 浏览器和服务器间的请求响应一次,下一次会重新创建连接.所有在有些情况下并不是太适用。这时websocket就是我们的一种实现方案,具体的websocket的内容网上很多,自行查阅哦,本文主要是介绍基于netty如何实现websocket通信。

    要求

    1. 实现基于webSocket的长连接的全双工的交互
    2. 改变Http协议多次请求的约束,实现长连接了, 服务器可以发送消息给浏览器
    3. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知

    2.案例代码实现

    2.1 服务端处理器

      服务器处理器中我们仅仅需要处理请求和客户端连接和端口的情况,具体如下:

    package com.dpb.netty.websocket;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
    
    import java.time.LocalDateTime;
    
    
    /**
     * @program: netty4demo
     * @description: 处理器
     * @author: 波波烤鸭
     * @create: 2019-12-30 22:39
     */
    public class SocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
            // 打印接收到的消息
            System.out.println("服务端接受到的消息:" + textWebSocketFrame.text());
            // 返回消息给客户端
            channelHandlerContext.writeAndFlush(new TextWebSocketFrame("服务器时间: " + LocalDateTime.now() + "  : " + textWebSocketFrame.text()));
        }
    
        /**
         * 客户端连接的时候触发
         * @param ctx
         * @throws Exception
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            // LongText() 唯一的  ShortText() 不唯一
            System.out.println("handlerAdded:" + ctx.channel().id().asLongText());
            System.out.println("handlerAdded:" + ctx.channel().id().asShortText());
        }
    
        /**
         * 客户端断开连接的时候触发
         * @param ctx
         * @throws Exception
         */
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            System.out.println("handlerRemoved:" + ctx.channel().id().asLongText());
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("异常发生了...");
            ctx.close();
        }
    }
    
    

    2.2 服务端

      服务端代码在原有的基础上需要转换相关的协议,具体如下:

    package com.dpb.netty.websocket;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.ServerSocketChannel;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpServerCodec;
    import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
    import io.netty.handler.logging.LogLevel;
    import io.netty.handler.logging.LoggingHandler;
    import io.netty.handler.stream.ChunkedWriteHandler;
    
    import java.net.ServerSocket;
    
    /**
     * @program: netty4demo
     * @description: 基于WebSocket协议的服务端
     * @author: 波波烤鸭
     * @create: 2019-12-30 22:31
     */
    public class SocketServerDemo {
    
        public static void main(String[] args) throws Exception {
            // 1.创建对应的EventLoopGroup对象
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workGroup = new NioEventLoopGroup();
            ServerBootstrap bootstrap = new ServerBootstrap();
            try{
                bootstrap.group(bossGroup,workGroup)
                        .channel(NioServerSocketChannel.class)
                        .handler(new LoggingHandler(LogLevel.INFO))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                // websocket 相关的配置
                                ChannelPipeline pipeline = socketChannel.pipeline();
                                //因为基于http协议,使用http的编码和解码器
                                pipeline.addLast(new HttpServerCodec());
                                //是以块方式写,添加ChunkedWriteHandler处理器
                                pipeline.addLast(new ChunkedWriteHandler());
    
                                /*
                                说明
                                    1. http数据在传输过程中是分段, HttpObjectAggregator ,就是可以将多个段聚合
                                    2. 这就就是为什么,当浏览器发送大量数据时,就会发出多次http请求
                                 */
                                pipeline.addLast(new HttpObjectAggregator(8192));
                                /*
                                说明
                                    1. 对应websocket ,它的数据是以 帧(frame) 形式传递
                                    2. 可以看到WebSocketFrame 下面有六个子类
                                    3. 浏览器请求时 ws://localhost:8888/hello 表示请求的uri
                                    4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接
                                    5. 是通过一个 状态码 101
                                 */
                                pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                                // 自定义handler,处理业务逻辑
                                pipeline.addLast(new SocketServerHandler());
    
                            }
                        });
                System.out.println("服务启动了....");
                ChannelFuture future = bootstrap.bind(8888).sync();
                future.channel().closeFuture().sync();
            }finally {
                bossGroup.shutdownGracefully();
                workGroup.shutdownGracefully();
            }
        }
    }
    
    

    2.3 客户端实现

      在客户端中我们就简单的实现一个HTML页面,在其中发送websocket协议相关的请求,然后接受相关的消息,如下,注意注释

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>websocket案例测试</title>
    </head>
    <body>
    <script>
        var socket;
        //判断当前浏览器是否支持websocket
        if(window.WebSocket) {
            //go on
            socket = new WebSocket("ws://localhost:8888/hello");
            //相当于channelReado, ev 收到服务器端回送的消息
            socket.onmessage = function (ev) {
                var rt = document.getElementById("responseText");
                rt.value = rt.value + "\n" + ev.data;
            }
    
            //相当于连接开启(感知到连接开启)
            socket.onopen = function (ev) {
                var rt = document.getElementById("responseText");
                rt.value = "连接开启了.."
            }
    
            //相当于连接关闭(感知到连接关闭)
            socket.onclose = function (ev) {
    
                var rt = document.getElementById("responseText");
                rt.value = rt.value + "\n" + "连接关闭了.."
            }
        } else {
            alert("当前浏览器不支持websocket")
        }
    
        //发送消息到服务器
        function send(message) {
            if(!window.socket) { //先判断socket是否创建好
                return;
            }
            if(socket.readyState == WebSocket.OPEN) {
                //通过socket 发送消息
                socket.send(message)
            } else {
                alert("连接没有开启");
            }
        }
    </script>
    <form onsubmit="return false">
        <textarea name="message" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="发生消息" onclick="send(this.form.message.value)">
        <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
    </form>
    </body>
    </html>
    

    3.测试分析

      分别运行服务器和客户端,访问测试,如下:
    在这里插入图片描述

    在这里插入图片描述

    运行效果:
    在这里插入图片描述

    同时在第一次运行的时候打开调试环境:

    在这里插入图片描述

    好了,本文我们就介绍到此,感兴趣的可以自己运行下

    欢迎关注点赞O(∩_∩)O哈哈~

    展开全文
  • Node.js 创建 WebSocket 服务 一、什么是WebSocket WebSocket 是一种网络通信协议。WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。 它的最大特点就是,服务器可以主动向客户端推送信息...
  • Node构建WebSocket服务

    2019-09-29 18:34:13
    1,WebSocket和Http的区别? ... 我们希望的场景是这样的:服务端数据发生变化,主动向客户端推送最新信息,客户端也可以主动向...服务器推送技术WebSocket就出场了。 2,WebSocket 客户端提供的使用方法 //...
  • 最近项目中有端对端通信场景,实时性要求较高,考虑后选用了websocket 这一通信协议,本地做了个demo测试,跑的好好的,部署到测试服务器上,出现了客户端连接服务端时提示404的问题,下面来看下。 正文 问题描述 ...
  • WebSocket

    2019-04-25 18:40:55
    WebSocket的出现,使得浏览器具备了实时双向通信的能力。本文由浅入深,介绍了WebSocket如何建立连接、交换数据的细节,以及数据帧的格式。此外,还简要介绍了针对WebSocket的安全攻击,以及协议是如何抵御类似攻击...
  • 简单搭建WebSocket服务器

    万次阅读 2019-08-18 09:05:04
    下载nodejs-websocket依赖包 npm install nodejs-websocket 点击回车下载完成之后发现项目中并没有nodejs-websocket相关的文件夹 检查终端有没有报错,确保依赖包名称输入正确 检查是否存在package.json文件,...
  • websocket服务器创建是基于https服务基础上增加 下载 websocketd 将解压的websocketd 文件夹放置在没有空格的英文路径下 nginx.conf 配置 指定websocketd的路径 # HTTPS server server { ...
  • 本人想通过js测试一个websocket client段的处理,但是网上找了一圈没有找到简单好用的websocket servermock工具,于是想到了nodejs 查到了有nodejs-websocket这个模块可以实现,大致看了下发现使用也很简单。 ...
  • 今天介绍如何用Go语言创建WebSocket服务,文章的前两部分简要介绍了WebSocket协议以及用Go标准库如何创建WebSocket服务。第三部分...
  • 第三部分实践环节我们使用了gorilla/websocket库帮助我们快速构建WebSocket服务,它帮封装了使用Go标准库实现WebSocket服务相关的基础逻辑,让我们能从繁琐的底层代码中解脱出来,根据业务需求快速构建...
  • 1 WebSocket是啥 本段来自 菜鸟教程-WebSocketWebSocket 和HTTP一样,也是一种通讯协议,允许服务端主动向客户端推送数据。 在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久...
  • 对于企业生产用途,需要多个WebSocket服务器来实现性能和高可用性,需要了解WebSocket协议的负载平衡层,NGINX自1.3版本起支持WebSocket,并可作为反向代理,并进行WebSocket的负载平衡应用。(所有版本的NGINX Plus...
  • webSocket服务器创建 //创建对象,监听8082端口 $webSocket=new swoole_websocket_server("0.0.0.0",8082); /*当WebSocket客户端与服务器建立连接并完成握手后会回调此函数。 *函数原型:function onOpen(swoole_...
  • webSocket

    2019-06-27 16:06:00
    package com.websocket.socket; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; impo.....
  • 线上代码 websocket.php &lt;?.../** * Created by PhpStorm. * User: Administrator ...$ws = new swoole_websocket_server("0.0.0.0",9505); //on //open 建立连接 $ws 服务器 $r...

空空如也

空空如也

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

websocket打印服务