最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android?Springboot?實現SSE通信案例詳解

 更新時間:2024年07月25日 08:59:52   作者:Android、Unity3d  
SSE是一種用于實現服務器主動向客戶端推送數據的技術,它基于?HTTP?協(xié)議,利用了其長連接特性,在客戶端與服務器之間建立一條持久化連接,并通過這條連接實現服務器向客戶端的實時數據推送,這篇文章主要介紹了Android?Springboot?實現SSE通信案例,需要的朋友可以參考下

SSE

SSE(Server-Sent Events)是一種用于實現服務器主動向客戶端推送數據的技術,它基于 HTTP 協(xié)議,利用了其長連接特性,在客戶端與服務器之間建立一條持久化連接,并通過這條連接實現服務器向客戶端的實時數據推送。

Server-Sent Events (SSE) 和 Sockets 都可以用于實現服務器向客戶端推送消息的實時通信,差異對比:
SSE:

優(yōu)點:
使用簡單,只需發(fā)送 HTTP 流式響應。
自動處理網絡中斷和重連。
支持由瀏覽器原生實現的事件,如 "error" 和 "message"。
缺點:
單向通信,服務器只能發(fā)送消息給客戶端。
每個連接需要服務器端的一個線程或進程。

Socket:

優(yōu)點:
雙向通信,客戶端和服務器都可以發(fā)送或接收消息。
可以處理更復雜的應用場景,如雙向對話、多人游戲等。
服務器可以更精細地管理連接,如使用長連接或短連接。
缺點:
需要處理網絡中斷和重連,相對復雜。
需要客戶端和服務器端的代碼都能處理 Socket 通信。
對開發(fā)者要求較高,需要對網絡編程有深入了解。

SSE使用場景:

使用場景主要包括需要服務器主動向客戶端推送數據的應用場景,?如AI問答聊天、實時新聞、?股票行情等。

案例

服務端基于springboot實現,默認支持SSE;
Android客戶端基于OkHttp實現,同樣也支SSE;

服務端接口開發(fā)

SSEController.java

package com.qxc.server.controller.sse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/sse")
public class SSEController {
    Logger logger = LoggerFactory.getLogger(SSEController.class);
    public static Map<String, SseEmitter> sseEmitters = new ConcurrentHashMap<>();
    /**
     * 接收sse請求,異步處理,分批次返回結果,然后關閉SseEmitter
     * @return SseEmitter
     */
    @GetMapping("/stream-sse")
    public SseEmitter handleSse() {
        SseEmitter emitter = new SseEmitter();
        // 在新線程中發(fā)送消息,以避免阻塞主線程
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Map<String, Object> event = new HashMap<>();
                    String mes = "Hello, SSE " + (i+1);
                    event.put("message", mes);
                    logger.debug("emitter.send:  "+mes);
                    emitter.send(event);
                    Thread.sleep(200);
                }
                emitter.complete(); // 完成發(fā)送
            } catch (IOException | InterruptedException e) {
                emitter.completeWithError(e); // 發(fā)送錯誤
            }
        }).start();
        return emitter;
    }
    /**
     * 接收sse請求,異步處理,分批次返回結果,并存儲SseEmitter,可通過外界調用sendMsg接口,繼續(xù)返回結果
     * @param uid 客戶唯一標識
     * @return SseEmitter
     */
    @GetMapping("/stream-sse1")
    public SseEmitter handleSse1(@RequestParam("uid") String uid) {
        SseEmitter emitter = new SseEmitter();
        sseEmitters.put(uid, emitter);
        // 在新線程中發(fā)送消息,以避免阻塞主線程
        new Thread(() -> {
            try {
                for (int i = 10; i < 15; i++) {
                    Map<String, Object> event = new HashMap<>();
                    String mes = "Hello, SSE " + (i+1);
                    event.put("message", mes);
                    logger.debug("emitter.send:  "+mes);
                    emitter.send(event);
                    Thread.sleep(200); // 每2秒發(fā)送一次
                }
            } catch (IOException | InterruptedException e) {
                emitter.completeWithError(e); // 發(fā)送錯誤
            }
        }).start();
        return emitter;
    }
    /**
     * 外界調用sendMsg接口,根據標識獲取緩存的SseEmitter,繼續(xù)返回結果
     * @param uid 客戶唯一標識
     */
    @GetMapping("/sendMsg")
    public void sendMsg(@RequestParam("uid") String uid) {
        logger.debug("服務端發(fā)送消息 to " + uid);
        SseEmitter emitter = sseEmitters.get(uid);
        if(emitter != null){
            new Thread(() -> {
                try {
                    for (int i = 20; i < 30; i++) {
                        Map<String, Object> event = new HashMap<>();
                        String mes = "Hello, SSE " + (i+1);
                        event.put("message", mes);
                        logger.debug("emitter.send:  "+mes);
                        emitter.send(event);
                        Thread.sleep(200); // 每2秒發(fā)送一次
                    }
                    emitter.send(SseEmitter.event().name("stop").data(""));
                    emitter.complete(); // close connection
                    logger.debug("服務端主動關閉了連接 to " + uid);
                } catch (IOException | InterruptedException e) {
                    emitter.completeWithError(e); // error finish
                }
            }).start();
        }
    }
}

代碼定義了3個接口,主要實現了兩個功能:
stream-sse 接口

用于模擬一次請求,批次返回結果,然后結束SseEmitter;

stream-sse1接口 & sendMsg接口

用于模擬一次請求,批次返回結果,緩存SseEmitter,后續(xù)還可以通過sendMsg接口,通知服務端繼續(xù)返回結果;

客戶端功能開發(fā)

Android客戶端依賴OkHttp:

implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation("com.squareup.okhttp3:okhttp-sse:4.9.1")

布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tv"
        android:layout_above="@id/btn"
        android:layout_centerHorizontal="true"
        android:text="--"
        android:lines="15"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"/>
    <Button
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:id="@+id/btn"
        android:text="測試普通接口"
        android:layout_centerInParent="true"/>
    <Button
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:id="@+id/btn1"
        android:layout_below="@id/btn"
        android:text="sse連接"
        android:layout_centerInParent="true"/>
    <Button
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:id="@+id/btn2"
        android:layout_below="@id/btn1"
        android:text="sse連接,攜帶參數"
        android:layout_centerInParent="true"/>
</RelativeLayout>

MainActivity.java

package com.cb.testsd;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.sse.RealEventSource;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
public class MainActivity extends Activity {
    Button btn;
    Button btn1;
    Button btn2;
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = findViewById(R.id.btn);
        btn1 = findViewById(R.id.btn1);
        btn2 = findViewById(R.id.btn2);
        tv = findViewById(R.id.tv);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        testDate();
                    }
                }).start();
            }
        });
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        sse();
                    }
                }).start();
            }
        });
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        sseWithParams();
                    }
                }).start();
            }
        });
    }
    private void testDate(){
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)   // 建立連接的超時時間
                .readTimeout(10, TimeUnit.MINUTES)  // 建立連接后讀取數據的超時時間
                .build();
        Request request = new Request.Builder()
                .url("http://192.168.43.102:58888/common/getCurDate")
                .build();
        okhttp3.Call call = client.newCall(request);
        try {
            Response response = call.execute(); // 同步方法
            if (response.isSuccessful()) {
                String responseBody = response.body().string(); // 獲取響應體
                System.out.println(responseBody);
                tv.setText(responseBody);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    void sse(){
        Request request = new Request.Builder()
                .url("http://192.168.43.102:58888/sse/stream-sse")
                .addHeader("Authorization", "Bearer ")
                .addHeader("Accept", "text/event-stream")
                .build();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)   // 建立連接的超時時間
                .readTimeout(10, TimeUnit.MINUTES)  // 建立連接后讀取數據的超時時間
                .build();
        RealEventSource realEventSource = new RealEventSource(request, new EventSourceListener() {
            @Override
            public void onEvent(EventSource eventSource, String id, String type, String data) {
                System.out.println(data);   // 請求到的數據
                String text = tv.getText().toString();
                tv.setText(data+"\n"+text);
                if ("finish".equals(type)) {    // 消息類型,add 增量,finish 結束,error 錯誤,interrupted 中斷
                }
            }
        });
        realEventSource.connect(okHttpClient);
    }
    void sseWithParams(){
        Request request = new Request.Builder()
                .url("http://192.168.43.102:58888/sse/stream-sse1?uid=1")
                .addHeader("Authorization", "Bearer ")
                .addHeader("Accept", "text/event-stream")
                .build();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)   // 建立連接的超時時間
                .readTimeout(10, TimeUnit.MINUTES)  // 建立連接后讀取數據的超時時間
                .build();
        RealEventSource realEventSource = new RealEventSource(request, new EventSourceListener() {
            @Override
            public void onEvent(EventSource eventSource, String id, String type, String data) {
                System.out.println(data);   // 請求到的數據
                String text = tv.getText().toString();
                tv.setText(data+"\n"+text);
            }
        });
        realEventSource.connect(okHttpClient);
    }
}

效果測試

調用stream-sse接口

服務器分批次返回了結果:

調用stream-sse1接口

服務器分批次返回了結果:

通過h5調用sendMsg接口,服務端繼續(xù)返回結果:

到此這篇關于Android Springboot 實現SSE通信案例的文章就介紹到這了,更多相關Android Springboot SSE通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Kotlin實用語法糖空安全類型轉換及相等性判斷

    Kotlin實用語法糖空安全類型轉換及相等性判斷

    這篇文章主要為大家介紹了Kotlin實用語法糖空安全類型轉換及相等性判斷示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Kotlin中協(xié)變、逆變和不變示例詳解

    Kotlin中協(xié)變、逆變和不變示例詳解

    這篇文章主要給大家介紹了關于Kotlin中協(xié)變、逆變和不變的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-04-04
  • Android自定義ViewGroup實現九宮格布局

    Android自定義ViewGroup實現九宮格布局

    這篇文章主要為大家詳細介紹了Android如何通過自定義ViewGroup實現九宮格布局,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-12-12
  • Android懸浮按鈕點擊返回頂部FloatingActionButton

    Android懸浮按鈕點擊返回頂部FloatingActionButton

    這篇文章主要為大家詳細介紹了Android懸浮按鈕FloatingActionButton點擊回到頂部的實現代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Android開發(fā)筆記之Fragment的使用教程

    Android開發(fā)筆記之Fragment的使用教程

    我們的Android入門一步步已經進入中級,我們講完了所有的基本組件的基本使用、Activity、Service、BroadCast,今天我們來介紹一下Fragment的使用教程,需要的朋友可以參考下
    2023-05-05
  • Android中使用AspectJ詳解

    Android中使用AspectJ詳解

    本文主要介紹了Android中使用AspectJ的相關知識。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • Android編程之動態(tài)壁紙實例分析

    Android編程之動態(tài)壁紙實例分析

    這篇文章主要介紹了Android編程之動態(tài)壁紙實現方法,以實例形式分析了Android動態(tài)壁紙的原理與實現步驟,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-12-12
  • Android中回調接口的使用介紹

    Android中回調接口的使用介紹

    回調接口在完成某些特殊的功能時還是蠻有用的,下面為大家分享下具體的使用方法,感興趣的朋友可以參考下哈
    2013-06-06
  • Flutter使用Provider進行狀態(tài)管理的實現

    Flutter使用Provider進行狀態(tài)管理的實現

    Provider是Flutter中一個非常流行的狀態(tài)管理工具,它可以幫助開發(fā)者更有效地管理Widget樹中的數據,本文主要介紹了Flutter使用Provider進行狀態(tài)管理的實現,感興趣的可以了解一下
    2024-04-04
  • Android后臺啟動Activity的實現示例

    Android后臺啟動Activity的實現示例

    這篇文章主要介紹了Android后臺啟動Activity的實現示例,幫助大家更好的理解和學習使用Android開發(fā),感興趣的朋友可以了解下
    2021-04-04

最新評論

沙雅县| 汝阳县| 厦门市| 巫山县| 九龙城区| 寻乌县| 新余市| 龙南县| 葫芦岛市| 团风县| 乾安县| 丹寨县| 巴南区| 岳阳市| 密云县| 修文县| 通城县| 达孜县| 罗山县| 东港市| 平江县| 同心县| 浠水县| 汉源县| 张北县| 滕州市| 林周县| 桐庐县| 札达县| 长海县| 兖州市| 武邑县| 深泽县| 德安县| 休宁县| 蕲春县| 陇川县| 铜梁县| 三江| 富阳市| 德清县|