Moto G5 Plus无法通过Socket连接到本地服务器

时间:2018-03-26 12:03:29

标签: android sockets android-6.0-marshmallow android-7.0-nougat motorola

我有一个智能手机必须通过SSLSocket连接到本地服务器的应用程序。我在5款不同的智能手机上测试了我的应用程序:Moto G2(6.0),Redmi 3S(6.0.1),LG K5(6.0),Moto G5 Plus(7.1.1)和OnePlus 5(8.0)。 Moto G5 Plus是唯一一个能够解决这个问题的人。

这是导致问题行为的行。所有测试都在同一网络上完成。

socket = (SSLSocket) sslContext.getSocketFactory().createSocket(serverAddress, serverPort);

对于这种行为,Moto G5 Plus或Android 7+是否存在任何已知问题?

编辑:更多测试导致Android系统在确定已连接WiFi接口但没有互联网时试图强制Socket通过移动网络连接的想法。 有没有办法强制Socket使用WiFi而不是移动网络?

2 个答案:

答案 0 :(得分:3)

免责声明:我没有对此进行测试,所以我真的不确定它是否有效。

Network类有一个bind(Socket)方法,也许你可以找到wifi网络,然后将它绑定到你的套接字。从文档来看,这似乎是你需要的,它说:

/**
 * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
 * will be sent on this {@code Network}, irrespective of any process-wide network binding set by
 * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
 */

Socket在绑定到网络之前不应该连接,所以我认为您应该使用socketFactory.createSocket()创建它,并在绑定后才连接它。

所以,你应该先找到你的Network(Kotlin):

val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val wifiNetwork = connectivityManager.allNetworks.firstOrNull {
    val info = connectivityManager.getNetworkInfo(it)
    info.type == ConnectivityManager.TYPE_WIFI
}

或(Java)

ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
Network wifiNetwork = null;
for(Network network : connectivityManager.getAllNetworks()){
    NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
    if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI){
        wifiNetwork = network;
        break;
    }
}

然后将其绑定到Socket并最终连接(Kotlin):

wifiNetwork?.bindSocket(socket)
val socketAddress = InetSocketAddress(hostname, port)
socket.connect(socketAddress)

或(Java)

if(wifiNetwork != null){
    wifiNetwork.bindSocket(socket);
}
InetSocketAddress socketAddress = InetSocketAddress(hostName, port);
socket.connect(socketAddress);

注意,它需要ACCESS_NETWORK_STATE权限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

答案 1 :(得分:2)

我希望它可以帮到你,我刚刚在github上找到了你的解决方案。 有关详细信息和官方链接,请check this out,我认为这对您有所帮助。 如果不是答案,请忽略这个答案。

我们正在使用AsyncTask来避免网络访问的StrictMode致命错误(查看引用)。 StrictMode策略只是禁止我们影响UI线程。

   /* AsyncTask class which manages connection with server app and is sending shutdown command.
   */
public class ShutdownAsyncTask extends AsyncTask<String, String, TCPClient> {

    private static final String     COMMAND     = "shutdown -s"      ;
    private              TCPClient  tcpClient                        ;
    private              Handler    mHandler                         ;
    private static final String     TAG         = "ShutdownAsyncTask";

    /**
     * ShutdownAsyncTask constructor with handler passed as argument. The UI is updated via handler.
     * In doInBackground(...) method, the handler is passed to TCPClient object.
     * @param mHandler Handler object that is retrieved from MainActivity class and passed to TCPClient
     *                 class for sending messages and updating UI.
     */
    public ShutdownAsyncTask(Handler mHandler){
        this.mHandler = mHandler;
    }

    /**
     * Overriden method from AsyncTask class. There the TCPClient object is created.
     * @param params From MainActivity class empty string is passed.
     * @return TCPClient object for closing it in onPostExecute method.
     */
    @Override
    protected TCPClient doInBackground(String... params) {
        Log.d(TAG, "In do in background");

        try{
            tcpClient = new TCPClient(mHandler,
                                      COMMAND,
                                      "192.168.1.1",
                                      new TCPClient.MessageCallback() {
                @Override
                public void callbackMessageReceiver(String message) {
                    publishProgress(message);
                }
            });

        }catch (NullPointerException e){
            Log.d(TAG, "Caught null pointer exception");
            e.printStackTrace();
        }
        tcpClient.run();
        return null;
    }   

在这个AsyncTask中,我们正在创建TCPClient对象(如下所述)。在TCPClient构造函数中,我们传递Handler对象以更改UI,COM​​MAND - 带有&#34; shutdown -s&#34的字符串;关闭计算机的命令,IP号码 - 服务器IP号码;回调对象 - 当我们获得服务器响应时,回调方法&#39; messageCallbackReceiver&#39;正在开始发布进度&#39;方法,即将进度发布到onProgressUpdate&#39; AsyncTask的方法。

        /**
         * Overriden method from AsyncTask class. Here we're checking if server answered properly.
         * @param values If "restart" message came, the client is stopped and computer should be restarted.
         *               Otherwise "wrong" message is sent and 'Error' message is shown in UI.
         */
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            Log.d(TAG, "In progress update, values: " + values.toString());
            if(values[0].equals("shutdown")){
                tcpClient.sendMessage(COMMAND);
                tcpClient.stopClient();
                mHandler.sendEmptyMessageDelayed(MainActivity.SHUTDOWN, 2000);

            }else{
                tcpClient.sendMessage("wrong");
                mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
                tcpClient.stopClient();
            }
        }

收到正确的消息后,我们正在发送命令,或者如果收到错误的消息,我们正在发送消息&#34;错误&#34;并停止客户。在此之后,我们将被转移到'onPostExecute&#39;方法:

    @Override
        protected void onPostExecute(TCPClient result){
            super.onPostExecute(result);
            Log.d(TAG, "In on post execute");
            if(result != null && result.isRunning()){
                result.stopClient();
            }
            mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 4000);

        }
    }

一步一步:

- &gt; AsyncTask正在创建TCPClient对象。

- &gt;在TCPClient构造函数中,我们传递Handler,Command,IP Number和Callback对象。

- &gt;当TCPClient开始连接时,它会发送消息&#34; shutdown&#34;到服务器。

- &gt;当我们从服务器接收消息时,回调会将其传递给&#39; onProgressUpdate&#39;。

- &gt;如果收到的消息(来自服务器的响应)等于&#34; shutdown&#34;,我们正在向服务器发送命令。

- &gt;发送后我们停止客户端,将客户转移到&#39; onPostExecute&#39;方法

- &gt;同时,处理程序正在接收带有“消息”的空消息。 MainActivity中定义的整数,负责更新GUI。

小部件UI的更新方式示例:

       mHandler = new Handler(){
        public void handleMessage(Message msg) {
            switch(msg.what){
                case SHUTDOWN:
                    Log.d(mTag, "In Handler's shutdown");

                     views     = new RemoteViews(context.getPackageName(), R.layout.activity_main);
                     widget    = new ComponentName(context, MainActivity.class);
                     awManager = AppWidgetManager.getInstance(context);
                                 views.setTextViewText(R.id.state, "Shutting PC...");
                                 awManager.updateAppWidget(widget,views);
                    break;

的TcpClient

此类负责维护连接。我会一步一步解释:

在第一步中,我们可以看到从ShutdownAsyncTask和其他人传递的对象。另外,我们可以看到sendMessage和stopClient方法。

    public class TCPClient {

        private static final String            TAG             = "TCPClient"     ;
        private final        Handler           mHandler                          ;
        private              String            ipNumber, incomingMessage, command;
                             BufferedReader    in                                ;
                             PrintWriter       out                               ;
        private              MessageCallback   listener        = null            ;
        private              boolean           mRun            = false           ;


        /**
         * TCPClient class constructor, which is created in AsyncTasks after the button click.
         * @param mHandler Handler passed as an argument for updating the UI with sent messages
         * @param command  Command passed as an argument, e.g. "shutdown -r" for restarting computer
         * @param ipNumber String retrieved from IpGetter class that is looking for ip number.
         * @param listener Callback interface object
         */
        public TCPClient(Handler mHandler, String command, String ipNumber, MessageCallback listener) {
            this.listener         = listener;
            this.ipNumber         = ipNumber;
            this.command          = command ;
            this.mHandler         = mHandler;
        }

        /**
         * Public method for sending the message via OutputStream object.
         * @param message Message passed as an argument and sent via OutputStream object.
         */
        public void sendMessage(String message){
            if (out != null && !out.checkError()) {
                out.println(message);
                out.flush();
                mHandler.sendEmptyMessageDelayed(MainActivity.SENDING, 1000);
                Log.d(TAG, "Sent Message: " + message);

            }
        }

        /**
         * Public method for stopping the TCPClient object ( and finalizing it after that ) from AsyncTask
         */
        public void stopClient(){
            Log.d(TAG, "Client stopped!");
            mRun = false;
        }

魔术发生在这里 - 在&#39; run()&#39;方法。我们在这里使用&#39; try-catch&#39;处理异常的工具(服务器未启用,ip不正确等)。正如您在下面看到的,我们有无限的while()循环来监听传入的消息。我们可以简单地停止它并使用&#39; stopClient()&#39;方法(用于ShutdownAsyncTask&#39; onProgressUpdate&#39;方法)

    public void run() {

            mRun = true;

            try {
                // Creating InetAddress object from ipNumber passed via constructor from IpGetter class.
                InetAddress serverAddress = InetAddress.getByName(ipNumber);

                Log.d(TAG, "Connecting...");

                /**
                 * Sending empty message with static int value from MainActivity
                 * to update UI ( 'Connecting...' ).
                 *
                 * @see com.example.turnmeoff.MainActivity.CONNECTING
                 */
                mHandler.sendEmptyMessageDelayed(MainActivity.CONNECTING,1000);

                /**
                 * Here the socket is created with hardcoded port.
                 * Also the port is given in IpGetter class.
                 *
                 * @see com.example.turnmeoff.IpGetter
                 */
                Socket socket = new Socket(serverAddress, 4444);


                try {

                    // Create PrintWriter object for sending messages to server.
                    out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

                    //Create BufferedReader object for receiving messages from server.
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                    Log.d(TAG, "In/Out created");

                    //Sending message with command specified by AsyncTask
                    this.sendMessage(command);

                    //
                    mHandler.sendEmptyMessageDelayed(MainActivity.SENDING,2000);

                    //Listen for the incoming messages while mRun = true
                    while (mRun) {
                        incomingMessage = in.readLine();
                        if (incomingMessage != null && listener != null) {

                            /**
                             * Incoming message is passed to MessageCallback object.
                             * Next it is retrieved by AsyncTask and passed to onPublishProgress method.
                             *
                             */
                            listener.callbackMessageReceiver(incomingMessage);

                        }
                        incomingMessage = null;

                    }

                    Log.d(TAG, "Received Message: " +incomingMessage);

                } catch (Exception e) {

                    Log.d(TAG, "Error", e);
                    mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);

                } finally {

                    out.flush();
                    out.close();
                    in.close();
                    socket.close();
                    mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 3000);
                    Log.d(TAG, "Socket Closed");
                }

            } catch (Exception e) {

                Log.d(TAG, "Error", e);
                mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);

            }

        }

客户端的最后一件事是Callback接口。我们最后在TCPClient类中有它:

    /**
         * Callback Interface for sending received messages to 'onPublishProgress' method in AsyncTask.
         *
         */
        public interface MessageCallback {
            /**
             * Method overriden in AsyncTask 'doInBackground' method while creating the TCPClient object.
             * @param message Received message from server app.
             */
            public void callbackMessageReceiver(String message);
        }