Android phones and IP issues as SocketServer

I. Introduction: In order to push and keep alive Research Program

Tried to Android as a server connected to the server Socket long after Taiwan as a client Socket client, the background push, so the purpose is to ensure that the background does not need a long time to connect multiple devices, the background every time a new message, by creating a plurality of Client pushes Android Socket terminal device, the data transfer is completed, all of the client terminal is closed, the release of resources. The Android as SocketServer been listening port, in a suspended state, ensure real-time data push. Of course, this approach is obvious shortcomings: 1.Android long-pending power, once the Android terminal program 2 killed, not pushed to your phone, 3.Android no real application layer keep-alive, 4 is the biggest problem Android terminal. through 3G, 4G Internet, WIFI Internet, VPN access over the wall, as SocketServer need to provide the public IP network, so the end of the IP Android is how much? how to obtain?

Second, the development process:

2.1, with the above problems, the preparation and testing of Demo

2.2 Development Environment: Android 5.1 device, back Springboot integrated Tomcat service

2.3 specific code:

Background code

 

 

@RequestMapping("/efss/test")
@RestController
public class TestController extends BaseController {


    @RequestMapping("/uploadTerminalIp")
    public ApiResponse<Object> uploadTerminalIp(@RequestBody String json) {
        JSONObject jsonObject = JSONObject.parseObject(json);
        JSONObject jsonData = jsonObject.getJSONObject("nameValuePairs");
        String ip = jsonData.getString("ip");
        Integer port = jsonData.getInteger("port");
        if (StringUtils.isBlank(ip)) {
            return apiFail("ip地址为空");
        }
        if (null == port) {
            return apiFail("port为空");
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        String currentTimeStr = TimeUtil.getDate2String(new Date(), "yyyy-MM-dd HH:mm:ss") + ",随机码:" + new Random().nextFloat();
                        String responseStr = SocketUtil.tcpPost(ip, port.toString(), currentTimeStr);
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();

        return apiSuccess("Socket connected....!!!.............");
    }

}

Background analog interface directly write a timing every 5 seconds time to send a Android by Socket.

Under SocketUtil responsible for posted write-only (push messages), do not read, of course, if you need to add their own realization

/**
 * * Created by [email protected]
 * * on 2019/5/31
 * *
 */
public class SocketUtil {
    private static final Logger logger = LoggerFactory.getLogger(SocketUtil.class);

    /**
     * 发送socket请求
     *
     * @param clientIp
     * @param clientPort
     * @param msg
     * @return
     */
    public static synchronized String tcpPost(String clientIp, String clientPort, String msg) {
        String rs = "";

        if (clientIp == null || "".equals(clientIp) || clientPort == null || "".equals(clientPort)) {
            logger.error("Ip或端口不存在...");
            return null;
        }

        int clientPortInt = Integer.parseInt(clientPort);

        logger.info("clientIp:" + clientIp + " clientPort:" + clientPort);

        Socket s = null;
        OutputStream out = null;
        //InputStream in = null;
        try {
            s = new Socket(clientIp, clientPortInt);
            s.setSendBufferSize(4096);
            s.setTcpNoDelay(true);
            s.setSoTimeout(60 * 1000);
            s.setKeepAlive(true);
            out = s.getOutputStream();
            //in = s.getInputStream();

            //准备报文msg
            logger.info("准备发送报文:" + msg);

            out.write(msg.getBytes("GBK"));
            out.flush();



        } catch (Exception e) {
            logger.error("tcpPost发送请求异常:" + e.getMessage());
        } finally {
            logger.info("tcpPost(rs):" + rs);
            try {
                if (out != null) {
                    out.close();
                    out = null;
                }

                if (s != null) {
                    s.close();
                    s = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return rs;

    }
}

front end

package com.yxytech.parkingcloud.efsspda.service;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;

import com.yxytech.parkingcloud.baselibrary.utils.LogUtil;
import com.yxytech.parkingcloud.efsspda.R;
import com.yxytech.parkingcloud.efsspda.utils.NetWorkUtil;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * <p>文件描述:<p>
 * <p>作者:jambestwick<p>
 * <p>创建时间:2019/5/30<p>
 * <p>更新时间:2019/5/30<p>
 * <p>版本号:${VERSION}<p>
 */
public class SingASongService extends Service {
    private static final String TAG = SingASongService.class.getName();
    private MediaPlayer mMediaPlayer;
    private Thread thread;

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

    @Override
    public void onCreate() {
        super.onCreate();
        MyThread myThread = new MyThread();
        thread = new Thread(myThread);
        mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_notice);
        mMediaPlayer.setLooping(true);//循环播放

        LogUtil.d(TAG, "onCreate() 创建播放对象:" + mMediaPlayer.hashCode());

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        thread.start();
        LogUtil.d(TAG, "播放时 线程名称:" + thread.getName());
        return START_STICKY;
    }

    private void startPlaySong() {
        if (mMediaPlayer == null) {
            mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_notice);
            LogUtil.d(TAG, "音乐启动播放,播放对象为: " + mMediaPlayer.hashCode());
            mMediaPlayer.start();
        } else {
            mMediaPlayer.start();
            LogUtil.d(TAG, "音乐启动播放,播放对象为: " + mMediaPlayer.hashCode());
        }
        try {
            Thread.sleep(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        if (mMediaPlayer != null) {
//            mMediaPlayer.pause();
//            LogUtil.d(TAG, "音乐启动播放,播放对象为: " + mMediaPlayer.hashCode());
//            int progress = mMediaPlayer.getCurrentPosition();
//            LogUtil.d(TAG, "音乐暂停,播放进度:" + progress);
//        }
    }

    private void stopPlaySong() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            LogUtil.d(TAG, "音乐停止播放,播放对象为:" + mMediaPlayer.hashCode());
            LogUtil.d(TAG, "音乐播放器是否在循环:" + mMediaPlayer.isLooping());
            LogUtil.d(TAG, "音乐播放器是否还在播放:" + mMediaPlayer.isPlaying());
            mMediaPlayer.release();
            LogUtil.d(TAG, "播放对象销毁,播放对象为:" + mMediaPlayer.hashCode());
            mMediaPlayer = null;
        }
    }

    private void startSocketConn() {
        //服务端在8899端口监听客户端请求的TCP连接
        try {
            ServerSocket server = new ServerSocket(8899);
            Socket client = null;
            //通过调用Executors类的静态方法,创建一个ExecutorService实例
            //ExecutorService接口是Executor接口的子接口
            Executor service = Executors.newCachedThreadPool();
            while (true) {
                //等待客户端的连接
                System.out.println("等待客户端连接!"+new Date());
                LogUtil.e(NetWorkUtil.class, "等待客户端连接" );
                client = server.accept();
                System.out.println("与客户端连接成功!" + new Date());
                LogUtil.e(NetWorkUtil.class, "与客户端连接成功" );
                //调用execute()方法时,如果必要,会创建一个新的线程来处理任务,但它首先会尝试使用已有的线程,
                //如果一个线程空闲60秒以上,则将其移除线程池;
                //另外,任务是在Executor的内部排队,而不是在网络中排队
                service.execute(new NetWorkUtil.ServerThread(client));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //server.close();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mMediaPlayer.pause();
        LogUtil.d(TAG, "恢复播放 时当前播放器对象:" + mMediaPlayer.hashCode());
        stopPlaySong();
        LogUtil.d(TAG, "应用播放服务被杀死,正在重启");
        LogUtil.d(TAG, "目标播放工作线程是否存活:" + thread.isAlive());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(new Intent(getApplicationContext(), SingASongService.class));
        } else {
            startService(new Intent(getApplicationContext(), SingASongService.class));
        }
    }

    class MyThread implements Runnable {

        @Override
        public void run() {
            startPlaySong();
            startSocketConn();
        }
    }

}
package com.yxytech.parkingcloud.efsspda.utils;

import android.content.Context;
import android.util.Log;

import com.yxytech.parkingcloud.baselibrary.utils.LogUtil;
import com.yxytech.parkingcloud.baselibrary.utils.ToastUtil;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.util.Enumeration;

/**
 * <p>文件描述:<p>
 * <p>作者:jambestwick<p>
 * <p>创建时间:2019/5/31<p>
 * <p>更新时间:2019/5/31<p>
 * <p>版本号:${VERSION}<p>
 * <p>邮箱:[email protected]<p>
 */
public class NetWorkUtil {
    public static final int SOCKET_PORT = 8899;

    /**
     * 获取内网IP地址
     *
     * @return
     * @throws SocketException
     */
    public static String getLocalIPAddress() throws SocketException {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) {
                    return inetAddress.getHostAddress().toString();
                }
            }
        }
        return "null";
    }

    /****
     * 获取外网的IP地址
     * @return
     *
     *
     * ***/
    public static String getNetIp() {
        String IP = "";
        try {
            String address = "http://pv.sohu.com/cityjson?ie=utf-8";
            URL url = new URL(address);

            //URLConnection htpurl=url.openConnection();

            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setUseCaches(false);
            connection.setRequestMethod("GET");
            connection.setRequestProperty("user-agent",
                    "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.7 Safari/537.36"); //设置浏览器ua 保证不出现503

            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream in = connection.getInputStream();

                // 将流转化为字符串
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(in));

                String tmpString = "";
                StringBuilder retJSON = new StringBuilder();
                while ((tmpString = reader.readLine()) != null) {
                    retJSON.append(tmpString + "\n");
                }

                JSONObject jsonObject = new JSONObject(retJSON.toString());
                String code = jsonObject.getString("code");

                if (code.equals("0")) {
                    JSONObject data = jsonObject.getJSONObject("data");
                    IP = data.getString("ip") + "(" + data.getString("country")
                            + data.getString("area") + "区"
                            + data.getString("region") + data.getString("city")
                            + data.getString("isp") + ")";

                    Log.e("提示", "您的IP地址是:" + IP);
                } else {
                    IP = "";
                    Log.e("提示", "IP接口异常,无法获取IP地址!");
                }
            } else {
                IP = "";
                Log.e("提示", "网络连接异常,无法获取IP地址!");
            }
        } catch (
                Exception e)

        {
            IP = "";
            Log.e("提示", "获取IP地址时出现异常,异常信息是:" + e.toString());
        }
        return IP;

    }

    public static class serverPool {

        public static void conn(int port) throws IOException {
            //服务端在20006端口监听客户端请求的TCP连接
            final ServerSocket server = new ServerSocket(port);

            //在线程池中一共只有THREADPOOLSIZE个线程,
            //最多有THREADPOOLSIZE个线程在accept()方法上阻塞等待连接请求
            //匿名内部类,当前线程为匿名线程,还没有为任何客户端连接提供服务
            Thread thread = new Thread() {
                public void run() {
                    //线程为某连接提供完服务后,循环等待其他的连接请求
                    while (true) {
                        try {
                            //等待客户端的连接
                            Socket client = server.accept();
                            System.out.println("与客户端连接成功!");
                            //一旦连接成功,则在该线程中与客户端通信
                            ServerThread.execute(client);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            //先将所有的线程开启
            thread.start();

        }
    }

    /**
     * 该类为多线程类,用于服务端
     */
    public static class ServerThread implements Runnable {

        private Socket client = null;

        public ServerThread(Socket client) {
            this.client = client;
        }

        //处理通信细节的静态方法,这里主要是方便线程池服务器的调用
        public static void execute(Socket client) {
            String str = null;
            try {
                //获取Socket的输出流,用来向客户端发送数据
                PrintStream out = new PrintStream(client.getOutputStream());
                //获取Socket的输入流,用来接收从客户端发送过来的数据
                BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
                boolean flag = true;

                while (flag) {
                    //接收从客户端发送过来的数据
                    str = buf.readLine();
                    if (str == null || "".equals(str)) {
                        flag = false;
                    } else {
                        if ("bye".equals(str)) {
                            flag = false;
                        } else {
                            //将接收到的字符串前面加上echo,发送到对应的客户端
                            out.println("echo:" + str);
                            LogUtil.e(NetWorkUtil.class, "接受到的推送信息:" + str);
                        }
                    }
                }

                out.close();
                buf.close();
                client.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            execute(client);
        }

    }
}

This image is accessible above the background of the interface parameters required to do according to their business needs,

Such logic, Android end user logs in the APP, acquired by the IP phone NetWorkUtil.getNetIp () method, a port is defined (8899), sent to the background, the background IP port sent through, and is connected to the push Socket.

Third, the question:

Code is finished now that the problem

3.1 Android long as the Socket Server hangs long-term consumption, since we are customizing equipment, front-line staff have the charging device, and a frequency of up to 12 hours, in the case lock screen is sufficient.

3.2 Android process killed, this requires a lot of real machine adaptation, and we may command the user whitelist, thus greatly improving the survival rate.

3.3 Key: IP phone problem: see a lot of articles about the IP phone, only to come out the analysis, this solution is not feasible,

I WIFI LAN environment can be simulated, my background is 192.168.0.107, my phone is 192.168.0.105, port 8899 Socket phone, send and receive normal.

But the problem is in the public Internet, since you are within the network address, there may be more than one network device, exposed to the public Internet how to know which devices are connected it, so as not SocketServer can not do a network mapping,

Most front-line staff are also on the road, use the 4G network, and the IP address of the operator's card will be dynamic, as follows:

1.49.128.0, China, Guizhou, Anshun, telecommunications 
1.49.129.0, China, Guizhou, Anshun, telecommunications 
1.49.131.0, China, Guizhou, Anshun, telecommunications 
1.49.132.0, China, Guizhou, Anshun, telecommunications 
1.49.136.0, China, Guizhou, Guiyang, telecommunications 
1.49.192.0, China, Guizhou, southwestern Guizhou, telecommunications 
1.86.37.95, China, Xi'an, Telecom

We will not see these strange then, that the public is not really true in the sense of IP addresses, mobile Internet operators ISP IP is assigned automatically, every time the Internet ISP operators will choose a free IP to you to ensure that your IP phone and other phones are not the same at the same time. But these IP are belong to the network operator's base station IP.
A bit like a mobile Internet company in the online computer access through a gateway server. The outside world, all computers with the same IP address, which is the IP address of the gateway server.

So, to get this IP phone is not accessible to you via the public network.

to sum up:

Thus, do push, or the use of tripartite platform, such as: Aurora jpush_androidpush,

Or you might have to consider data security, it would open its own service, phone Pull pull message, in fact, there is a short connection interface to access the same.

Published 19 original articles · won praise 19 · views 30000 +

Guess you like

Origin blog.csdn.net/baidu_30084597/article/details/90719339