SSE
SSE (Server-Sent Events) is a technology used to realize the server actively push data to the client, it is based on the HTTP protocol, using the characteristics of its long connection, to establish a persistent connection between the client and the server, and through this connection to realize the server to the client's real-time data push.
Server-Sent Events (SSE) and Sockets can both be used to implement real-time communication where the server pushes messages to the client, with contrasting differences:
SSE:
Pros.
Easy to use, just send HTTP streaming responses.
Automatically handles network outages and reconnections.
Supports events natively implemented by browsers, such as "error" and "message".
Disadvantages:
One-way communication, server can only send messages to the client.
Each connection requires a thread or process on the server side.
Socket:
Pros:
Two-way communication, both client and server can send or receive messages.
Can handle more complex application scenarios such as two-way conversations, multiplayer games, etc.
The server can manage connections more finely, such as using long or short connections.
Disadvantages:
Needs to handle network outages and reconnections, which is relatively complex.
Requires both client-side and server-side code to handle socket communication.
Higher requirements for developers, requiring in-depth knowledge of network programming.
SSE Usage Scenarios:
Usage scenarios mainly include application scenarios that require the server to actively push data to the client, such as AI Q&A chat, real-time news, stock quotes, and so on.
case (law)
The server side is implemented based on springboot and supports SSE by default;
The Android client is based on the OkHttp implementation, which also supports SSE;
server-side interface development
package ;
import org.;
import org.;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
@RestController
@RequestMapping("/sse")
public class SSEController {
Logger logger = ();
public static Map<String, SseEmitter> sseEmitters = new ConcurrentHashMap<>();
/**
* reception (of transmitted signal)sserequesting,asynchronous processing,Returning results in batches,Then closeSseEmitter
* @return SseEmitter
*/
@GetMapping("/stream-sse")
public SseEmitter handleSse() {
SseEmitter emitter = new SseEmitter();
// Send a message in a new thread,to avoid blocking the main thread
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Map<String, Object> event = new HashMap<>();
String mes = "Hello, SSE " + (i+1);
("message", mes);
(": "+mes);
(event);
(200);
}
(); // Finish sending
} catch (IOException | InterruptedException e) {
(e); // Send Error
}
}).start();
return emitter;
}
/**
* reception (of transmitted signal)sserequesting,asynchronous processing,Returning results in batches,parallel storageSseEmitter,Can be called from outsidesendMsgconnector,Continue to return results
* @param uid Customer Unique Identification
* @return SseEmitter
*/
@GetMapping("/stream-sse1")
public SseEmitter handleSse1(@RequestParam("uid") String uid) {
SseEmitter emitter = new SseEmitter();
(uid, emitter);
// Send a message in a new thread,to avoid blocking the main thread
new Thread(() -> {
try {
for (int i = 10; i < 15; i++) {
Map<String, Object> event = new HashMap<>();
String mes = "Hello, SSE " + (i+1);
("message", mes);
(": "+mes);
(event);
(200); // each2spike
}
} catch (IOException | InterruptedException e) {
(e); // Send Error
}
}).start();
return emitter;
}
/**
* external callsendMsgconnector,Get the cached one based on the logo of theSseEmitter,Continue to return results
* @param uid Customer Unique Identification
*/
@GetMapping("/sendMsg")
public void sendMsg(@RequestParam("uid") String uid) {
("The server side sends a message to " + uid);
SseEmitter emitter = (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);
("message", mes);
(": "+mes);
(event);
(200); // each2spike
}
(().name("stop").data(""));
(); // close connection
("The server closed the connection voluntarily to " + uid);
} catch (IOException | InterruptedException e) {
(e); // error finish
}
}).start();
}
}
}
The code defines three interfaces that implement two main functions:
stream-sse interface
Used to simulate a request, batch return the result, and then end the SseEmitter;
stream-sse1 interface & sendMsg interface
Used to simulate a request, batch return results, cache SseEmitter, and subsequently can be sent through the sendMsg interface, notify the server to continue to return results;
Client-side functionality development
The Android client depends on OkHttp:
implementation '.okhttp3:okhttp:4.9.1'
implementation(".okhttp3:okhttp-sse:4.9.1")
Layout file: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="/apk/res/android"
xmlns:app="/apk/res-auto"
xmlns:tools="/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="Test Common Interface"
android:layout_centerInParent="true"/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:id="@+id/btn1"
android:layout_below="@id/btn"
android:text="ssegrout"
android:layout_centerInParent="true"/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:id="@+id/btn2"
android:layout_below="@id/btn1"
android:text="ssegrout,carry-over parameter"
android:layout_centerInParent="true"/>
</RelativeLayout>
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class MainActivity extends Activity {
Button btn;
Button btn1;
Button btn2;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
setContentView(.activity_main);
btn = findViewById();
btn1 = findViewById(.btn1);
btn2 = findViewById(.btn2);
tv = findViewById();
(new () {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
testDate();
}
}).start();
}
});
(new () {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
sse();
}
}).start();
}
});
(new () {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
sseWithParams();
}
}).start();
}
});
}
private void testDate(){
OkHttpClient client = new ()
.connectTimeout(10, ) // Timeout for establishing a connection
.readTimeout(10, ) // Timeout for reading data after establishing a connection
.build();
Request request = new ()
.url("http://192.168.43.102:58888/common/getCurDate")
.build();
call = (request);
try {
Response response = (); // synchronization method
if (()) {
String responseBody = ().string(); // Getting the response body
(responseBody);
(responseBody);
}
} catch (Exception e) {
();
}
}
void sse(){
Request request = new ()
.url("http://192.168.43.102:58888/sse/stream-sse")
.addHeader("Authorization", "Bearer ")
.addHeader("Accept", "text/event-stream")
.build();
OkHttpClient okHttpClient = new ()
.connectTimeout(10, ) // Timeout for establishing a connection
.readTimeout(10, ) // Timeout for reading data after establishing a connection
.build();
RealEventSource realEventSource = new RealEventSource(request, new EventSourceListener() {
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
(data); // Requested data
String text = ().toString();
(data+"\n"+text);
if ("finish".equals(type)) { // Message Type,add incremental,finish close,error incorrect,interrupted disruptions
}
}
});
(okHttpClient);
}
void sseWithParams(){
Request request = new ()
.url("http://192.168.43.102:58888/sse/stream-sse1?uid=1")
.addHeader("Authorization", "Bearer ")
.addHeader("Accept", "text/event-stream")
.build();
OkHttpClient okHttpClient = new ()
.connectTimeout(10, ) // Timeout for establishing a connection
.readTimeout(10, ) // Timeout for reading data after establishing a connection
.build();
RealEventSource realEventSource = new RealEventSource(request, new EventSourceListener() {
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
(data); // Requested data
String text = ().toString();
(data+"\n"+text);
}
});
(okHttpClient);
}
}
Effectiveness Test
Calling the stream-sse interface
The server returned the results in batches:
Calling the stream-sse1 interface
The server returned the results in batches:
Call the sendMsg interface via h5 and the server side continues to return the result: