QtQuick 与 WebSocket 之 WebSocket 的压力测试



  • 大家好,我是萌萌哒新人~

    今天不说别的,就说说 WebSocket 吧。

    WebSocket 是一种应用层的协议,类似于 TCP 可以维持一个长连接,又可以像 http 协议那样,不要考虑包的问题(取决于实现)。

    由于我的软件工程实训是做一个论坛(功能删减许多呢)。客户端与服务端使用 WebSocket 进行通讯。

    但是没有进行压力测试。今天晚上刚好有空。就来了个简单的回写服务的压力测试。

    客户端开启大概 1300 个链接,隔 2 秒发送一个 1024 * 8 个字节的信息,服务端收到后再回写给客户端。

    以下为客户端:

    0_1466007010374_upload-70cca159-c603-4410-ab66-c2ceaac28585

    图中的方块为一个连接,数字为服务端回写信息的次数。

    运行大概 5 分钟后,有些链接就被服务端自动断开了。。

    这里的 WebSocket 的实现是 Tomcat 版本的,Tomact 版本是 8。



  • @qyvlik

    QML 的测试代码:

    import QtQuick 2.5
    import QtQuick.Controls 1.3
    import Qt.WebSockets 1.0
    
    Item {
        id: app
        visible: true
        width: 400
        height: 400
    
        Rectangle {
            anchors.fill: parent
        }
    
        Flow {
            id: flow
            anchors.fill: parent
        }
    
        Button {
            id: button
            text: "clinet: " +count
            property int count: 0
            anchors.centerIn: parent
            onClicked: {
    
                var step = 100
                while(step != 0) {
                    com.createObject(flow, {"count": count} )
                    count++;
                    step --;
                }
            }
        }
    
        Component {
            id: com
            Rectangle {
                id: root
                width: 50
                height: 50
                color: Qt.rgba(Math.random(), Math.random(), Math.random(),1)
                property int count: 0
                property int messageCount: 0
                Text {
                    anchors.centerIn: parent
                    text: parent.messageCount + ""
                    color: Qt.lighter(parent.color)
                }
    
                WebSocket {
                    id: websocket
                    // 开启wifi后的链接地址
                    url: "ws://192.168.253.1:8080/JavaWebSocket/websocket"
                    // url: "ws://localhost:8080/JavaWebSocket/websocket"
                    active: true
    
                    onStatusChanged: {
                        if(status == WebSocket.Error) {
                            console.log(websocket.errorString)
                            websocket.active = false;
                            root.destroy(1000)
                            button.count--;
                        }
                    }
                    onTextMessageReceived: {
                       // console.log(message)
                        root.messageCount++;
                    }
                }
                Timer {
                    repeat: true
                    interval: 2000 * Math.max(Math.random(), 0.9)
                    running: true
                    onTriggered: {
                        if(websocket.status == WebSocket.Open) {
                            websocket.sendTextMessage(bigMessage)
                        }
                    }
                }
            }
        }
    
    
        property string bigMessage: "";
        Component.onCompleted: {
            var step = 2048*4;
            while(step != 0) {
                bigMessage += "a"
                step --;
            }
    
            console.log(bigMessage.length)
    
        }
    
    }
    

    Java WebSocket 服务端:

    package org.gdpurjyfs.websocket;
    
    import java.io.IOException;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    import javax.websocket.CloseReason;
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    @ServerEndpoint("/websocket")
    public class WebSocketServer {
    	 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        private static int onlineCount = 0;
         
        //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
        private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
         
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
         
        /**
         * 连接建立成功调用的方法
         * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
         */
        @OnOpen
        public void onOpen(Session session){
            this.session = session;
            webSocketSet.add(this);     //加入set中
            addOnlineCount();           //在线数加1
            System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        }
         
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose(CloseReason reason){
            webSocketSet.remove(this);  //从set中删除
            subOnlineCount();           //在线数减1    
            System.out.println("关闭原因是:"+reason.getReasonPhrase());
            System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
        }
         
        /**
         * 收到客户端消息后调用的方法
         * @param message 客户端发送过来的消息
         * @param session 可选的参数
         */
        @OnMessage
        public void onMessage(String message, Session session) {    	
            // System.out.println("来自客户端的消息:" + message);
            try {
    			session.getBasicRemote().sendText(message);
    		} catch (IOException e1) {
    		}
            //群发消息
    //        for(WebSocketServer item: webSocketSet){             
    //            try {
    //                item.sendMessage(item.session.getId()+": "+message);
    //                
    //            } catch (IOException e) {
    //                e.printStackTrace();
    //                continue;
    //            }
    //        }
        }
         
        /**
         * 发生错误时调用
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error){
            System.out.println("发生错误");
            // webSocketSet.remove(this);  //从set中删除
            // subOnlineCount();           //在线数减1    
            // error.printStackTrace();
        }
             
        /**
         * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
         * @param message
         * @throws IOException
         */
        public void sendMessage(String message) throws IOException{
            this.session.getBasicRemote().sendText(message);
            //this.session.getAsyncRemote().sendText(message);
        }
     
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
     
        public static synchronized void addOnlineCount() {
        	WebSocketServer.onlineCount++;
        }
         
        public static synchronized void subOnlineCount() {
        	WebSocketServer.onlineCount--;
        }
    }
    


  • @qyvlik

    PS: 由于打算将项目从 Tomcat 的 WebSocket 移植到 Jetty 的 WebSocket 。但是我发现 Jetty 的 WebSocket 的 API 虽然很强大,但是每一个版本的 API 接口都不兼容,然后我在 MyEclipse 2014 + Jetty 7.x 以及 MyEclipse 2016 + Jetty 9.x 都无法将网上找到的 WebSocket 例子运行起来。其中 WebSocketServlet 在 Jetty 容器中加载时就会报错。(或许在 Jetty 加载 HttpServlet 子类需要另外设置一下?)


Log in to reply
 

走马观花

最近的回复

  • 诶 没有Linux吗??

    read more
  • 萌梦 男孩,女孩,和蛋

    menghome.png

    read more
  • 设计模式-工厂模式

    使用qt/qml来演示设计模式效果,便于学习理解

    1)定义创建对象的接口,封装对象的创建
    2)使具体化类的工作延迟到工厂子类中

    bg.png
    image.png

    1. 工厂类

    createProduct使用了参数来选择要创建哪个产品

    #ifndef FACTORY_H #define FACTORY_H #include <QObject> class Product; class QString; class Factory: public QObject { Q_OBJECT public: virtual ~Factory() = 0; virtual Product* createProduct(QString type) = 0; protected: Factory(); }; class ConcreteFactory: public Factory { Q_OBJECT public: ~ConcreteFactory(); ConcreteFactory(); public slots: Product* createProduct(QString type); }; #endif // FACTORY_H #include "factory.h" #include "product.h" #include <QtQml/qqml.h> Factory::~Factory() { } Factory::Factory() { qmlRegisterType<Product>("Product", 1, 0, "Product"); } ConcreteFactory::~ConcreteFactory() { } ConcreteFactory::ConcreteFactory() { } Product *ConcreteFactory::createProduct(QString type) { if(type == "boy") return static_cast<Product *>(new ConcreteProduct1()); else if(type == "girl") return static_cast<Product *>(new ConcreteProduct2()); return static_cast<Product *>(new ConcreteProduct1()); } 2 产品类

    一个产品是萌梦男,一个产品是萌梦女

    #ifndef PRODUCT_H #define PRODUCT_H #include <QObject> class Product: public QObject { Q_OBJECT public: virtual ~Product() = 0; Q_PROPERTY(QString icon READ icon NOTIFY iconChanged) QString m_icon; QString icon() const { return m_icon; } signals: void iconChanged(QString icon); protected: Product(); signals: public slots: }; class ConcreteProduct1: public Product { Q_OBJECT public: ~ConcreteProduct1(); ConcreteProduct1(); }; class ConcreteProduct2: public Product { Q_OBJECT public: ~ConcreteProduct2(); ConcreteProduct2(); }; #endif // PRODUCT_H #include "product.h" Product::~Product() { } Product::Product() { } ConcreteProduct1::~ConcreteProduct1() { } ConcreteProduct1::ConcreteProduct1() { m_icon = "qrc:/images/boy.png"; } ConcreteProduct2::~ConcreteProduct2() { } ConcreteProduct2::ConcreteProduct2() { m_icon = "qrc:/images/girl.png"; } 3. main.qml

    使用timer,canvas,listview等实现一个自动化生产的动画效果

    源代码

    Fork me on Gitee

    read more
  • blender建模 章鱼
    捕获2.PNG 😵

    read more

关注我们

微博
QQ群