记录长连接(WebSocket)使用

本文分享了作者在实际项目中使用WebSocket的经验,从选择第三方SDK到因项目需求自定义WebSocket的全过程。文章详细介绍了如何在Android平台上封装WebSocket客户端,包括连接、消息处理、心跳检测和重连机制,并提供了具体代码实现。
摘要由CSDN通过智能技术生成

自从使用过 融云 之后,我就很少写自己写长连接了,毕竟自己实现的长连接和 融云,腾讯云,环信等等这些三方SDK比较起来,还是太幼稚,最重要的是好似每一种类型的APP都会要求实现IM功能。

自从16年使用WebSocket写一个简单的直播聊天场景,后来因为要交互一些复杂的场景实现 例如送礼物,发私信,等等。自己实现的IM显然不成熟,基于xxml 制定的传输协议,也不足与应付各种实现。然后采用了融云,三方SDK这个东西,用过一次真的容易上瘾。让平常需要一周,半个月的开发工期简简单单的一两个小时就完美实现。 一个爽字不足以说明当时的顺滑~

但是有时候也受公司商务和领导的限制,所以知道今年这种顺滑的感觉彻底打破,我们在已经接入了腾讯的SDK之后,碰到了需要使用长连接场景的功能,几个领导一碰,我们采用WebSocket !!! 当我得知这个噩耗时候,也尝试挣扎一下,理论上完全可以使用腾讯云提供的长连接方式实现,但是没用,人微言轻。所以就再次拾起了WebSocket 功能。这里记录下使用

步骤一:导包

    // WebSocket
    api "org.java-websocket:Java-WebSocket:1.4.0"

步骤二:基础封装

WeSoketUtil:封装连接地址, 心跳包时间间隔
public class WeSoketUtil {
    public static final String ws = "";//websocket测试地址

    public static final long HEART_BEAT_RATE = 30 * 1000;//每隔30秒进行一次对长连接的心跳检测
}
JWebSocketClient: 继承  WebSocketClient 设置 连接,发送,收到消息,出错,关闭的回调
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()"+reason);
    }

    @Override
    public void onError(Exception ex) {
        Log.e("JWebSocketClient", "onError()"+ex.toString());
    }
}
JWebSocketClientService :主要功能,设置保活,设置心跳,设置重连
public class JWebSocketClientService extends Service {
    public JWebSocketClient client;
    private JWebSocketClientBinder mBinder = new JWebSocketClientBinder();
    private final static int GRAY_SERVICE_ID = 1001;

    //灰色保活
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
        }

        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }


    //用于Activity和service通讯
    public class JWebSocketClientBinder extends Binder {
        public JWebSocketClientService getService() {
            return JWebSocketClientService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //初始化websocket
        initSocketClient();
        mHandler.postDelayed(heartBeatRunnable, WeSoketUtil.HEART_BEAT_RATE);//开启心跳检测
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        closeConnect();
        super.onDestroy();
    }

    public JWebSocketClientService() {
    }


    /**
     * 初始化websocket连接
     */
    private void initSocketClient() {
        URI uri = URI.create(WeSoketUtil.ws);
        client = new JWebSocketClient(uri) {
            @Override
            public void onMessage(String message) {
                EventBus.getDefault().post(new SocketMessage(1, message));
            }

            @Override
            public void onOpen(ServerHandshake handshakedata) {
                super.onOpen(handshakedata);
                EventBus.getDefault().post(new SocketMessage(0, ""));

            }


        };
        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) {
            Logger.e("发送的消息:" + msg);
            client.send(msg);
        }
    }

    /**
     * 断开连接
     */
    private void closeConnect() {
        try {
            if (null != client) {
                client.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            client = null;
            mHandler.removeCallbacksAndMessages(null);
        }
    }

    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已为空,重新初始化连接
                client = null;
                initSocketClient();
            }
            //每隔一定的时间,对长连接进行一次心跳检测
            mHandler.postDelayed(this, WeSoketUtil.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();
    }
}

使用:

        <service
            android:name=".websocket.JWebSocketClientService"
            android:enabled="true"
            android:exported="true" />
        <service
            android:name=".websocket.JWebSocketClientService$GrayInnerService"
            android:enabled="true"
            android:exported="false"
            android:process=":gray" />


    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Logger.e("服务与活动成功绑定");
            binder = (JWebSocketClientService.JWebSocketClientBinder) iBinder;
            jWebSClientService = binder.getService();
            client = jWebSClientService.client;
            isOpenService = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Logger.e("服务与活动成功断开");
        }
    };



直接使用
                    //启动服务
                    startJWebSClientService();
                    //绑定服务
                    bindService();

结束:

这篇文章仅仅是记录,方便以后在遇到当时的问题直接CV,希望以后不要遇到了。

WebSocket 长连接如果遇到断开(例如网络问题、服务器重启等),在重连后可能会导致之前发送的数据丢失。这是因为 WebSocket 的协议设计并不是为了保证消息顺序,当连接中断后再恢复,新建立的连接无法保证旧的消息能够到达。 解决这个问题,有几种策略: 1. **心跳检测**:定期向服务器发送“心跳”包,确认连接状态。如果服务器长时间没有响应,客户端可以尝试重连。这种方式可以帮助尽早发现连接异常。 2. **消息持久化**:对于重要的数据,可以在客户端缓存,直到确认消息已经成功发送到服务器。如果连接断开,可以从缓存中重新发送。 3. **有序消息**:如果应用允许,可以考虑在数据前加上序号或ID,然后在接收端对每个收到的消息进行排序,检查是否缺失。 4. **服务器回执机制**:如果服务器能提供消息确认机制(比如ACK消息),客户端可以等待服务器确认消息已接收后再继续发送后续数据。 5. **错误处理及重试机制**:在连接失败或断开时,应该记录错误信息,并设置合理的重试时间,避免频繁无效地尝试连接。 6. **使用幂等性API**:对于一些操作,即使多次发送也能达到同样的效果,尽量选择这样的 API,减少因网络问题带来的数据丢失风险。 在实际开发中,结合以上策略并根据应用需求来设计解决方案,才能更好地处理WebSocket长连接重连后的数据丢失问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值