Android Socket通信 发送心跳包 重连

最近项目中,有使用Socket与后端进行通信,然后简单的了解了下Socket使用,大致流程是配置服务端的Ip、端口号,连接,监听数据和发送数据,数据的读取和发送都是以流的形式实现的,然后自己将项目中的代码写了简单的管理类,测试下代码。下面是demo的两个界面,连接和发送接收界面

 

这里使用了一个Tcp调试助手,模拟服务端发送和接收数据,通讯模式选择TcpService ,本地端口随意定义一个,保持和demo填写的端口号一致,demo里面的ip是你主机的ip地址,cmd下ipconfig查看

代码

一、初始化

public TcpManager initSocket(final String ip, final String port) {
        this.ip=ip;
        this.port=port;
        /* 开启读写线程*/
        threadStatus =true;
        new ReadThread().start();

        if (socket == null && connectThread == null) {
            connectThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    socket = new Socket();
                    try {
                        /*超时时间为2秒*/
                        socket.connect(new InetSocketAddress(ip, Integer.valueOf(port)), 2000);
                        /*连接成功的话  发送心跳包*/
                        if (socket.isConnected()) {
                            inputStream = socket.getInputStream();
                            dis = new DataInputStream(inputStream);
                            /*因为Toast是要运行在主线程的  这里是子线程  所以需要到主线程哪里去显示toast*/
                            Log.e(TAG, "服务连接成功");
                            /*发送连接成功的消息*/
                            if(onSocketStatusListener!=null)
                                onSocketStatusListener.onConnectSuccess();
                            /*发送心跳数据*/
                            sendBeatData();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (e instanceof SocketTimeoutException) {
                            Log.e(TAG,"连接超时,正在重连");
                            releaseSocket();
                        } else if (e instanceof NoRouteToHostException) {
                            Log.e(TAG,"该地址不存在,请检查");
                        } else if (e instanceof ConnectException) {
                            Log.e(TAG,"连接异常或被拒绝,请检查");
                        }
                    }
                }
            });
            /*启动连接线程*/
            connectThread.start();
        }
        return this;
    }

二、发送数据

public void sendData(final String data) {
        if (socket != null && socket.isConnected()) {
            /*发送指令*/
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        outputStream = socket.getOutputStream();
                        if (outputStream != null) {
                            outputStream.write((data).getBytes("UTF-8"));
                            outputStream.flush();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }).start();

        } else {
            Log.e(TAG,"socket连接错误,请重试");
        }
    }

三、读取数据

private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            //判断进程是否在运行,更安全的结束进程
            while (threadStatus) {
                if (inputStream != null) {
                    try {
                        rcvLength = dis.read(buff);
                        if (rcvLength > 0) {
                            rcvMsg = new String(buff, 0, rcvLength, "GBK");
                            //接收到数据,切换主线程,显示数据
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    if(onReceiveDataListener!=null){
                                        onReceiveDataListener.onReceiveData(rcvMsg);
                                    }
                                }
                            });

                        }
                    } catch (Exception e) {
                        Log.e(TAG,"接收总控数据异常");
                    }
                }

            }
        }
    }

四、全部代码

package com.yufs.testsocket.tcp;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Timer;
import java.util.TimerTask;

public class TcpManager {

    private static final String TAG = TcpManager.class.getSimpleName();
    /*socket*/
    private Socket socket;
    /*连接线程*/
    private Thread connectThread;
    /* 发送输出流*/
    private OutputStream outputStream;
    /* 读写输入流*/
    private InputStream inputStream;
    private DataInputStream dis;
    /* 线程状态,安全结束线程*/
    private  boolean threadStatus =false;
    /* 读取保存进数组*/
    byte buff[]  = new byte[1024*1024*2];
    private String ip;
    private String port;
    private Handler handler = new Handler(Looper.getMainLooper());
    /*默认重连*/
    private boolean isReConnect = true;
    /*倒计时Timer发送心跳包*/
    private Timer timer;
    private TimerTask task;

    /* 心跳周期(s)*/
    private int heartCycle = 30;
    /*接收数据长度*/
    private int rcvLength;
    /*接收数据*/
    private String rcvMsg;
    private TcpManager(){}
    private static TcpManager instance;
    public static synchronized TcpManager getInstance(){
        if(instance==null){
            synchronized (TcpManager.class){
                instance=new TcpManager();
            }
        }
        return instance;
    }

    public TcpManager initSocket(final String ip, final String port) {
        this.ip=ip;
        this.port=port;
        /* 开启读写线程*/
        threadStatus =true;
        new ReadThread().start();

        if (socket == null && connectThread == null) {
            connectThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    socket = new Socket();
                    try {
                        /*超时时间为2秒*/
                        socket.connect(new InetSocketAddress(ip, Integer.valueOf(port)), 2000);
                        /*连接成功的话  发送心跳包*/
                        if (socket.isConnected()) {
                            inputStream = socket.getInputStream();
                            dis = new DataInputStream(inputStream);
                            /*因为Toast是要运行在主线程的  这里是子线程  所以需要到主线程哪里去显示toast*/
                            Log.e(TAG, "服务连接成功");
                            /*发送连接成功的消息*/
                            if(onSocketStatusListener!=null)
                                onSocketStatusListener.onConnectSuccess();
                            /*发送心跳数据*/
                            sendBeatData();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (e instanceof SocketTimeoutException) {
                            Log.e(TAG,"连接超时,正在重连");
                            releaseSocket();
                        } else if (e instanceof NoRouteToHostException) {
                            Log.e(TAG,"该地址不存在,请检查");
                        } else if (e instanceof ConnectException) {
                            Log.e(TAG,"连接异常或被拒绝,请检查");
                        }
                    }
                }
            });
            /*启动连接线程*/
            connectThread.start();
        }
        return this;
    }

    /*发送数据*/
    public void sendData(final String data) {
        if (socket != null && socket.isConnected()) {
            /*发送指令*/
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        outputStream = socket.getOutputStream();
                        if (outputStream != null) {
                            outputStream.write((data).getBytes("UTF-8"));
                            outputStream.flush();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }).start();

        } else {
            Log.e(TAG,"socket连接错误,请重试");
        }
    }

    /*定时发送数据*/
    private void sendBeatData() {
        if (timer == null) {
            timer = new Timer();
        }

        if (task == null) {
            task = new TimerTask() {
                @Override
                public void run() {
                    try {
                        outputStream = socket.getOutputStream();
                        Log.i(TAG,"发送心跳包");
                        /*这里的编码方式根据你的需求去改*/
                        outputStream.write(("test\n").getBytes("UTF-8"));
                        outputStream.flush();
                    } catch (Exception e) {
                        /*发送失败说明socket断开了或者出现了其他错误*/
                        Log.e(TAG,"连接断开,正在重连");
                        /*重连*/
                        releaseSocket();
                        e.printStackTrace();
                    }
                }
            };
        }

        timer.schedule(task, 0, 1000*heartCycle);
    }

    /*释放资源*/
    private  void releaseSocket(){
        if (task != null) {
            task.cancel();
            task = null;
        }
        if (timer != null) {
            timer.purge();
            timer.cancel();
            timer = null;
        }
        if (outputStream != null) {
            try {
                outputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            outputStream = null;
        }
        if(inputStream!=null){
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            inputStream=null;
        }
        if(dis!=null){
            try {
                dis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            dis=null;
        }
        if (socket != null) {
            try {
                socket.close();

            } catch (IOException e) {
            }
            socket = null;
        }
        if (connectThread != null) {
            connectThread = null;
        }
        /*重新初始化socket*/
        if (isReConnect) {
            initSocket(ip,port);
        }
    }


    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            //判断进程是否在运行,更安全的结束进程
            while (threadStatus) {
                if (inputStream != null) {
                    try {
                        rcvLength = dis.read(buff);
                        if (rcvLength > 0) {
                            rcvMsg = new String(buff, 0, rcvLength, "GBK");
                            //接收到数据,切换主线程,显示数据
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    if(onReceiveDataListener!=null){
                                        onReceiveDataListener.onReceiveData(rcvMsg);
                                    }
                                }
                            });

                        }
                    } catch (Exception e) {
                        Log.e(TAG,"接收总控数据异常");
                    }
                }

            }
        }
    }

    public interface OnSocketStatusListener{
        void onConnectSuccess();
    }

    public OnSocketStatusListener onSocketStatusListener;

    public void setOnSocketStatusListener(OnSocketStatusListener onSocketStatusListener) {
        this.onSocketStatusListener = onSocketStatusListener;
    }

    public interface OnReceiveDataListener{
        void onReceiveData(String str);
    }

    public OnReceiveDataListener onReceiveDataListener;

    public void setOnReceiveDataListener(OnReceiveDataListener onReceiveDataListener) {
        this.onReceiveDataListener = onReceiveDataListener;
    }
}

总体来说分为三步:连接、发送、线程监听数据,这里连接状态和接收数据使用了监听事件,可以考虑使用EventBus。整个代码写在一个工具类里面,里面还有很多需要优化的地方。不如说将这些写在服务里面,服务的生命周期与界面绑定。还有数据的读取这一方面好像有长度限制,超过一定长度,它就分为两次或三次,这个问题待解决,不过应对小数据的通信是没有问题的。

demo下载

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值