WebSocket 慢 - Java 和 JavaScript

2024-03-09

我正在处理我的世界插件的编码。 但现在我遇到了以下问题,我的 websocket 服务器响应非常非常慢。

这是我的 WebSocketClass(用于插件)

// 套接字服务器类

package me.mickerd.pcoc;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;

import org.bukkit.Bukkit;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

public class WebsocketServer extends WebSocketServer {
public static WebsocketServer s;

public WebsocketServer(int port) throws UnknownHostException {
    super(new InetSocketAddress(port));
}

public WebsocketServer(InetSocketAddress address) {
    super(address);
}

@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
    WebsocketSessionManager.getSessionManager().openSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
    Bukkit.getLogger().info(conn.getRemoteSocketAddress().getAddress().getHostName() + " has connected to the Websocket server!");
}

@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
    WebsocketSessionManager.getSessionManager().endSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
    Bukkit.getLogger().info(conn + " has disconnected form the Websocket server");
}

@Override
public void onMessage(WebSocket conn, String message) {
    Bukkit.getLogger().info("Recieve Websocket packet - " + conn + ":" + message);
    if (message.split(":")[0].equalsIgnoreCase("name")) {
        WebsocketSessionManager.getSessionManager().addSessionUsername(conn.getRemoteSocketAddress().getAddress().getHostAddress(), message.split(":")[1]);
    }
}

public static void runServer() throws InterruptedException, IOException {
    WebSocketImpl.DEBUG = true;
    int port = 8887;
    s = new WebsocketServer(port);
    s.start();
    Bukkit.getLogger().info("Websocket server started on port: " + s.getPort());
}

@Override
public void onError(WebSocket conn, Exception ex) {
    ex.printStackTrace();
    if (conn != null) {
        // some errors like port binding failed may not be assignable to a specific websocket
    }
}

public void sendToAll(String data) {
    Collection<WebSocket> con = connections();
    synchronized (con) {
        for (WebSocket c : con) {
            c.send(data);
        }
    }
}

public void sendData(WebsocketSession session, String data) {
    Collection<WebSocket> con = connections();
    synchronized (con) {
        for (WebSocket c : con) {
            if (c.getRemoteSocketAddress().getAddress().getHostAddress().equalsIgnoreCase(session.getHost())) {
                Bukkit.getLogger().info("Send data packet: " + data);
                c.send(data);
            }
        }
    }
}
}

这是我的 Javascript 接收器:

var sound = null;
var name = window.location
document.session.name.value = name

var text = document.session.name.value

var ws = new WebSocket("ws://" + window.location.hostname + ":8887/");

ws.onopen = function () {
        ws.send("name:" + delineate(text));
document.getElementById("title").innerHTML = "Welcome on the music server. Please hold this window open!";

};

ws.onmessage = function (evt) {
function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    // Then bind the event to the callback function.
    // There are several events for cross browser compatibility.
    script.onreadystatechange = callback;
    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}
if(evt.data == "stop"){
  sound.fadeOut(0, 3700);
} else {
sound = new Howl({
  urls: ['music/' + evt.data + '.ogg']
}).play();
console.log("playing music");
};
}

ws.onclose = function () {
    alert("Closed!");
};

ws.onerror = function (err) {
    alert("Error: " + err);
};

function delineate(str) {
    theleft = str.indexOf("=") + 1;
    theright = str.lastIndexOf("&");
    return (str.substring(theleft, theright));
}

反应非常慢,但服务器上的其他东西却快得令人难以置信!

有人可以帮忙吗?


您正在使用的 websocket 库发送数据阻塞,这意味着调用c.send(将阻塞直到发送帧。

有不同的方法可以解决这个问题,例如:

对每条消息使用单独的异步线程:

public void sendToAll(String data) {
    // Ferrybig - added bukkit async task
    Bukkit.getSchedular().runTaskAsynchronously(plugin, new Runnable(){
    @Override public void run(){
        Collection<WebSocket> con = connections();
        synchronized (con) {
            for (WebSocket c : con) {
                c.send(data);
            }
        }
    // Ferrybig - added bukkit async task
    }});
}

虽然这可以快速解决您的问题,但它不是一个干净的解决方案,因为大量发送消息意味着为了发送消息而创建了大量线程,即不要经常发送,或者参见下一个解决方案:

使用专用线程来发送消息:

使用专用线程发送消息是更好的解决方案,但它的代码量很大。

对于这个解决方案,我们需要执行以下操作:

  • 使用变量来存储需要发送给每个客户端的消息

    private final BlockingQueue<Pair<WebSocket,String>> messageQueue
                  = new LinkedBlockingDeque<>();
    

    我们使用一个阻塞队列来保存包含 Web 套接字和要发送的消息的 Pair 对象。虽然我们也可以使用 Map 中的 Map.Entry 类,但我选择使用 Pair,因为我们稍后可以稍微更改代码以使其根据优先级自动重新处理消息。我用于此答案的 Pair 类可以在以下位置找到:Java 中的 C++ Pair 相当于什么? https://stackoverflow.com/a/677248/1542723.

  • 使用专用线程发送消息

    我们现在有了传入消息的列表,现在我们可以在消息传入时对其进行处理。这可以通过创建一个阻塞的任务来完成messageQueue.take() https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html#take()。以下是此方法的快速实现:

    public class MessageProcessor extends BukkitRunnable {
    
        private BlockingQueue<Pair<WebSocket,String>> messageQueue;
    
        public MessageProcessor (BlockingQueue<Pair<WebSocket,String>> messageQueue) {
            this.messageQueue = messageQueue;
        }
    
        @Override 
        public void run() {
            try {
                Pair<WebSocket,String> next;
                while(true) {
                    next = messageQueue.take();
                    if(next.getFirst() == null) {
                        // Special condition, will be explained later
                        return; // Quit run method
                    }
                    // System.out.println("Send message to " + next.getFirst()); // DEBUG
                    next.getFirst().send(next.getSecond());
                }
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
                // Someone wanted to quit our thread, so we are quiting
            } finally {
                messageQueue.clear();
            }
        }
    }  
    

    上述类别有2个特殊条件,next.getFirst() == null and catch(InterruptedException e),当我们禁用插件退出任务时将使用这些。

    • 当 bukkit 启动时开始我们的专用任务

    我们需要在 bukkit 和我们的 Websocket 服务器启动时启动我们的任务,这样它就可以开始处理消息和发送数据。这在我们的onEnable()使用以下代码:

    新的 MessageProcessor (messageQueue).runTaskAsynchronously(this);

    • 停止专用任务

    当我们的插件被禁用时,我们需要确保专用任务停止,以防止 bukkit 发送垃圾邮件“This plugin is not properly shutting down its async tasks when it is being reloaded.”。这确实很容易做到,因为我们在上面为此制定了特殊条件。

    为此,我们将以下代码放入我们的onDisable():

    messageQueue.add(new Pair<>(null,null)); https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html#add(E)

    • 重写我们的方法以使用 messageQueue

    该过程的最后一步是重写sendToAll使用我们的队列的方法。这真的很容易做到,只需要我们更换 1 行即可。

    public void sendToAll(String data) {
        Collection<WebSocket> con = connections();
        synchronized (con) {
            for (WebSocket c : con) {
                messageQueue.add(new Pair<>(c,data)); // Ferrybig: Use messageQueue
            }
        }
    }
    

    也可以进行相同的小修改sendData方法,但不是我作为读者的练习而完成的。

旁注

A BlockingQueue设计时考虑了并发操作,并且不需要外部同步。

您可以选择使用BlockingQueue.offer() https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html#offer(E)代替BlockingQueue.add()因为事实上后者在列表已满时抛出异常,但第一个返回 false。

默认大小为LinkedBlockingDeque is 整数.MAX_VALUE https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#MAX_VALUE并且可以用它来改变构造函数 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/LinkedBlockingDeque.html#LinkedBlockingDeque(int).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WebSocket 慢 - Java 和 JavaScript 的相关文章

随机推荐

  • Java 项目中服务器和客户端包的共享资源

    我有一个 Java 项目 其中包含服务器包和客户端包 另外我还有一个图书馆包 我使用 eclipse 并将所有内容放入一个 Java 项目中 每个部分服务器 客户端和库都位于单独的包中 问题是当我导出时 所有内容都会添加到 Jar 文件中
  • 如何在 Azure Key Vault 中序列化和反序列化 PFX 证书?

    我有一堆字符串和 pfx 证书 我想将它们存储在 Azure Key Vault 中 只有允许的用户 应用程序才能获取它们 将字符串存储为 Secret 并不难 但是如何以可以检索它并反序列化为证书的方式序列化证书 X509证书2 http
  • Pandas:连接数据帧并保留重复索引

    我有两个数据框 我想将它们按列 axis 1 与内部联接连接起来 其中一个数据帧有一些重复的索引 但行不重复 我不想丢失其中的数据 df1 pd DataFrame a 1 b 2 a 1 b 3 a 2 b 4 columns a b s
  • Laravel - 计算目录的总大小?

    我试图获取 Laravel 中 directory 的总大小 到目前为止我只能使用 size 函数获取单个文件的大小 好像没有这方面的例子或者文档 任何对此的意见将不胜感激 谢谢 我认为你不能 Laravel 5 用途飞行系统 http f
  • 如何将负数转换为正数?

    如何在Python中将负数转换为正数 并保持积极的态度 gt gt gt n 42 gt gt gt n if you know n is negative 42 gt gt gt abs n for any n 42 不要忘记检查docs
  • 如何将枚举类型变量转换为字符串?

    如何使 printf 显示枚举类型变量的值 例如 typedef enum Linux Apple Windows OS type OS type myOS Linux 我需要的是类似的东西 printenum OS type My OS
  • CSS 弹跳线加载动画

    我正在尝试创建一个简单的加载动画 来回绘制一条线 但目前仅朝一个方向移动 一旦到达动画的中间 它就不会以相反的方向动画 这是我的CSS keyframes loader animation 0 width 0 49 width 100 50
  • 在TabLayout设计支持库中无法看到Tab Indicator

    我正在使用材料设计库并使用创建布局CoordinatorLayout 应用栏布局 RecyclerView and TabLayout 我的问题是我无法看到选项卡指示器Tablayout 下面是我正在使用的 xml 文件AppCompact
  • 错误的snakemake glob_wilcards 和 wildcard_constraints

    在我的 Snakemake 管道中 我试图检索正确的通配符 我研究过 wildcard constraints 和这个帖子 https stackoverflow com questions 66882849 snakemake how t
  • 使用 jersey 和 grizzly 启用 JSON

    我正在玩 Grizzly 托管的 Jersey 并且希望能够使用和生成 JSON 但是我在 get 请求中从服务器收到 500 并且 POST 中不支持媒体类型 我的服务器代码是 org glassfish jersey server Re
  • Safari 9.0无法播放存储服务器上的mp4视频

    以下视频链接无法用safari 9 0 最新版本 播放 但老版本的 safari chrome 和 firefox 都可以玩 http assets00 grou ps 0F2E3C wysiwyg files Videos saksuka
  • 执行迁移时 Npgsql 找不到 NpgsqlException

    当我做一个update database我得到的数据库发生错误 System Runtime Serialization SerializationException 类型不是 已解决成员 Npgsql NpgsqlException Np
  • Holo 主题可以与自定义标题栏一起使用吗?

    我有一个应用程序 它使用以下样式为应用程序主题绘制自定义标题栏 这并没有给我全息主题 所以我将其设置为parent android style Theme Holo 这会使应用程序崩溃并出现以下错误 E AndroidRuntime 204
  • 使用 CAMediaTimingFunction 计算时间 (t) 处的值

    在Cocoa Touch中 CAMediaTimingFunction代表四个控制点 它们指定定时函数的三次贝塞尔曲线 对于我正在编写的应用程序 我希望能够在任意时间 t 0 gt 1 提取所述贝塞尔曲线的结果 让我困惑的是 当我查找如何d
  • CBOW 与Skip-gram:为什么要颠倒上下文和目标词?

    In this https www tensorflow org versions r0 9 tutorials word2vec index html vector representations of words页面上 据说 skip
  • SQL Server 2005 Unicode字符串排序问题

    我有一张名为 Soum 的表 这张表有NVARCHAR 100 字段命名 Name 但排序依据name 那个错误的工作 请看图片 执行查询后 第一个红行排序错误 我不明白为什么这是错误的工作 我检查了字符是否相同 但红色行中的 字符是相同的
  • git:如何从远程分支获取并合并到本地分支?

    我分叉自 github com mantisbt mantisbt https github com mantisbt mantisbt 这导致 github com MYACCOUNT mantisbt 从我克隆它的地方并将分支 我感兴趣
  • 命名空间 .AspNetCore.Hosting 与 .Extensions.Hosting

    在我的 ASP NET Core 2 0 项目中 我一直在使用 Microsoft Extensions Hosting 命名空间中的 IHostingEnvironment 和 IApplicationLifetime 在 ASP NET
  • 如何在 C# 中将字符串数据作为 ZIP 存档上传到 FTP 服务器

    这是我的代码 我想导出 上传这个 dat文件以 zip 格式传输到 FTP 服务器 我尝试了很多但没有找到任何解决方案 任何人都可以帮助我解决这个问题 public string ExportVoid FileSetups fileSetu
  • WebSocket 慢 - Java 和 JavaScript

    我正在处理我的世界插件的编码 但现在我遇到了以下问题 我的 websocket 服务器响应非常非常慢 这是我的 WebSocketClass 用于插件 套接字服务器类 package me mickerd pcoc import java