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.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
public class JWebSocketClient extends WebSocketClient {
public JWebSocketClient(URI serverUri) {
super(serverUri, new Draft_6455());
}
@Override
public void onOpen(ServerHandshake handshakedata) {
Log.e("JWebSocketClient", "onOpen()");
}
@Override
public void onMessage(String message) {
Log.e("JWebSocketClient", "onMessage()");
}
@Override
public void onClose(int code, String reason, boolean remote) {
Log.e("JWebSocketClient", "onClose()");
}
@Override
public void onError(Exception ex) {
Log.e("JWebSocketClient", "onError()");
}
}
4.建立websocket连接
private void createWebSocketClient(String userId){
URI uri = URI.create("ws://172.25.204.62:8082/websocket/"+userId);
client = new JWebSocketClient(uri){
@Override
public void onOpen(ServerHandshake handshakedata) {
super.onOpen(handshakedata);
sb.append("onOpen at time:");
sb.append(new Date());
sb.append("服务器状态:");
sb.append(handshakedata.getHttpStatusMessage());
sb.append("\n");
tv_message.setText(sb.toString());
}
@Override
public void onMessage(String message) {
super.onMessage(message);
Message handlerMessage = Message.obtain();
handlerMessage.obj = message;
handler.sendMessage(handlerMessage);
}
@Override
public void onClose(int code, String reason, boolean remote) {
super.onClose(code, reason, remote);
sb.append("onClose at time:");
sb.append(new Date());
sb.append("\n");
sb.append("onClose info:");
sb.append(code);
sb.append(reason);
sb.append(remote);
sb.append("\n");
tv_message.setText(sb.toString());
}
@Override
public void onError(Exception ex) {
super.onError(ex);
sb.append("onError at time:");
sb.append(new Date());
sb.append("\n");
sb.append(ex);
sb.append("\n");
tv_message.setText(sb.toString());
}
};
try {
client.connectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
XToastUtils.error(e+"");
}
}
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
sb.append("服务器返回数据:");
sb.append(msg.obj.toString());
sb.append("\n");
tv_message.setText(sb.toString());
return true;
}
});
5.关闭连接
@Override
public void onDestroy() {
super.onDestroy();
client .close();
}
服务端:
springboot集成websocket。
1.添加依赖
Maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<!-- <scope>test</scope> -->
</dependency>
2.编写配置类:
首先要注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的WebsocketEndpoint。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
具体实现类:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;
//注册成组件
@Component
//定义websocket服务器端,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址
@ServerEndpoint("/websocket/{userId}")
//如果不想每次都写private final Logger logger = LoggerFactory.getLogger(当前类名.class); 可以用注解@Slf4j;可以直接调用log.info
@Slf4j
public class WebSocket {
//实例一个session,这个session是websocket的session
private Session session;
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();
/**接收userId*/
private String userId="";
private static int onlineCount = 0;
//前端请求时一个websocket时
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
//加入set中
}else{
webSocketMap.put(userId,this);
//加入set中
addOnlineCount();
//在线数加1
}
log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
sendMessage("连接成功");
}
//前端关闭时一个websocket时
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//从set中删除
subOnlineCount();
}
log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
}
@OnError
public void onError(Session session, Throwable t) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
log.error("WebSocket连接发生异常,message:"+t.getMessage());
}
StringBuilder stringBuilder;
//前端向后端发送消息
@OnMessage
public void onMessage(String message) {
log.info("用户消息:"+userId+",报文:"+message);
//可以群发消息
//消息保存到数据库、redis
if(!message.isEmpty()){
try {
//解析发送的报文
JSONObject jsonObject = JSON.parseObject(message);
//追加发送人(防止串改)
jsonObject.put("fromUserId",this.userId);
String toUserId=jsonObject.getString("toUserId");
//传送给对应toUserId用户的websocket
if(!toUserId.isEmpty()&&webSocketMap.containsKey(toUserId)){
sendMessage(jsonObject.toJSONString());
}else{
log.error("请求的userId:"+toUserId+"不在该服务器上");
//否则不在这个服务器上,发送到mysql或者redis
}
}catch (Exception e){
e.printStackTrace();
}
}
}
//新增一个方法用于主动向客户端发送消息
public void sendMessage(String message) {
log.info("【websocket消息】广播消息, message={}", message);
try {
this.session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
/** * 发送自定义消息 * */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(!userId.isEmpty()&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("用户"+userId+",不在线!");
}
}
public synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
c
WebSocket.onlineCount--;
}
public ConcurrentHashMap<String, WebSocket> getWebSocketMap() {
return webSocketMap;
}
}
测试结果:
心跳检测和重连
1.service
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import com.jqwl.myapp.utils.websocket.JWebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
/**
* @author yangyuan
* @description:
* @date :2022-05-23 15:33
*/
public class JWebSocketClientService1 extends Service {
private String TAG="JWebSocketClientService";
private URI uri;
public JWebSocketClient client;
private JWebSocketClientBinder mBinder = new JWebSocketClientBinder();
//用于Activity和service通讯
public class JWebSocketClientBinder extends Binder {
public JWebSocketClientService1 getService() {
return JWebSocketClientService1.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
//初始化websocket
initSocketClient();
mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/**
* 初始化websocket连接
*/
private void initSocketClient() {
URI uri = URI.create( "ws://172.25.204.62:8082/websocket/00001");//测试使用
client = new JWebSocketClient(uri) {
@Override
public void onMessage(String message) {
Log.e("JWebSocketClientService", "收到的消息:" + message);
Intent intent = new Intent();//广播接收到的消息,在Activity接收
intent.setAction("com.xxx.servicecallback.content");
intent.putExtra("message", message);
sendBroadcast(intent);
}
@Override
public void onOpen(ServerHandshake handshakedata) {
super.onOpen(handshakedata);
Log.e("JWebSocketClientService", "websocket连接成功");
}
};
connect();
}
/**
* 连接websocket
*/
private void connect() {
new Thread() {
@Override
public void run() {
try {
//connectBlocking多出一个等待操作,会先连接再发送,否则未连接发送会报错
client.connectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 发送消息
*
* @param msg
*/
public void sendMsg(String msg) {
if (null != client) {
Log.e("JWebSocketClientService", "发送的消息:" + msg);
client.send(msg);
}
}
// -------------------------------------websocket心跳检测------------------------------------------------
private static final long HEART_BEAT_RATE = 30 * 1000;//每隔30秒进行一次对长连接的心跳检测
private Handler mHandler = new Handler();
private Runnable heartBeatRunnable = new Runnable() {
@Override
public void run() {
Log.e("JWebSocketClientService", "心跳包检测websocket连接状态");
if (client != null) {
if (client.isClosed()) {
reconnectWs();
}else {
//业务逻辑 这里如果服务端需要心跳包为了防止断开 需要不断发送消息给服务端
// client.send("");
}
} else {
//如果client已为空,重新初始化连接
client = null;
initSocketClient();
}
//每隔一定的时间,对长连接进行一次心跳检测
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
/**
* 开启重连
*/
private void reconnectWs() {
mHandler.removeCallbacks(heartBeatRunnable);
new Thread() {
@Override
public void run() {
try {
Log.e("JWebSocketClientService", "开启重连");
client.reconnectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
在 AndroidManifest.xml 添加
<service
android:name=".utils.service.JWebSocketClientService1"
android:enabled="true"
android:exported="true" />
2.fragment
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.alibaba.fastjson.JSONObject;
import com.jqwl.myapp.core.BaseFragment;
import com.jqwl.myapp.databinding.FragmentWebsocketByServiceBinding;
import com.jqwl.myapp.models.ChatMessage;
import com.jqwl.myapp.utils.XToastUtils;
import com.jqwl.myapp.utils.service.JWebSocketClientService1;
import com.jqwl.myapp.utils.websocket.JWebSocketClient;
import com.xuexiang.xpage.annotation.Page;
import com.xuexiang.xpage.enums.CoreAnim;
import java.util.ArrayList;
import java.util.List;
@Page(name = "WebSocket+service心跳检测1", anim = CoreAnim.slide)
public class WebSocketServiceFragment1 extends BaseFragment<FragmentWebsocketByServiceBinding> {
FragmentWebsocketByServiceBinding binding;
TextView tv_message;
EditText et_send_message;
EditText et_user_id;
Button btn_send;
Button btn_clear;
Button btn_create;
Button btn_close;
StringBuffer stringBuffer = new StringBuffer();
private JWebSocketClientService1.JWebSocketClientBinder binder;
private JWebSocketClientService1 jWebSClientService;
private JWebSocketClient client;
private ChatMessageReceiver chatMessageReceiver;
private List<ChatMessage> chatMessageList = new ArrayList<>();//消息列表
@NonNull
@Override
protected FragmentWebsocketByServiceBinding viewBindingInflate(LayoutInflater inflater, ViewGroup container) {
binding = FragmentWebsocketByServiceBinding.inflate(inflater, container, false);
tv_message = binding.tvMessage;
et_send_message = binding.etSendMessage;
btn_send = binding.btnSend;
btn_clear = binding.btnClear;
et_user_id = binding.etUserId;
btn_create = binding.btnCreate;
btn_close = binding.btnClose;
return binding;
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.e("this", "服务与活动成功绑定");
binder = (JWebSocketClientService1.JWebSocketClientBinder) iBinder;
jWebSClientService = binder.getService();
client = jWebSClientService.client;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e("this", "服务与活动成功断开");
}
};
private class ChatMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setContent(message);
chatMessage.setIsMeSend(0);
chatMessage.setIsRead(1);
chatMessage.setTime(System.currentTimeMillis() + "");
chatMessageList.add(chatMessage);
for (ChatMessage chatMessage1 : chatMessageList){
stringBuffer.append(chatMessage1.getContent());
}
tv_message.setText(stringBuffer);
}
}
@Override
protected void initViews() {
//启动服务
startJWebSClientService();
//绑定服务
bindService();
//注册广播
doRegisterReceiver();
//检测通知是否开启
// checkNotification(getContext());
}
/**
* 启动服务(websocket客户端服务)
*/
private void startJWebSClientService() {
Intent intent = new Intent(getActivity(), JWebSocketClientService1.class);
getActivity().startService(intent);
}
/**
* 绑定服务
*/
private void bindService() {
Intent bindIntent = new Intent(getContext(), JWebSocketClientService1.class);
getActivity().bindService(bindIntent, serviceConnection, 0000);
}
/**
* 动态注册广播
*/
private void doRegisterReceiver() {
chatMessageReceiver = new ChatMessageReceiver();
IntentFilter filter = new IntentFilter("com.xxx.servicecallback.content");
getActivity().registerReceiver(chatMessageReceiver, filter);
}
@Override
protected void initListeners() {
super.initListeners();
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String content = et_send_message.getText().toString();
if (content.length() <= 0) {
XToastUtils.error("消息不能为空哟");
return;
}
if (client != null && client.isOpen()) {
JSONObject json = new JSONObject();
json.put("message",content);
json.put("toUserId","00001");
jWebSClientService.sendMsg(json.toString());
//暂时将发送的消息加入消息列表,实际以发送成功为准(也就是服务器返回你发的消息时)
ChatMessage chatMessage = new ChatMessage();
chatMessage.setContent(content);
chatMessage.setIsMeSend(1);
chatMessage.setIsRead(1);
chatMessage.setTime(System.currentTimeMillis() + "");
tv_message.setText(chatMessage.getContent());
} else {
XToastUtils.error("连接已断开,请稍等或重启App哟");
}
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
tv_message.setText("");
}
});
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
/**
* 注销广播
*/
private void unRegisterReceiver() {
getActivity().unregisterReceiver(chatMessageReceiver);
}
@Override
public void onDestroy() {
super.onDestroy();
unRegisterReceiver();
getActivity().unbindService(serviceConnection);
}
}
效果: