无法从 Android 中 ServerSocket 主机上的 URLConnection 客户端接收内容

问题描述 投票:0回答:0

以下类作为线程运行,从相当空的 Android 应用程序调用(即,该应用程序实际上仅用于生成线程,然后将消息发布到 URLSender)。观察到的行为是建立了连接(即调用

socket.isConnected()
返回 true),但是
while(socket.isConnected){}
只是循环而没有在
inputStream.read()
上接收到任何内容,所以
while (bytesRead != -1){}
永远不会被输入。

添加备注:

我将

ServerSocketHost
URLSender
移动到 vanilla Java 控制台驱动程序中,行为看起来是一样的。因此,问题似乎是从
URLConnection
发送方到
ServerSocket
接收方的实际转移。

有什么建议吗?

服务器端接收客户端通信的实现...

public class ServerSocketHost implements Runnable {

    public  static final int         HOST_PORT   = 45000;
    public  static final byte[]      HOST_IP     = new byte[]{127, 0, 0, 1};
    private static InetSocketAddress hostAddress = null;

    private static final int DEFAULT_TIMEOUT = 5000;
    private static final int CONNECT_BACKLOG = 50;

    private boolean      okToRun      = true;
    private ServerSocket serverSocket = null;
    private int          timeoutValue = -1;

    public ServerSocketHost() throws IOException {
        this(DEFAULT_TIMEOUT);
    }

    public ServerSocketHost(int timeoutValue) throws IOException {
        this.timeoutValue = timeoutValue;
        getServerAddress();
        openServerSocket();
    }

    private void openServerSocket() throws IOException {
        serverSocket = new ServerSocket(HOST_PORT);
        serverSocket.setSoTimeout(timeoutValue);
    }

    public void shutdown() {
        /* will cause run() to terminate execution on evaluation of "while (okToRun)", either after
             input has been read or a timeout has occurred
         */
        okToRun = false;
    }

    @Override
    public void run() {
        BufferedInputStream   inputStream      = null;
        ByteArrayOutputStream byteArrayOS      = null;
        int                   initialArraySize = 1024;
        byte[]                byteInput        = new byte[8]; // '8' is an arbitrary number
        int                   bytesRead        = -1;

        while (okToRun) {
            Socket socket = null;
            try {
                if(serverSocket.isClosed()) {
                    // ServerSocket is closed on timeout, so reopen if needed
                    openServerSocket();
                }

                socket = serverSocket.accept();
                while (socket.isConnected()) {
                    inputStream = new BufferedInputStream(socket.getInputStream());
                    byteArrayOS = new ByteArrayOutputStream(initialArraySize);
                    /* BEGIN CONSTRAINED IMPLEMENTATION
                        the following implementation (using InputStream.read() vice readAllBytes()) is
                      constrained by the target Android API. InputStream.readAllBytes() is not supported
                      until API 33, and this implementation targets API 29.
                     */
                    bytesRead = inputStream.read(byteInput);
                    while (bytesRead != -1) {
                        byteArrayOS.write(byteInput, 0, bytesRead);
                        bytesRead = inputStream.read(byteInput);
                    }
                    byteArrayOS.flush();
                }
            } catch(IOException e) {
                // should handle the exception, but for now just swallow it
                e.printStackTrace();
            } 
        }
    }
}

客户端的实现,将消息发布到内部队列,然后将这些消息发布到服务器/主机...

public class URLSender implements Runnable {

    private boolean           okToRun          = true;
    private URL               hostURL          = null;
    private List<String>      synchronizedList = null;

    public URLSender() throws MalformedURLException {
        this.hostURL          = new URL( "http://127.0.0.1:45000");
        this.synchronizedList = Collections.synchronizedList(new LinkedList<String>());
    }

    public void shutdown() {
        okToRun = false;
    }

    public void postMessage(String message) {
        synchronized (synchronizedList) {
            synchronizedList.add(message + " (1)\n");
            synchronizedList.add(message + " (2)\n");
            synchronizedList.add(message + " (3)\n");
            synchronizedList.notify();
        }
    }

    public void run() {
        URLConnection      urlConnection      = null;
        OutputStreamWriter outputStreamWriter = null;

        try {
            while (okToRun) {
                if (synchronizedList.isEmpty()) {
                    synchronized (synchronizedList) {
                        synchronizedList.wait();
                    }
                }

                try {
                    urlConnection = hostURL.openConnection();
                    urlConnection.setDoOutput(true);
                    outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream());
                } catch(IOException e) {
                    // should handle the exception, but for now just swallow it
                    e.printStackTrace();
                }
                while (synchronizedList.size() > 0) {
                    try {
                        String localMessage = synchronizedList.remove(0);
                        byte[] bytes2write  = localMessage.getBytes();

                        outputStreamWriter.write(localMessage);
                        outputStreamWriter.flush();
                    } catch (MalformedURLException e) {
                        // ?? okay to swallow this exception, as it should have been thrown in constructor
                        e.printStackTrace();
                    } catch (IOException e) {
                        // should handle the exception, but for now just swallow it
                        e.printStackTrace();
                    }
                }
                try {
                    outputStreamWriter.close();
                    ((HttpURLConnection) urlConnection).disconnect();
                } catch(IOException e) {
                    // should handle the exception, but for now just swallow it
                    e.printStackTrace();
                }
            }
        } catch(InterruptedException e) {
            // swallow the exception (i.e., assume thread is being stopped)
        }
    }
}

Android 清单,包含使用 HTTP 支持明文的条目

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

    <!--
          Entries "android:networkSecurityConfig" and "android:usesCleartextTraffic" added to
          support use of http communications
      -->
    <application
        android:networkSecurityConfig="@xml/xml_security_config"
        android:usesCleartextTraffic="true"

        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.URLConnect"
        tools:targetApi="29">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.URLConnect.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

相关的网络安全配置...

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- * * *  FOR DEMONSTRATION PURPOSES ONLY  * * *
      Network configuration exists solely to allow the URL socket to be used with plain HTTP
      protocol. In a real-world implementation, communication using HTTPS protocol should be
      employed, with appropriate certificates.
      -->
    <base-config cleartextTrafficPermitted="true"></base-config>

    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">127.0.0.1</domain>
    </domain-config>
</network-security-config>
java android serversocket urlconnection
© www.soinside.com 2019 - 2024. All rights reserved.