【问题标题】:TCP Client socket hanging the Server SocketTCP Client socket 挂了 Server Socket
【发布时间】:2023-06-14 23:04:01
【问题描述】:

我正在开发一个 TCP/IP 套接字程序来连接我的 Android 手机和我的服务器 PC。

我创建了一个简单的回显程序。

连接到服务器后,我可以毫无问题地发送我的注册命令/延迟/挂起。

使用缓冲读取器接收数据后,我将数据传递给主要活动中的函数,实习生通过写入 OutputStream 将此数据发送到服务器。当我的客户端发送此数据时,我的服务器挂起(忙等待 -> 我的猜测)但 5 秒后它恢复正常操作并成功接收数据。甚至我的工作线程 (AsyncTask) 也挂起,这很明显,因为与其他消息相比,它们在 Logcat 中显示消息的延迟明显。

Android Studio 3.3.1

目标 API 级别:>= 26 (8.0 Oreo)

TCP 测试服务器 - Hercules

@SuppressLint("StaticFieldLeak")
public void connect(){
    new AsyncTask<Void, Void, Boolean>(){

        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                socket  = new Socket(ip, portNo);
                out     = socket.getOutputStream();
                in      = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                socketAlive.set(true);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if(result){
                sendData("RegisterDevice:<ip>:<mac>");
                readerThread();
            }
            else{
                socketAlive.set(false);
                reconnect();
            }
        }
    }.execute();
}
@SuppressLint("StaticFieldLeak")
public void sendData(final String data){
    if(this.socket == null || this.socket.isClosed() )
    {
        //Unable to send data as output is closed returning
        return;
    }
    new AsyncTask<String, Void, Boolean>(){
        @Override
        protected Boolean doInBackground(String... writeString) {
            try {

out.write(writeString[0].getBytes()); // -->> Casing Server Socket to 挂了

                //out.flush();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if(!result) {
                reconnect();
                sendData(data);
            }
        }
    }.execute(data);
}

@SuppressLint("StaticFieldLeak")
public void readerThread(){
    if(this.socket.isClosed())
    {
        //Unable to recieve data as output is closed returning
        return;
    }
    new AsyncTask<Void, Void, Boolean>(){
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                Arrays.fill(readBuffer, '\0');
                //in.read(readBuffer, 0, Ethernet.TCP_READ_BUFFER_SIZE);//8192
                in.read(readBuffer);
                return  true;

            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if(result) {
                //dataRead = String.copyValueOf(readBuffer, 0, Ethernet.TCP_READ_BUFFER_SIZE);
                String str = String.copyValueOf(readBuffer);
                str.trim();
                Log.i("Test", "Test");
                mainActivity.processEthernetData(String.copyValueOf(readBuffer, 0, Ethernet.TCP_READ_BUFFER_SIZE));
                Log.i("ReadInfo", String.copyValueOf(readBuffer, 0, Ethernet.TCP_READ_BUFFER_SIZE));
                readerThread();
            }
            else {
                reconnect();
            }
        }
    }.execute();
}

public void processEthernetData(final String protcol){

    eth.sendData(protcol);
}

【问题讨论】:

  • 唯一可以挂起ServerSocket 的是它的accept() 方法,并且只有在没有客户端套接字和连接时才会发生。您的代码大多是垃圾,依赖于 isClosed() 和朋友不存在的属性,忽略 read() 返回的值,执行毫无意义的数组初始化等。
  • 那么为什么server和正在执行out.write的线程一起挂了,然后5秒后server又恢复运行,甚至成功接收并显示已经传输的数据???
  • 使用 tcpdump 或 Wireshark 进行诊断。为什么flush() 电话被评论了?
  • 当我尝试使用 BufferedWriter 进行传输时使用了 Flush,但使用 OutputStringStream 则没有必要。

标签: java android sockets


【解决方案1】:

问题是由

引起的

mainActivity.processEthernetData(String.copyValueOf(readBuffer, 0, Ethernet.TCP_READ_BUFFER_SIZE));

显然,创建并传递给函数的字符串的顺序是TCP_READ_BUFFER_SIZE,哪个值是 8192。当通过 TCP/IP 传输这么大的字符串时,它会导致我的服务器暂时挂起,直到它收到完整的数据。

我通过将行更改为解决了这个错误

mainActivity.processEthernetData(String.copyValueOf(readBuffer, 0, ethernetBytesRead)); 

【讨论】: