【问题标题】:Android TCP does not flush until socket is closed [duplicate]在套接字关闭之前,Android TCP 不会刷新 [重复]
【发布时间】:2012-08-04 12:49:37
【问题描述】:

我一直在尝试各种实现来完成这项工作,并在 StackOverflow 和 Android 开发人员中搜索了解决方案,但我在编程方面没有太多经验,无法让这个代码块正常工作。

我的意图:

  1. 这是一个线程,将循环检查是否有 outMessage,如果有,它将发送消息。
  2. 接下来,它会检查流内是否有任何内容,如果有,它会将其发送到我的主要活动中的处理程序。
  3. 最后,它会休眠 1 秒,然后再次检查。
  4. 这应该允许我多次读/写,而无需关闭和打开套接字。

问题:

  • 在我关闭套接字之前,外流不会被刷新。 flush() 似乎没有效果。

我的要求:

  • 请发布使此代码正常工作所需的更改,如上文所述(任何解释为什么的注释将不胜感激。指向其他类似问题/答案的链接将非常有助于我学习,但我一直在看那些几个星期,但无法让它工作,所以请确保您还包括此代码所需的更改,以便按上述方式工作。提前致谢。

其他:

  • 我想知道我的插播和/或插播是否需要查找行尾字符?
  • 这里会使用 TCP_NODELAY 之类的东西吗?
  • 我们将不胜感激任何可以提供的额外信息。我想好好学习这方面的东西,但目前我什么都做不了。

代码:

 public void run() {                        
        while (connectionStatus == TCP_SOCKET_STATUS_CONNECTED) {
            try {   
                if (outMessage != null){
                   OutStream.writeBytes(outMessage);
                   OutStream.flush();           
                   outMessage = ("OUT TO SERVER: " + outMessage);           
                // socketClient.close();     
                   sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , outMessage);
                   outMessage = null;               
                } 
                if (InStream != null) {                     
                    String modifiedSentence = InStream.readLine();      
                    sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "\n" + "IN FROM SERVER: " + modifiedSentence);
            }
            Thread.sleep(1000);
        } catch (IOException e) {               
            connectionLost();
            break;
        } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }                           
}

制作套接字的线程:

public void run() {
        if(I) Log.i(LOGTAG, "Attempt Connection with IP: " + serverIP + " ...");
        setName("AttemptConnectionThread");
        connectionStatus = TCP_SOCKET_STATUS_CONNECTING;
        try {
            SocketAddress sockaddr = new InetSocketAddress(serverIP, port);
            tempSocketClient = new Socket(); // Create an unbound socket

            // This method will block no more than timeoutMs. If the timeout occurs, SocketTimeoutException is thrown.
            tempSocketClient.connect(sockaddr, timeoutMs);
            OutStream = new DataOutputStream(tempSocketClient.getOutputStream());
            InStream = new BufferedReader(new InputStreamReader(tempSocketClient.getInputStream()));
            socketClient = tempSocketClient;
            socketClient.setTcpNoDelay(true);
            connected(); 
        } catch (UnknownHostException e) {
            if(I) Log.i(LOGTAG,"     ...UnknownException e: e.getMessage() shows: " + e.getMessage());
            connectionFailed();
        } catch (SocketTimeoutException e) {
            if(I) Log.i(LOGTAG,"     ...SocketTimoutException e: e.getMessage() shows: " + e.getMessage());
            connectionFailed();
        } catch (IOException e) {
            if(I) Log.i(LOGTAG,"     ...caught on run()");
            // Close the socket
            try {
                tempSocketClient.close();
            } catch (IOException e2) {
                Log.e(LOGTAG, "unable to close() socket during connection failure", e2);
            }
            if(I) Log.i(LOGTAG,"     ...IOException e: e.getMessage() shows: " + e.getMessage());
            connectionFailed();
            return;
        }
    } 

我在网上找到并使用的 java 服务器,直到我将它移植到真实服务器:

public class Server {

private static String SERVERIP;

/**
 * @param args
 * @throws IOException
 */

public static void main(String[] args) throws IOException {
    String clientSentence;
    String capitalizedSentence;

    try {
        ServerSocket welcomeSocket = new ServerSocket(8888);
        getIp();
        System.out.println("Connected and waiting for client input!\n");

        while (true) {
            Socket connectionSocket = welcomeSocket.accept();
            BufferedReader inFromClient = new BufferedReader(
                    new InputStreamReader(connectionSocket.getInputStream()));
            DataOutputStream outToClient = new DataOutputStream(
                    connectionSocket.getOutputStream());

            clientSentence = inFromClient.readLine();
            String ip = connectionSocket.getInetAddress().toString()
                    .substring(1);
            System.out.println("In from client (" + ip + "): "
                    + clientSentence);
            if (clientSentence != null) {
                capitalizedSentence = clientSentence.toUpperCase() + '\n';
                System.out.println("Out to client (" + ip + "): "
                        + capitalizedSentence);
                outToClient.writeBytes(capitalizedSentence + "\n");
            }

        }
    } catch (IOException e) {
        // if server is already running, it will not open new port but
        // instead re-print the open ports information
        getIp();
        System.out
                .println("Server connected and waiting for client input!\n");

    }
}

private static void getIp() {
    InetAddress ipAddr;
    try {
        ipAddr = InetAddress.getLocalHost();
        System.out.println("Current IP address : "
                + ipAddr.getHostAddress());

    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}
}

【问题讨论】:

  • 消息有多大?它们是在不同的线程中生成的吗?没有锁定?
  • 首先,消息大多是小字符串。诸如“转身”或“直走”之类的东西。也可能有多线的,但仍然很短。其次,如果您的意思是阻止,我不这么认为,但我对它的工作方式/如何检查感到有些困惑。我很确定它只会在创建套接字时阻塞。这应该在任何地方阻塞吗?如果您确实是指“锁定”,我不知道在这种情况下是什么意思。
  • 我的意思是 锁定 避免线程之间的竞争条件。我的问题是outMessage 是在哪里创建的?
  • mSocket.setTcpNoDelay(on);是我如何解释this,但是eclipse要我初始化“on”我设置do boolean on = true; ?我应该在创建套接字时执行这些操作,因为状态应该是持久的?
  • outMessage 是我服务中的静态成员。它目前是一个字符串,但一旦套接字正常工作,我最终会将其更改为队列。此方法位于服务内的嵌套类中。所以我可以在可以绑定到服务的多个活动之间保持套接字打开

标签: android sockets tcp flush dataoutputstream


【解决方案1】:

我怀疑你正在读取消费者的行,但你没有写行,所以消费者阻塞,直到它获得 EOS 并提供一个大行。发送时根据需要添加行终止符。

【讨论】:

  • 这解决了我发布的具体问题,但它直接导致我进入另一个问题。我的线程挂在 inputStream.readLine() 上,我发布了一个关于该问题的新问题here,以防您或其他任何人想看看。感谢您的帮助。
  • 我知道这个答案是 3-4 岁,但如果你有机会看到我的评论,你能否详细解释一下你所说的“但你没有写行”时的意思?看起来我遇到了类似的问题,这可能是原因
【解决方案2】:

由于这些是短消息 (< MSS),我猜堆栈可能正在实现 Nagle 的算法。服务器是否在执行某种延迟的 ACK?如果可能,您应该捕获一个跟踪并查看是否有来自另一端的待处理确认。

无论哪种情况,TCP_NODELAY 都会有所帮助。

【讨论】:

  • 1. ACK 代表什么?我用谷歌搜索了它,但找不到定义 2。我会给你和 Nikolai 建议的 TCP_NODELAY 一个镜头,如果它确实回答了问题,则标记为已回答。感谢您迄今为止的帮助!
  • ACK 是 tcp 确认。服务器可能正在等待将它们捆绑在一起,而不是为每个段发送 ACK。如果客户端实现了 Nagle 的算法,它会在发送具有
  • 好的,我更新了问题以在我的线程中显示创建套接字以及 Java 服务器的 run 方法。你会看到我有 socketClient.setTcpNoDelay(true);已经在那里了,但它没有修复它。该文档有它应该是 socketClient.setTcpNoDelay(on);但后来我需要初始化。我有点困惑我应该初始化什么“on”来制作 socketClient.setTcpNoDelay(on);工作吗?
  • 关于 TCP 跟踪,我是从 android(客户端)还是计算机(服务器)端执行此操作?我在哪里/如何实现它?它只是 socketClient. 吗?
  • 您可以在服务器端使用 tcpdump 进行跟踪。
猜你喜欢
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 2014-07-24
相关资源
最近更新 更多