【问题标题】:Android : Keep socket open to read message from serverAndroid:保持套接字打开以从服务器读取消息
【发布时间】:2015-03-25 16:34:01
【问题描述】:

我正在开发一个 Android 应用程序,

  • 首先,我必须通过 TCP 套接字连接到硬件服务器
  • 连接后,我必须向服务器发送*99*1##,然后服务器将响应“*#*1##*#*1##”
  • 然后,我需要保持此套接字处于活动状态并读取传入消息
  • 此后,服务器可以不时向我发送消息。但是,消息何时发送或消息长度未确定。
  • 每条消息都以“##”结尾,例如,*1*1*18##、*1*0*19##、*1*1*#4*11## 等。李>
  • 当客户端(此应用)收到消息时,它会通知 Activity 更新 UI。

所以,我创建了一个线程子类来做到这一点

public class ServerThread extends Thread {
    public interface OnReadListener {
        public void onRead(ServerThread serverThread, String response);
    }

    Socket socket;
    String address;
    int port;
    OnReadListener listener = null;

    public ServerThread(String address, int port) {
        this.address = address;
        this.port = port;
    }

    @Override
    public void run() {
        try {
            socket = new Socket(InetAddress.getByName(address), port);

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            PrintWriter writer = new PrintWriter(bw, true);
            writer.println("*99*1##");
            writer.flush();

            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line;

            while (!socket.isConnected() && !Thread.currentThread().isInterrupted()) {
                line = br.readLine();

                if (line != null) {
                    Log.i("Dev", "Line ")

                    if (listener != null) {
                        listener.onRead(this, line);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setListener(OnReadListener listener) {
        this.listener = listener;
    }
}

而在活动中,我是这样做的

public class MainActivity extends Activity {

    ServerThread st = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
    }

    @Override
    public void onResume() {
        startMonitoring();

        super.onResume();
    }

    @Override
    public void onPause() {
        stopMonitoring();

        super.onPause();
    }

    private void startMonitoring() {
        stopMonitoring();

        st = new ServerThread(SERVER_IP_ADDRESS, SERVER_PORT);
        st.setListener(new OnReadListener{
            @Override
            public void onRead(ServerThread serverThread, String response) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // update UI or do something with response
                    }
                })
            }
        });
        st.start();
    }

    private void stopMonitoring() {
        if (st != null) {
            st.stop();
            st = null;
        }
    }
}

在我开始这个活动后,我发现

  • 消息“*99*1##”已发送到服务器,我可以从服务器硬件中看到此消息。
  • 但是,我只收到来自服务器 '*#*1##*#*1##' 的第一行响应
  • 之后,套接字仍然连接(未打印堆栈跟踪),但我再也没有收到来自服务器的任何进一步消息。侦听器永远不会被调用。

我不知道如何使这项工作。欢迎任何建议。

您可能需要知道的一些注意事项

我不确定 BufferReader 是否是我工作所需的正确对象。因为当它读取失败时,它将返回 null 并且循环将继续运行。我可能需要一些可以冻结等待输入的线程的对象。该对象可以等待输入,因为服务器可能会在几秒钟、几分钟、几小时或更长时间内发送消息。收到消息后,继续执行代码并进入下一轮循环。

(我是全职 iOS Dev,对 Java 不是很熟悉)

最终编辑

我仔细检查代码后发现我犯的愚蠢错误

writer.println("*99*1##")

基本上,println 会发送“*99*1##”,然后跟着 newline。但是我的硬件服务器不喜欢这样,所以它终止了连接。这就是为什么我从 BufferReader 的 readLine() 中得到 null 的原因。

我改成之后

writer.print("*99*1##")

服务器收到“*99*1##”并保持连接。然后,我可以像 EJP 再次建议的那样循环读取响应

String line;
while ((line = br.readLine()) != null) {
    if (listener != null) {
        listener.onRead(this, line);
    }
}

if (listener != null) {
    listener.onTerminated(this);
}

【问题讨论】:

    标签: java android multithreading sockets tcp


    【解决方案1】:
    while (!socket.isConnected() ...
    

    问题就在这里。无论如何,测试是没有意义的,因为它永远不会是假的,但是否定它意味着受控块永远不会执行。只需删除 isConnected() 测试即可。

    读取失败时返回null,循环继续运行。

    因为您没有正确处理该案例。如果line 为空,则必须退出循环并关闭连接。通常的写法是:

    String line;
    while ((line = reader.readLine()) != null)
    {
        // ...
    }
    

    您在循环中的睡眠实际上是在浪费时间。

    【讨论】:

    • 我明白你在说什么。但是,请问是否有办法打开一个套接字,等待来自服务器的传入消息而不必循环?因为这个硬件是如何设计的(不是我设计的),所以我必须连接到套接字并保持连接状态。一旦硬件方面发生任何事情,服务器将通过这个连接的套接字通知外部设备。几年前我用 Objective-C 成功地做到了这一点,但在 Java 中卡住了。
    • 欢迎任何方法,因为我相信 BufferReader 可能不是解决此问题的正确工具。但是我对Java不太熟悉:|
    • 方法是在readLine()不返回null时循环。这里有数百个例子。
    • 问题中有一点,我必须保持套接字和服务器之间的连接打开消息。在套接字连接到服务器后,消息可能会在 5 分钟或半天后立即到达。所以,如果我确实喜欢你所说的,当我连接时,readLine() 将返回 *#*1##*#*1## 然后返回 null,然后在我收到任何消息之前退出循环并断开连接。如果我重新连接,它会是一样的(连接,接收第一条消息,断开连接)。无论如何要保持套接字打开,保持线程直到InputStream中有东西或服务器发送东西?
    • 所以你必须实现应用协议,不管它是什么。循环直到您阅读完整的回复,然后停止阅读。只有您可以确切地知道这涉及到什么,因为只有您知道您的应用程序协议是什么。
    【解决方案2】:

    试试这个

    while (isRunning) {
                line = br.readLine();
    
                if (line != null) {
                    Log.i("Dev", "Line ")
    
                    if (listener != null) {
                        listener.onRead(this, line);
                    }
                }
            }
    

    注意:isRunning 是一个布尔变量,用于控制您的线程生命周期。每当您想停止此线程时,请设置 isRunning = false 并在您的连接上调用 close()

    如果你想创建一个 TCP 服务器,你应该使用ServerSocket 而不是Socket。这是关于socket的java文档,但android使用相同的http://docs.oracle.com/javase/tutorial/networking/sockets/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-23
      • 2018-07-11
      相关资源
      最近更新 更多