Android 基于UDP的Socket通信

作者: Kandy_JS | 来源:发表于2019-02-16 14:24 被阅读7次

    参考

    1、Android Socket通信--UdpClient
    2、Android UDP
    3、Android Socket通信(一)--基于UDP协议通信

    截图

    1、Android 客户端
    client.png
    2、PC服务端:用的是SocketTool软件模拟
    server.png

    流程

    1、连接DatagramSocket的服务端(ip和port):开启异步线程和socket
    2、发送数据(DatagramPacket):异步
    3、接收数据(DatagramPacket):注意连接状态,异步读取
    4、关闭连接:关闭DatagramSocket和对应线程

    注意

    1、异常:android.os.NetworkOnMainThreadException。 socket需要在线程中使用
    2、前后端统一传输或者接收协议 [requestcode size d1 d2 d3 ... ],在解析时候用得到
    3、实施监控socket的连接状态,还是用心跳包发过去,然后返回数据,一段时间没有的话则代表socket连接失败。
    4、注意receive接收数据后的有效长度(一个是预存的buffer,一个是有效结果buffer)
    5、客户端连上去后不知道为何一定要先发送一次,才能接收?
    6、UDP不安全,有长度限制64K

    代码

    1、UdpClient.java:udp-socket的客户端,略微做了通用封装,主要是连接,发送,接收,然后设置监听
    /**
     * Created by wujn on 2019/2/15.
     * Version : v1.0
     * Function: udp client 64k限制
     */
    public class UdpClient {
    
    
        /**
         * single instance UdpClient
         * */
        private static UdpClient mSocketClient = null;
        private UdpClient(){}
        public static UdpClient getInstance(){
            if(mSocketClient == null){
                synchronized (UdpClient.class) {
                    mSocketClient = new UdpClient();
                }
            }
            return mSocketClient;
        }
    
    
        String TAG_log = "Socket";
        private DatagramSocket mSocket;
        private DatagramPacket sendPacket;    //发送
        private DatagramPacket receivePacket; //接受
    
    //  private OutputStream mOutputStream;
    //  private InputStream mInputStream;
    
        private SocketThread mSocketThread;
        private boolean isStop = false;//thread flag
    
    
        /**
         * 128 - 数据按照最长接收,一次性
         * */
        private class SocketThread extends Thread {
    
            private String ip;
            private int port;
            public SocketThread(String ip, int port){
                this.ip = ip;
                this.port = port;
            }
    
            @Override
            public void run() {
                Log.d(TAG_log,"SocketThread start ");
                super.run();
    
                //connect ...
                try {
                    if (mSocket != null) {
                        mSocket.close();
                        mSocket = null;
                    }
    
                    InetAddress ipAddress = InetAddress.getByName(ip);
                    mSocket = new DatagramSocket();
                    mSocket.connect(ipAddress, port); //连接
    
                    //设置timeout
                    //mSocket.setSoTimeout(3000);
                    Log.d(TAG_log,"udp connect = "+isConnect());
    
                    if(isConnect()){
                        isStop = false;
                        uiHandler.sendEmptyMessage(1);
                    }
                    /* 此处这样做没什么意义不大,真正的socket未连接还是靠心跳发送,等待服务端回应比较好,一段时间内未回应,则socket未连接成功 */
                    else{
                        uiHandler.sendEmptyMessage(-1);
                        Log.e(TAG_log,"SocketThread connect fail");
                        return;
                    }
    
                }
                catch (IOException e) {
                    uiHandler.sendEmptyMessage(-1);
                    Log.e(TAG_log,"SocketThread connect io exception = "+e.getMessage());
                    e.printStackTrace();
                    return;
                }
                Log.d(TAG_log,"SocketThread connect over ");
    
                //发送一次,否则不发送则收不到,不知道为啥。。。
                sendByteCmd(new byte[]{00},-1);//send once
    
                //read ...
                while (isConnect() && !isStop && !isInterrupted()) {
    
                    int size;
                    try {
                        byte[] preBuffer = new byte[4 * 1024];//预存buffer
                        receivePacket = new DatagramPacket(preBuffer, preBuffer.length);
                        mSocket.receive(receivePacket);
    
                        if (receivePacket.getData() == null) return;
                        size = receivePacket.getLength();     //此为获取后的有效长度,一次最多读64k,预存小的话可能分包
                        Log.d(TAG_log, "pre data size = "+receivePacket.getData().length + ", value data size = "+size);
                        byte[] dataBuffer = Arrays.copyOf(preBuffer, size);
    
                        if (size > 0) {
                            Message msg = new Message();
                            msg.what = 100;
                            Bundle bundle = new Bundle();
                            bundle.putByteArray("data",dataBuffer);
                            bundle.putInt("size",size);
                            bundle.putInt("requestCode",requestCode);
                            msg.setData(bundle);
                            uiHandler.sendMessage(msg);
                        }
                        Log.i(TAG_log, "SocketThread read listening");
                        //Thread.sleep(100);//log eof
                    }
                    catch (IOException e) {
                        uiHandler.sendEmptyMessage(-1);
                        Log.e(TAG_log,"SocketThread read io exception = "+e.getMessage());
                        e.printStackTrace();
                        return;
                    }
                }
            }
        }
    
    
    
        //==============================socket connect============================
        private String ip;
        private int port;
        /**
         * connect socket in thread
         * Exception : android.os.NetworkOnMainThreadException
         * */
        public void connect(String ip, int port){
            this.ip = ip;
            this.port = port;
            mSocketThread = new SocketThread(ip, port);
            mSocketThread.start();
        }
    
        /**
         * socket is connect
         * */
        public boolean isConnect(){
            boolean flag = false;
            if (mSocket != null) {
                flag = mSocket.isConnected();
            }
            return flag;
        }
        
        /**
         * socket disconnect
         * */
        public void disconnect() {
            isStop = true;
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
            if (mSocketThread != null) {
                mSocketThread.interrupt();//not intime destory thread,so need a flag
            }
        }
    
    
    
        /**
         * send byte[] cmd
         * Exception : android.os.NetworkOnMainThreadException
         * */
        public void sendByteCmd(final byte[] mBuffer,int requestCode) {
            this.requestCode = requestCode;
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        InetAddress ipAddress = InetAddress.getByName(ip);
                        sendPacket = new DatagramPacket(mBuffer, mBuffer.length, ipAddress, port);
                        mSocket.send(sendPacket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
        }
    
    
    
    
        Handler uiHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch(msg.what){
                    //connect error
                    case -1:
                        if (null != onDataReceiveListener) {
                            onDataReceiveListener.onConnectFail();
                            disconnect();
                        }
                        break;
    
                    //connect success
                    case 1:
                        if (null != onDataReceiveListener) {
                            onDataReceiveListener.onConnectSuccess();
                        }
                        break;
    
                    //receive data
                    case 100:
                        Bundle bundle = msg.getData();
                        byte[] buffer = bundle.getByteArray("data");
                        int size = bundle.getInt("size");
                        int mequestCode = bundle.getInt("requestCode");
                        if (null != onDataReceiveListener) {
                            onDataReceiveListener.onDataReceive(buffer, size, mequestCode);
                        }
                        break;
                }
            }
        };
    
        
        /**
         * socket response data listener
         * */
        private OnDataReceiveListener onDataReceiveListener = null;
        private int requestCode = -1;
        public interface OnDataReceiveListener {
            public void onConnectSuccess();
            public void onConnectFail();
            public void onDataReceive(byte[] buffer, int size, int requestCode);
        }
        public void setOnDataReceiveListener(
                OnDataReceiveListener dataReceiveListener) {
            onDataReceiveListener = dataReceiveListener;
        }
        
    
    }
    
    
    MainActivity.java:使用,简单明了
    private void initListener(){
            //socket connect
            btn_connect.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String ip = et_ip.getText().toString();
                    String port = et_port.getText().toString();
    
                    if(TextUtils.isEmpty(ip)){
                        Toast.makeText(UdpActivity.this,"IP地址为空",Toast.LENGTH_SHORT).show();
                        return;
                    }
                    if(TextUtils.isEmpty(port)){
                        Toast.makeText(UdpActivity.this,"端口号为空",Toast.LENGTH_SHORT).show();
                        return;
                    }
    
                    connect(ip, Integer.parseInt(port));
                }
            });
    
            //socket disconnect
            btn_disconnect.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    disconnect();
                }
            });
    
            //socket send
            btn_send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (UdpClient.getInstance().isConnect()) {
                        byte[] data = et_send.getText().toString().getBytes();
                        send(data);
                    } else {
                        Toast.makeText(UdpActivity.this,"尚未连接,请连接Socket",Toast.LENGTH_SHORT).show();
                    }
                }
            });
    
            //clear receive
            btn_clear.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    tv_receive.setText("");
                }
            });
        }
    
        /**
         * socket data receive
         * */
        private void initDataReceiver(){
            UdpClient.getInstance().setOnDataReceiveListener(dataReceiveListener);
        }
    
        /**
         * socket connect
         * */
        private void connect(String ip, int port){
            UdpClient.getInstance().connect(ip, port);
        }
    
        /**
         * socket disconnect
         * */
        private void disconnect(){
            UdpClient.getInstance().disconnect();
            tv_state.setText("未连接");
        }
    
        /**
         * socket send
         * */
        private void send(byte[] data){
            String ip = et_ip.getText().toString();
            String port = et_port.getText().toString();
    
            UdpClient.getInstance().sendByteCmd(data,1001);
        }
    
    
        /**
         * socket data receive
         * data(byte[]) analyze
         * */
        private UdpClient.OnDataReceiveListener dataReceiveListener = new UdpClient.OnDataReceiveListener() {
            @Override
            public void onConnectSuccess() {
                Log.i(TAG_log,"onDataReceive connect success");
                tv_state.setText("已连接");
            }
    
            @Override
            public void onConnectFail() {
                Log.e(TAG_log,"onDataReceive connect fail");
                tv_state.setText("未连接");
            }
    
            @Override
            public void onDataReceive(byte[] buffer, int size, int requestCode) {
                //获取有效长度的数据
                byte[] data = new byte[size];
                System.arraycopy(buffer, 0, data, 0, size);
    
                final String oxValue = Arrays.toString(HexUtil.Byte2Ox(data));
                Log.i(TAG_log,"onDataReceive requestCode = "+requestCode + ", content = "+oxValue);
    
                tv_receive.setText(tv_receive.getText().toString() + oxValue + "\n");
    
            }
        };
    
    
    
        @Override
        protected void onDestroy() {
            UdpClient.getInstance().disconnect();
            super.onDestroy();
        }
    
    

    2019 (* ̄(oo) ̄) 诸事顺利!

    相关文章

      网友评论

        本文标题:Android 基于UDP的Socket通信

        本文链接:https://www.haomeiwen.com/subject/aewbeqtx.html