【Android WebSocket】状态管理框架 - WebSocketGo

在安卓中使用WebSocket WebSocket是一种在Web浏览器和服务器之间进行实时双向通信的协议。与传统的HTTP请求-响应模型不同,WebSocket允许服务器主动向客户端推送数据,而不需要客户端发起请求。当然,发展到如今,WebSocket已经不再局限于Web浏览器了,也可以在移动应用、桌面应用上使用。 阅读详情

前言

为了方便大家更加了解故事的背景,顺便科普一下智能家居,想直奔WebSocket主题的童鞋可以直接第二章节。

智能家居,算是物联网的一个典型应用场景。什么是物联网呢,字面意思就是把众多物体连接起来组成一个网络,英文是Internet of things(IoT)。小到一个你的手机跟蓝牙耳机,大到一个城市的各个角落,究极形态就是“万物互联”。(为什么突然想起万佛朝宗 -_-!)

IoT其实不在意网络的协议,也不在意连接的到底是什么东东,就这种形态本身而言,就是IoT。其实这个概念其实很早就有了,在笔者上大学那会,不怕暴露年龄的说是在09到10年左右就听说过物联网这个词。还记得实验课的GPRS智能抄表系统么,你可能没有觉得多么高大上,不就是水表上插个SIM卡,定期把值发到客户端上么,再也不怕被人上门查水表啦~ 没错,这也是IoT的一种体现。

但这么多年,物联网但一直不温不火。至于为什么最近几年又被提出来呢,智能家居在其中扮演了很重要的作用。另外AI这把火也起到了推波助澜的作用,任何产品都要加上智能二字。所以有了AI + IoT,也就是AIOT。具体就不详述了,以后可以再单开一文详细介绍下物联网。

说回智能家居,智能家居是家庭为单位的物联网,简单说就是你可以通过家里中控或者App控制制家里的灯、插座、监控、电器等等。

文字算个球,一图解千愁,上架构图

简单解释一下,网关的作用是负责连接屋内的所有设备,网关和设备之间一般不会采用HTTP(个别单品除外),而会采用比如zigbee、lora等近场通信协议(或者直接用有线方式更稳定),因为功耗低,比较省电,你想家里如果装了几十个开关面板,总归每个月也能节约十几二十块钱的电费吧。当然最重要的原因还是因为方案也比较成熟。所以使用这类设备之前需要有个“组网”的操作,把设备组到网关上。如果断开了,网关就认为设备离线了。

网关会将设备的状态上报给云平台,云平台就将状态下发给客户端了。同样,客户端控制设备就是个反向的过程。当然如果以后5G普及了,设备直接通过5G网络连接云平台,因为5G功耗低、延迟低、速度快,就可以不需要网关了。

整体的架构还算是比较简单的,当然中间也有很多复杂的逻辑,涉及到比如设备状态、人员、权限、房屋的管理等等这里就不关注了。

在服务器下发设备状态的时候就涉及到了推送。因为智能家居有情景模式的概念,比如回家模式,执行一个请求,咔咔打开十几个灯,所以靠请求的返回值判断状态是不太现实的,只能依靠推送。

由于业务的特殊性,智能家居的推送需要有较高的实时性,比如用户打开了灯(无论在app上打开或者直接打开),app上的灯的状态都需要立即变成开。如果等个3、5秒状态再发生改变,这样的用户体验很不好。

我们最早使用的是某光推送,发现延迟严重,有时需要等十几秒才收到推送,毕竟适用场景毕竟不一样。所以决定自建长连接当推送。经过选型,决定采用本文的主角,WebSocket。

WebSocket状态管理现存的问题

市面上有很多现成的WebSocket连接库,比较著名的有Java-WebSocket,OkHttp也自带WebSocket支持。

最初因为项目内已经接入了OkHttp,所以直接使用了OkHttp。使用的方式很简单,熟悉OkHttp的童鞋应该很懂,

OkHttpClient client = OkHttpClient.Builder().build();
Request request = new Request.Builder().build();
client.newWebSocket(request, new WebSocketListener() {
    @Override
    public void onOpen(okhttp3.WebSocket webSocket, Response response) {}

    @Override
    public void onMessage(okhttp3.WebSocket webSocket, String text) {}

    @Override
    public void onClosed(okhttp3.WebSocket webSocket, int code, String reason) {}

    @Override
    public void onFailure(okhttp3.WebSocket webSocket, Throwable t, Response response) {}
});
复制代码 

调用方便,回调状态也很清晰。Java-Websocket也差不多类似,但总体来说有以下几个问题:

1. 心跳机制

无论OkHttp还是Java-WebSocket,都是有心跳机制的。但OkHttp的心跳间隔,也就是pingInterval,在创建client时候就已经固定了,是不支持中途调整的。(Jake大神还在github上回复过相关问题,意思是调用者不需要关注这些。但往往确实有这种需求的啊😢😢😢)

比如应用在前台的时候心跳可能稍微频繁点,但应用出于后台时出于省电的优化,心跳间隔可以设置的稍微长一点。如果需要调整心跳,需要创建新的client,断掉旧的,重新连接。有两种策略:

  • 先断旧的,再连新的。如果服务端的消息没有做缓存策略,在断掉和新连接的间隔可能会漏掉消息。
  • 先连新的,再断旧的。这要在一瞬时就会有两个连接连上服务端,服务端对于消息的状态可能会发生异常。

以上两种策略都需要客户端和服务端先约定好,服务端做额外处理,增加研发成本不说,也有增加出错概率。

2. 断掉重连

通常连接断掉有以下这么几种情况:

  • 客户端主动断(无需重连)
  • 服务端主动断(无需重连)
  • 客户端因为网络原因,比如手机断网,非主动段(需要重连)
  • 运营商因连接空闲导致的连接断开(需要重连)

关于最后一种情况,可以参见 移动端IM实践:实现Android版微信的智能心跳机制

对于重连,还需要考虑的问题是重连的时间间隔,如果手机网断了,不停地重连同样是失败,所以需要自己制定一个重连次数和时间间隔关系。

总之,重连的逻辑和策略是需要我们自己维护的。

3. 多线程

OkHttp中关于状态的回调发生在OkHttp创建的子线程中,根据状态我们需要发起重连。如果这时候我们的应用发生了前后台切换又要创建新连接,各种连接(重连)交织在一起,那真是一团乱麻。需要考虑的问题有:

  • 同时发起多个连接的情况,需要等上一个连接请求执行完,再根据请求发起下一个连接请求。上一个连接如果连上了,那下一个等待的连接请求就可以直接跳过了。
  • 连接请求的发生在不同线程中,状态的同步

解决方案

因为笔者项目内用的是OkHttp,所以最初的解决方案都是针对于OkHttp来做的。大致思路如下:

1. 心跳机制

1.1 OkHttp WebSocket心跳分析

前面说到过,OkHttp是不支持动态调整心跳的,那OkHttp是怎么维护心跳的呢,我们来分析一下它的源码:

分析源码,我们从调用入口client.newWebSocket开始:

// newWebSocket@OkHttpClient.java

/**
 * 内部使用RealWebSocket类来发起连接
 */
@Override public WebSocket newWebSocket(Request request, WebSocketListener listener) {
    // 注意这里的pingInterval,所以在创建RealWebSocket时就已经固定,没法再修改了。
    RealWebSocket webSocket = new RealWebSocket(request, listener, new Random(), pingInterval);
    webSocket.connect(this);
    return webSocket;
}
复制代码

// RealWebSocket@RealWebSocket.java

public RealWebSocket(Request request, WebSocketListener listener, Random random, long pingIntervalMillis) {
    this.originalRequest = request;
    this.listener = listener;
    this.random = random;
    this.pingIntervalMillis = pingIntervalMillis;
    // ...
    // 这里的writerRunnable是用来向服务端发送消息的,在后面详述
    this.writerRunnable = new Runnable() {
      @Override public void run() {
        try {
          while (writeOneFrame()) {
          }
        } catch (IOException e) {
          failWebSocket(e, null);
        }
      }
    };
  }
  
  public void connect(OkHttpClient client) {
    // WebSocket协议相关的header
    client = client.newBuilder()
        .eventListener(EventListener.NONE)
        .protocols(ONLY_HTTP1)
        .build();
    final Request request = originalRequest.newBuilder()
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", key)
        .header("Sec-WebSocket-Version", "13")
        .build();
    call = Internal.instance.newWebSocketCall(client, request);
    call.enqueue(new Callback() {
      @Override public void onResponse(Call call, Response response) {
        try {
          checkResponse(response);
        } catch (ProtocolException e) {
          failWebSocket(e, response);
          closeQuietly(response);
          return;
        }

        // Promote the HTTP streams into web socket streams.
        StreamAllocation streamAllocation = Internal.instance.streamAllocation(call);
        streamAllocation.noNewStreams(); // Prevent connection pooling!
        Streams streams = streamAllocation.connection().newWebSocketStreams(streamAllocation);

        // Process all web socket messages.
        try {
          // 连接成功,调用listern的onOpen
          listener.onOpen(RealWebSocket.this, response);
          String name = "OkHttp WebSocket " + request.url().redact();
          // 重点1:创建reader和writer,见下文
          initReaderAndWriter(name, streams);
          streamAllocation.connection().socket().setSoTimeout(0);
          // 重点2:轮询读
          loopReader();
        } catch (Exception e) {
          failWebSocket(e, null);
        }
      }

      @Override public void onFailure(Call call, IOException e) {
        failWebSocket(e, null);
      }
    });
  }
  
   public void initReaderAndWriter(String name, Streams streams) throws IOException {
    synchronized (this) {
      this.streams = streams;
      this.writer = new WebSocketWriter(streams.client, streams.sink, random);
      // 创建Scheduled线程池
      this.executor = new ScheduledThreadPoolExecutor(1, Util.threadFactory(name, false));
      if (pingIntervalMillis != 0) {
        // 利用线程池定期运行PingRunnable,越来越接近真像了
        executor.scheduleAtFixedRate(
            new PingRunnable(), pingIntervalMillis, pingIntervalMillis, MILLISECONDS);
      }
      if (!messageAndCloseQueue.isEmpty()) {
        runWriter(); // Send messages that were enqueued before we were connected.
      }
    }
    // 创建reader,用来读取消息
    reader = new WebSocketReader(streams.client, streams.source, this);
  }
复制代码 

重点看一下PingRunnable都做了什么

private final class PingRunnable implements Runnable {
    PingRunnable() {
    }

    @Override public void run() {
      // 发送ping frame
      writePingFrame();
    }
  }

  void writePingFrame() {
    WebSocketWriter writer;
    int failedPing;
    synchronized (this) {
      if (failed) return;
      writer = this.writer;
      // 判断是否在等待pong,如果在等待failedPing置为sentPingCount,否则置为-1
      failedPing = awaitingPong ? sentPingCount : -1;
      sentPingCount++;
      awaitingPong = true;
    }

    if (failedPing != -1) {
      // 运行到此处的条件是上一个pong还没有返回,报错退出
      failWebSocket(new SocketTimeoutException("sent ping but didn't receive pong within "
          + pingIntervalMillis + "ms (after " + (failedPing - 1) + " successful ping/pongs)"),
          null);
      return;
    }

    try {
      // 发送一个ping消息
      writer.writePing(ByteString.EMPTY);
    } catch (IOException e) {
      failWebSocket(e, null);
    }
  }
复制代码 

那么怎么判断有没有收到pong了,就需要用到reader了。我们来看一下reader里做了什么。

还记得前文重点2标记的loopReader么?

// loopReader@RealWebSocket.java

/** Receive frames until there are no more. Invoked only by the reader thread. */
public void loopReader() throws IOException {
    while (receivedCloseCode == -1) {
        // This method call results in one or more onRead* methods being called on this thread.
        // 处理收到的frame
        reader.processNextFrame();
    }
}

/**
 * 根据header判断是消息帧还是控制帧,心跳pong属于控制帧
 */
void processNextFrame() throws IOException {
    readHeader();
    if (isControlFrame) {
      readControlFrame();
    } else {
      readMessageFrame();
    }
  }

/**
 * 解析opcode
 */
private void readControlFrame() throws IOException {
    // ...
    switch (opcode) {
      case OPCODE_CONTROL_PING:
        frameCallback.onReadPing(controlFrameBuffer.readByteString());
        break;
      case OPCODE_CONTROL_PONG:
        // 注意:收到pong,触发onReadPing
        frameCallback.onReadPong(controlFrameBuffer.readByteString());
        break;
      case OPCODE_CONTROL_CLOSE:
        // ...
        frameCallback.onReadClose(code, reason);
        closed = true;
        break;
      default:
        throw new ProtocolException("Unknown control opcode: " + toHexString(opcode));
    }
  }
  
  /**
    * 计数器自增,将awaitingPong状态置为false
    */
  @Override public synchronized void onReadPong(ByteString buffer) {
    // This API doesn't expose pings.
    receivedPongCount++;
    awaitingPong = false;
  }
复制代码 

一路追下来发现,其实收到pong之后,okhttp就是简单地改变awaitingPong标记位的状态,就代表收到了上一个pong消息。

总结一下,okhttp的心跳流程,

  1. ReadWebSocket连接的时候创建schedule线程池,定期地执行PingRunnable发送ping消息;(发送其他的请求消息在WriteRunnable中)
  2. 同时创建一个reader不断接受消息;
  3. 发送ping之前,通过awaitingPong标记,判断上一个pong有没有回来;
  4. 对于收到的消息,进行解析,如果收到的pong,改变awaitingPong状态。

1.2 动态调整okhttp心跳

我们发现,只要改变线程池执行PingRunable的delayTime,就能达到在不重新创建OkHttpClient的情况下,动态改变ping的速率。

所以可以用反射的方式,shutDown原来的线程池,创建新的线程池,用新的pingInterval执行pingRunnable。

Class clazz = Class.forName("okhttp3.internal.ws.RealWebSocket");
Field field = clazz.getDeclaredField("executor");
field.setAccessible(true);
ScheduledExecutorService oldService = (ScheduledExecutorService) field.get(mWebSocket);

Class[] innerClasses = Class.forName("okhttp3.internal.ws.RealWebSocket").getDeclaredClasses();
for (Class innerClass : innerClasses) {
    if ("PingRunnable".equals(innerClass.getSimpleName())) {
        // 创建新的PingRunnable实例
        Constructor constructor = innerClass.getDeclaredConstructor(RealWebSocket.class);
        constructor.setAccessible(true);
        Object pingRunnable = constructor.newInstance(mWebSocket);

        // 创建新的线程池
        ScheduledThreadPoolExecutor newService = new ScheduledThreadPoolExecutor(1, Util.threadFactory("ws-ping", false));
        newService.scheduleAtFixedRate((Runnable) pingRunnable, interval, interval, unit);
        field.set(mWebSocket, newService);

        // shutdown旧的线程池
        oldService.shutdown();
    }
}
复制代码 

这样,我们就达成了动态调整心跳的目的。

Java-WebSocket的ping是通过外部调用sendPing()来达到发送ping的目的的,内部没有ping/pong状态机制,所以需要我们自己去维护这个关系。其实可以仿照OkHttp那样,利用定时消息去发送ping,然后解析pong来维护心跳状态。这里就不过多阐述了。

2. 断掉重连

正如前面提过的,断掉重连只要处理好几种断开状态的逻辑即可。好在绝大多数websocket库都分close、error回调,可以区分异常断开和主动断开。

需要自己维护的也就是重连的间隔和重试次数的关系,类似指数退避算法。

3. 多线程

针对上文说到的多线程问题,主要的问题点就是发生在同时发生多个连接请求的时候,与其对多线程加各种同步,不如并行该串行。采用类似消息队列的方式,消息的处理放在单独的线程中取做。这样就可以省却了线程的同步,同时也能保证状态的有序性。

其实Android中,利用HandlerThread就可以完美地满足上述两点,最初笔者也确实是利用HandlerThread来做的。后来觉得可以独立于平台,也可以运行在纯Java的平台上,所以还是自己手动实现了。同时自己维护消息队列,也可以更好地自己处理优先级、延迟等。

WebSocketGo

之前也说过,笔者的项目是基于OkHttp的,但发现状态管理这部分可以单独抽象出来,所以才会有了本文的标题WebSocketGo(以下简称WsGo)。

数据流如下:

  1. Dispatcher就是前文提到的消息队列,就是生产者-消费者模式,维护两个队列,发送command,接收event。

    • command主要有:CONNECT、DISCONNECT、RECONNECT、CHANGE_PING、SEND

    • event主要有:OnConnect、onMessage、onSend、onRetry、onDisConnect、onClose

  2. Channel Manager主要负责状态管理。

  3. WebSocket interface可以理解为适配层,负责调用WebSocket库。

  4. Event Listener就是前台的状态回调。

接入WsGo

implementation 'com.gnepux:wsgo:1.0.2'
// use okhttp
implementation 'com.gnepux:wsgo-okwebsocket:1.0.1'
// use java websocket
implementation 'com.gnepux:wsgo-jwebsocket:1.0.1'
复制代码 

初始化

WsConfig config = new WsConfig.Builder()
        .debugMode(true)    // true to print log
        .setUrl(pushUrl)    // ws url
        .setHttpHeaders(headerMap)  // http headers
        .setConnectTimeout(10 * 1000L)  // connect timeout
        .setReadTimeout(10 * 1000L)     // read timeout
        .setWriteTimeout(10 * 1000L)    // write timeout
        .setPingInterval(10 * 1000L)    // initial ping interval
        .setWebSocket(OkWebSocket.create()) // websocket client
        .setRetryStrategy(retryStrategy)    // retry count and delay time strategy
        .setEventListener(eventListener)    // event listener
        .build();
WsGo.init(config);
复制代码 

Go

// 连接
WsGo.getInstance().connect();
// 发送消息
WsGo.getInstance().send("hello from WsGo");
// 断开
WsGo.getInstance().disconnect(1000, "close");
WsGo.getInstance().disconnectNormal("close");
// 改变心跳
WsGo.getInstance().changePingInterval(10, TimeUnit.SECONDS);
// 释放
WsGo.getInstance().destroyInstance();
复制代码 

WsConfig的更多配置

setWebSocket(WebSocket socket)

WsGo 已经支持OkHttp and Java WebSocket

// for OkHttp (wsgo-okwebsocket)
setWebSocket(OkWebSocket.create());
// for Java WebSocket (wsgo-jwebsocket)
setWebSocket(JWebSocket.create());
复制代码 

如果你需要使用其他的WebSocket库或自定义客户端,只需要实现一个WebSocket接口,将对应结果传递给ChannelCallback即可。剩下的连接管理,WsGo会帮你完成。

public interface WebSocket {
    void connect(WsConfig config, ChannelCallback callback);
    void reconnect(WsConfig config, ChannelCallback callback);
    boolean disconnect(int code, String reason);
    void changePingInterval(long interval, TimeUnit unit);
    boolean send(String msg);
}
复制代码 

setRetryStrategy(RetryStrategy retryStrategy)

对于非正常断开,WsGo会自动重连。RetryStrategy指的是重连次数和延时的关系。

WsGo默认有一个DefaultRetryStrategy,如果你需要自己调整,实现RetryStrategy接口里的onRetry方法即可。

public interface RetryStrategy {
    /**
     * 重试次数和延迟的关系
     *
     * @param retryCount 已经重试的次数
     * @return 延时的时间
     */
    long onRetry(long retryCount);
}
复制代码 

setEventListener(EventListener eventListener)

添加事件回调。需要注意,回调在WsGo自己创建的一个线程中运行,不在调用线程中。如有必要,需要在会调用手动切换线程。

public interface EventListener {
    void onConnect();
    void onDisConnect(Throwable throwable);
    void onClose(int code, String reason);
    void onMessage(String text);
    void onReconnect(long retryCount, long delayMillSec);
    void onSend(String text, boolean success);
}
复制代码 

PS

目前WsGo没有与手机网络的变化通知关联(即断网可以收到回调,网络恢复不会自动重连),因为笔者觉得这部分不属于WebSocket自身的状态管理范畴,而且已经不属于平台无关了。如有有这方便需求,需要自己从外部发起连接。在项目中简单地封装一下就ok啦~

后记

本文稍显长,从最初的智能家居的场景开始,介绍到websocket状态管理现存的问题,然后给出了笔者的解决方案,最后是通用的状态管理框架WebSocketGo。也算是笔者在工作中的一点思考和总结。其实细想,不光WebSocket,所有的长连接应该都会面临相同的问题。但思考的角度和解决的出发点应该都是一样的。

最后

在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。

**如果你觉得自己学习效率低,缺乏正确的指导,可以扫码,加入我们资源丰富,学习氛围浓厚的技术圈一起学习交流吧!

【 WebSocket 框架 】 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的 消息推送机制理解消息推送:传统的 web 程序, 都是属于 “一问一答” 的形式. 客户端给服务器发送了一个 HTTP 请求, 服务器给客户端返回一个 HTTP 响应.这种情况下, 服务器是属于被动的一方. 如果客户端不主动发起请求, 服务器就无法主动给客户端响应.像五子棋这样的程序, 或者聊天这样的程序, 都是非常依赖 “消息推送” 的. 如果只是使用原生的 HTTP 协议, 要想实现消息推送一般需要通过 “轮询。 阅读详情

相关推荐

Android websocket详细使用方法

里面加入了断线重连、心跳机制、退出释放资源,废话不多直接开干 1.添加依赖 implementation "org.java-websocket:Java-WebSocket:1.5.1" 2、添加使用类 import android.util.Log; import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft_6455; import org.java_websocket.hands

lanrenxiaowen的博客 1万+

Android WebSocket实现即时通讯功能

最近做这个功能,分享一下。即时通讯(Instant Messaging)最重要的毫无疑问就是即时,不能有明显的延迟,要实现IM的功能其实并不难,目前有很多第三方,比如极光的JMessage,都比较容易实现。但是如果项目有特殊要求(如不能使用外网),那就得自己做了,所以我们需要使用WebSocket。 WebSocket WebSocket协议就不细讲了,感兴趣的可以具体查阅资料,简而言之,它就......

程序员大佬超的博客 3万+

Android中 使用 WebSocket 实现消息通信

心跳、发送请求都属于客户端主动发送请求,对于请求结果我们分为成功和失败以及超时,发送超时我们是收不到服务器任何回复的,所以我们需要在发送之后将发送放在超时任务队列中,如果请求成功将任务从超时队列中移除,超时从超时队列中获取任务重新请求。此处我们要做的处理是,如果收到连接失败或者断开连接的回调 需要重新连接,我们重新调用一次连接方法即可,并且如果超过三次重连失败,我们在业务中可以通过调用接口来获取数据,避免数据丢失,此处细节省略。Log.d(TAG, “onConnected: 连接成功”);

2401_87288903的博客 2058

4步实现Android中的Websocket

本文介绍如何在Android应用中使用WebSocket实现实时通信,并通过示例入门。

u013643074的博客 7216

YOLOv11 + TensorRT 在 Jetson 设备上的实时部署

YOLOv11 + TensorRT 在 Jetson 设备上的实时部署

p6666666668的博客 1589

HDLbits练习11 - Counter 1-12 ( Exam/ece241 2014 q7a )

(如这题用于当复位信号为高或者计数器递增至12时,该信号置1使计数器的输入信号 ’ d ’ 变为 ’ 4’b0001 ’ ,再加载到 ’ Q ’ 中。当 ’ load ’ 信号为高时,在时钟的上升沿,计数器会将 ’ d ’ 输入的值加载到 ’ Q ’ (计数器的输出)中,而不是递增当前值。用途:在时钟上升沿触发的情况下,如果 ’ enable ’ 信号为高,计数器的值会递增,如果 ’ load ’ 信号为高,计数器将加载 ’ d ’ 信号的值。作用: ’ Q ’ 是计数器的当前值,也是模块的主要输出。

weixin_62830176的博客 473

Android WebSocket状态管理框架 - WebSocketGo

阅读本文需要 Integer.MAX_VALUE 分钟。笔者所在的公司主营业务是智能家居,笔者在公司负责的Android端App的开发。关于智能家居,估计现在百分之八九十的童鞋都听过,但真正了解或者使用过的估计就不占多数了。本文不谈行业前景,只谈技术。为了方便大家更加了解故事的背景,顺便科普一下智能家居,想直奔WebSocket主题的童鞋可以直接第二章节。智能家居,算是物联网的一个典型应用场景。什么是物联网呢,字面意思就是把众多物体连接起来组成一个网络,英文是Internet of things(IoT)。

Android23333的博客 1996

MATLAB 论文复现——含氢综合能源系统多目标最优折中分布鲁棒低碳调度

提出基于多目标决策理论和逼近理想解排序技术,构造综合考虑系统调度的经济性、低碳性和鲁棒度的多目标最优折中函数,从而建立含氢综合能源系统多目标最优折中分布鲁棒低碳调度模型,可快速准确地获得兼具经济性、低碳性和鲁棒性的含氢综合能源系统优化调度方案。[1]孙惠娟,胡紫平,彭春华,等.含氢综合能源系统多目标最优折中分布鲁棒低碳调度[J/OL].电力自动化设备,1-13[2024-07-26].https://doi.org/10.16081/j.epae.202406003.关键词: 综合能源系统;

sjr1478的博客 412

Android 网络通信WebSocket使用详解

WebSocket的详细使用步骤,2022年6月写的。客户端服务端都是在android上实现的。

fusu2178192的博客 1万+

FreeSWITCH实战:5分钟搞定阿里云SDM(MRCP-SERVER)语音机器人搭建(含Docker部署技巧)

本文详细介绍了如何利用FreeSWITCH与阿里云SDM(MRCP-SERVER)快速搭建高可用语音机器人,包括Docker部署技巧、系统调优及高可用架构设计。通过实战案例和优化建议,帮助开发者5分钟内完成部署,提升语音交互系统的性能和稳定性。

stone的博客 505

RK3588 C-PHY摄像头驱动升级踩坑记:从‘Only CSI2 supported’报错到成功取流

本文详细记录了在RK3588平台上调试C-PHY摄像头驱动的全过程,从‘Only CSI2 supported’报错分析到成功取流。内容涵盖C-PHY与CSI-2协议差异、SDK版本对比、V4L2框架下的bus-type解析机制,以及硬件排查与寄存器调优实战经验,为嵌入式Camera驱动工程师提供宝贵参考。

weixin_42522374的博客 125

Android WebSocket 使用指南:详细步骤与实践

WebSocket 是一种在单个长连接上进行全双工通信的协议。相比于传统的HTTP请求,WebSocket 可以减少通信的延迟,适用于需要实时数据传输的场景,如聊天应用、游戏和实时数据更新服务。在Android开发中,WebSocket的使用可以借助OkHttp库或库来实现。

工宗浩生财指南针 1万+

【Pix4d精品教程】Pix4d空三后处理:点云分类与过滤、DSM精编生成DEM、生成等高线案例详解

本文详细介绍了利用Pix4D软件进行无人机摄影测量后处理的关键步骤,主要包括点云分类与分配、DEM精编生成和等高线绘制三个核心环节。首先通过点云分类区分地面、植被等7种地物类型,然后通过点云分配将地面点云归并到Ground类别,剔除植被等干扰要素。随后重新运行DSM生成步骤获取精确DEM,最后在附加输出中设置参数生成标准等高线。文中配有详细的操作截图和注意事项,如处理前需备份原始DSM/DOM数据,并解释了DEM与DSM的本质区别。该教程为无人机摄影测量内业处理提供了完整的解决方案。

智慧空间实验室 1万+

在Android中使用WebSocket

WebSocket是一种通信协议,它提供了一个单一的、长期的连接上的全双工通信通道。它设计用于在Web浏览器和Web服务器中实现,但也可以用于其他应用。与传统的基于请求-响应和无状态的HTTP连接不同,WebSocket允许客户端和服务器之间的双向通信。这使得实时数据交换和推送功能成为可能。

chuyouyinghe的专栏 3009

实时系统vxWorks - 虚拟机环境搭建

概述VxWorks 操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是嵌入式开发环境的关键组成部分。良好的持续发展能力、高性能的内核以及友好的...

不只会拍照的程序猿 7854

uniapp [安卓苹果App端] - 实现微信支付功能+拉起微信付款支付后返回本app页面,详解uniApp如何唤起本机微信app并跳转到支付页面全流程及示例源码(uniapp实现微信支付,可打包)

uni-app项目在APP端使用微信支付功能,uniapp前端实现微信支付,uniapp开发安卓App对接微信支付,uniapp苹果ios唤起微信支付,uniapp调用微信支付uniapp开发app-调用微信支付,uniapp开发安卓苹果app如何接入微信支付,前后端应该怎么做,UniApp中实现微信支付功能,支付报错,调起失败没响应,uniapp苹果微信支付唤起实现,app唤起微信支付,拉起微信支付,uniapp中唤醒微信进行支付并返回app,uniApp调起微信支付失败报错,全网最详细的uniapp微信

🏆 前端领域优质创作者 1万+

android_进阶_websocket基本原理和基本方法

websocket 转载自: https://www.cnblogs.com/cangqinglang/p/8331120.html 初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。 举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 [外链图片转存失败,源站可能有防盗链机制,建议

写bug的蚊子的博客 2860

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

封装websocket请求-----vue项目实战(完整版)

前言: 如果项目中多个组件都使用到WebSocket请求,那么我们需要对WebSocket进行封装,方便我们使用。 下面我根据我的项目结构来创建文件和引入文件,大家注意一下自己项目结构。 一、在utils目录下创建websocket.js文件 import { Message } from 'element-ui' import { getToken } from '@/utils/authTok...

winne雪 2万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

websocket使用

websocket,html5中新一代全双工通信协议。其底层仍然是http协议。 传统 HTTP 请求响应客户端服务器交互图 WebSocket 请求响应客户端服务器交互图 WebSocket 客户端支持 浏览器 支持情况 Chrome Chrome version 4+支持 Firefox

风火一回,一生不毁 2万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Android WebSocket长连接的实现

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于[“服务器推送技术”]的一种。WebSocket的特点包括:建立在 TCP 协议之上,服务器端的实现比较容易。与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。支持双向通信,实时性更强。

Billy_Zuo的博客 3642

【Android WebSocket】Android 端 WebSocket 基本用法 ( 添加依赖和权限 | 创建 WebSocketClient 客户端类 | 建立连接并发送消息 )

一、添加依赖和权限、 二、创建 WebSocketClient 客户端类、 三、建立连接并发送消息、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1万+

android开发使用OkHttp自带的WebSocket实现IM功能

android app开发经常会有IM需求,很多新手不晓得如何入手,难点在于通讯不中断。其实android发展到今天,很多技术都很完善,有很多类似框架可以实现。例如有:okhttp自带的websocket框架、easysocket等等。本文主要讨论的是okhttp自带的websocket框架。

shi450561200的博客 3429

十分钟带你写一个Android Websocket 第一篇

虽然我们说Android使用Java,但实际上还有一些小技巧。给大家带来一篇关于Android WebSocket的教程。当然,我们将从零开始,一步一步地构建它。一起学习、一起进步。为了更加直观的,看到分发的原理,我们自己写一个,嘻嘻嘻。如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

yihanss的博客 1551

Android端WebSocket简单实现

5.在appliction启动也可以在其他地方看需求别忘了在清单文件注册。6.至此就可简单实现Android端的代码后台有需要demo的可以私聊我。2.AndroidManifest 添加权限和service 代码。1.导入Java-WebSocket依赖。3.服务包含心跳检测和连接操作。

weixin_44935137的博客 2103

Android 使用WebSocket通信

APP端实现 1.添加依赖 implementation "org.java-websocket:Java-WebSocket:1.5.1" 2.加入网络请求权限 <uses-permission android:name="android.permission.INTERNET" /> 3.创建客户端类并继承WebSocketClient,需要实现它的四个抽象方法和构造函数 import android.util.Log; import org.java_websocket.client

gdvfs12的博客 3167
上一篇: 【Android】一文读懂 Activity 的生命周期
下一篇: Android四大组件之BroadcastReceiver
不秃头de程序猿
博客等级 码龄5年 4089粉丝 1478原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值