java网络编程 BufferedReader的readLine方法读不到数据且一直阻塞

这篇具有很好参考价值的文章主要介绍了java网络编程 BufferedReader的readLine方法读不到数据且一直阻塞。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近在整理Java IO相关内容,会遇到一些以前没有注意的问题,特此记录,以供自查和交流。

需求:

基于Java的BIO API,实现简单的客户端和服务端通信模型,客户端使用BufferedReader的readLine方法读取System.in上的用户输入,然后通过字节输出流发送给服务端,服务端使用BufferedReader的readLine方法读取客户端的数据,进行打印;

问题:

服务端没有打印出客户端发送的数据,且卡在BufferedReader的readLine方法处

上代码:

客户端:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 基于BIO的TCP网络通信的客户端,接收控制台输入的数据,然后通过字节流发送给服务端
 *
 */
class ChatClient {
    public static void main(String[] args) throws IOException {
        // 连接server
        Socket serverSocket = new Socket("localhost", 9999);
        System.out.println("client connected to server");

        // 读取用户在控制台上的输入,并发送给服务器
        InputStream in = System.in;
        // sendDataToServerByByteStream(in, serverSocket.getOutputStream()); //服务端可以正常接收
        sendDataToServerByCharStream(in, serverSocket.getOutputStream()); //服务端无法正常接收
    }

    /**
     * 通过字节流发送数据给服务端
     */
    private static void sendDataToServerByByteStream(InputStream in, OutputStream outputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        // read操作阻塞,直到有数据可读
        while ((len = in.read(buffer)) != -1) {
            System.out.println("client receive data from console" + in + " : " + new String(buffer, 0, len));
            // 发送数据给服务器端
            outputStream.write(new String(buffer, 0, len).getBytes()); // 此时buffer中是有换行符的
        }
    }

    /**
     * 通过字符流,使用readLine发送数据给服务端
     */
    private static void sendDataToServerByCharStream(InputStream in, OutputStream outputStream) {
        String content = null;
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("client send data: " + content);
                outputStream.write(content.getBytes()); // 字节流,没有添加换行符
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

服务端代码:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 基于BIO的TCP网络通信的服务端,可以接收多个客户端连接,通过字节流接收客户端发送的消息;
 * 一个客户端需要使用一个线程
 * todo:线程资源复用
 *
 * @author freddy
 */
class ChatServer {
    public static void main(String[] args) throws IOException {
        // 开启server 监听端口
        ServerSocket serverSocket = new ServerSocket(9999);
        while (true) {
            Socket client = serverSocket.accept(); // 阻塞操作,需要新的线程处理客户端
            // 接收Client数据,并转发
            new Thread(new ServerThread(client)).start();
        }
    }
}

/**
 * 服务端的线程,一个客户端对应一个
 */
class ServerThread implements Runnable {
    Socket socket;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("server had a client" + socket);
        // 获取输入流程,读取用户输入
        // 持续接收Client数据,并打印
        readDataFromClientByCharStream(); // 无法正常读取客户端发送过来的数据
    }

    /**
     * 使用字符流读取客户端的数据,主要使用readLine
     */
    private void readDataFromClientByCharStream() {
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            String content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("serer receive data from " + socket + " : " + content);
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            System.out.println("Client disconnect ");
        }
    }   
}

先运行服务端,在启动客户端,然后在客户端的控制台发送数据:

可以看到,客户端和服务端之间已经建立了连接,但是服务端并没有打印日志,说明服务端的程序卡在了代码1这个地方。

为什么呢?

那我们需要去看java.io.BufferedReader#readLine()这个方法的源码:

基于debug方式,我们可以看到java.io.BufferedReader#readLine()这个方法

先调用java.io.BufferedReader#fill方法读取输入流的内容

bufferedreader.readline()解决阻塞,java,bio,BufferedReader,readLine

可以看到,这里读取到的内容是hello 5个字符,没有换行符;

fill方法调用完后,回到readLine方法的charLoop中:

可以看到,for循环中有个条件,当读取到的字节中包含'\n' 或者 '\r'的时候,会设置eol = true,后面会根据该eol标志,return读取到的字符串,结束readLine方法;

当读取到的字节中没有'\n' 或者 '\r'的时候,eol = false,readLine方法就会回到

bufferedreader.readline()解决阻塞,java,bio,BufferedReader,readLine

bufferLoop循环中的fill方法继续读取输入流程中的内容:

如果输入流中有内容,会读取后继续判断是否有换行符:'\n' 或者 '\r'

如果输入流中没有内容,那么fill方法会阻塞在java.io.Reader#read(char[], int, int)方法:

bufferedreader.readline()解决阻塞,java,bio,BufferedReader,readLine

bufferedreader.readline()解决阻塞,java,bio,BufferedReader,readLine

这就是服务端的代码阻塞在java.io.BufferedReader#readLine()的原因;

解决问题:

找到问题后,那么我们就好解决问题了:

解决思路如下:

1.服务端仍然使用java.io.BufferedReader#readLine()读取客户端的数据的话,那么客户端发送数据时,就必须代换行符

1.1  客户端在发送完用户数据后,继续Socket.getOutputStream().write("\r\n".getBytes());发送换行符;

1.2 调用增强的输出流的api,直接发送数据的同时发送换行符:

比如:PrintWriter pw = new PrintWriter(outputStream, true);

pw.println(content); // 添加换行符

1.3 调整客户端获取用户输入数据的方式,把用户的换行符直接读取过来后,用原来的方式发送

    private static void sendDataToServerByByteStream(InputStream in, OutputStream outputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        // read操作阻塞,直到有数据可读
        while ((len = in.read(buffer)) != -1) {
            System.out.println("client receive data from console" + in + " : " + new String(buffer, 0, len));
            // 发送数据给服务器端
            outputStream.write(new String(buffer, 0, len).getBytes()); // 此时buffer中是有换行符的
        }
    }

2.服务端调整数据读取方式

客户端使用java.io.DataOutputStream#writeUTF(java.lang.String)发送给数据

服务端使用java.io.DataInputStream#readUTF()方法接收数据

这种方式,是相当于客户端在发送数据的时候,给数据规定了格式,服务端可以根据约定的格式,来正确读取数据;类似于java.io.DataOutputStream#writeShort方法

关于这种思想,用的地方很多

常用来解决RPC发送数据的粘包问题

在常用的RPC框架,如Netty中就有使用;在大数据框架如MapReduce中也有writeShort类似方式序列号和反序列话;

完整的客户端和服务端验证代码如下:

客户端:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 基于BIO的TCP网络通信的客户端,接收控制台输入的数据,然后通过字节流发送给服务端
 *
 */
class ChatClient {
    public static void main(String[] args) throws IOException {
        // 连接server
        Socket serverSocket = new Socket("localhost", 9999);
        System.out.println("client connected to server");

        // 读取用户在控制台上的输入,并发送给服务器
        InputStream in = System.in;
        // sendDataToServerByByteStream(in, serverSocket.getOutputStream()); //服务端可以正常接收
        sendDataToServerByCharStream(in, serverSocket.getOutputStream()); // 服务端无法正常接收
    }

    /**
     * 通过字节流发送数据给服务端
     */
    private static void sendDataToServerByByteStream(InputStream in, OutputStream outputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        // read操作阻塞,直到有数据可读
        while ((len = in.read(buffer)) != -1) {
            System.out.println("client receive data from console" + in + " : " + new String(buffer, 0, len));
            // 发送数据给服务器端
            outputStream.write(new String(buffer, 0, len).getBytes()); // 此时buffer中是有换行符的
        }
    }

    /**
     * 通过字符流,使用readLine发送数据给服务端
     */
    private static void sendDataToServerByCharStream(InputStream in, OutputStream outputStream) {
        String content = null;
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("client send data: " + content);
                outputStream.write(content.getBytes()); // 字节流,没有添加换行符
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过字符流,使用readLine发送数据给服务端
     */
    private static void sendDataToServerByCharStream2(InputStream in, OutputStream outputStream) {
        String content = null;
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("client send data: " + content);
                outputStream.write(content.getBytes()); // 字节流,没有添加换行符
                outputStream.write("\r\n".getBytes());
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过字符流,使用readLine发送数据给服务端
     */
    private static void sendDataToServerByCharStream3(InputStream in, OutputStream outputStream) {
        String content = null;
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
            PrintWriter pw = new PrintWriter(outputStream, true);) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("client send data: " + content);
                pw.println(content); // 添加换行符
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void sendDataToServerByCharStream4(InputStream in, OutputStream outputStream) {
        String content = null;
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
            DataOutputStream pw = new DataOutputStream(outputStream);) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("client send data: " + content);
                pw.writeUTF(content);
                pw.flush();
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

服务端:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 基于BIO的TCP网络通信的服务端,可以接收多个客户端连接,通过字节流接收客户端发送的消息;
 * 一个客户端需要使用一个线程
 * todo:线程资源复用
 *
 * @author freddy
 */
class ChatServer {
    public static void main(String[] args) throws IOException {
        // 开启server 监听端口
        ServerSocket serverSocket = new ServerSocket(9999);
        while (true) {
            Socket client = serverSocket.accept(); // 阻塞操作,需要新的线程处理客户端
            // 接收Client数据,并转发
            new Thread(new ServerThread(client)).start();
        }
    }
}

/**
 * 服务端的线程,一个客户端对应一个
 */
class ServerThread implements Runnable {
    Socket socket;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("server had a client" + socket);
        // 获取输入流程,读取用户输入
        // 持续接收Client数据,并打印
        readDataFromClientByCharStream(); // 无法正常读取客户端发送过来的数据
    }

    /**
     * 使用字节流读取客户端的数据
     */
    private void readDataFromClientByByteStream() {
        try (InputStream inputStream = socket.getInputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            // read操作阻塞,直到有数据可读
            while ((len = inputStream.read(buffer)) != -1) {
                System.out.println("serer receive data from " + socket + " : " + new String(buffer, 0, len));
            }
        } catch (IOException e) {
            System.out.println(socket + " disconnect ");
        }
    }

    /**
     * 使用字符流读取客户端的数据,主要使用readLine
     */
    private void readDataFromClientByCharStream() {
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            String content = bufferedReader.readLine();
            while (content != "exit") {
                System.out.println("serer receive data from " + socket + " : " + content);
                content = bufferedReader.readLine();
            }
        } catch (IOException e) {
            System.out.println("Client disconnect ");
        }
    }

    /**
     * 使用字符流读取客户端的数据,主要使用readLine
     */
    private void readDataFromClientByCharStream2() {
        try (DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());) {
            // 此时content中已经没有换行符了,
            // 并且如果原始字节流中没有换行符,该方法内部会循环等待换行符,相当于阻塞在这里
            String content = dataInputStream.readUTF();
            while (content != "exit") {
                System.out.println("serer receive data from " + socket + " : " + content);
                content = dataInputStream.readUTF();
            }
        } catch (IOException e) {
            System.out.println("Client disconnect ");
        }
    }
}

参考:java网络编程 BufferedReader的readLine方法读不到数据的原因_java后台服务端bufferedreader不能读全数据 前台出现超时提示-CSDN博客文章来源地址https://www.toymoban.com/news/detail-859063.html

到了这里,关于java网络编程 BufferedReader的readLine方法读不到数据且一直阻塞的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机网络技术与JAVA网络编程UDP编程-----JAVA入门基础教程-----计算机网络经典

    import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.*; public class UDP { public static void main(String[] args) { DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(); InetAddress inetAddress = InetAddress.getByName(\\\"127.0.0.1\\\"); int port = 9090; byte[] byte

    2024年02月15日
    浏览(37)
  • Java网络编程(一)基本网络概念

            网络(network) 是几乎可以实时相互发送和接收数据的计算机和其他设备的集合。网络通常用线缆连接,数据位转换为电磁波,通过线缆移动。不过,无线网络会通过无线电波传输数据,许多长距离的传输现在会用通过玻璃纤维发送可见光的光纤电缆来完成。传输数

    2024年02月16日
    浏览(35)
  • Java 网络编程 —— 安全网络通信

    SSL(Secure Socket Layer,安全套接字层)是一种保证网络上的两个节点进行安全通信的协议。IETF(Interet Engineering Task Force)国际组织对 SSL 作了标准化,制定了 RFC2246 规范,并将其称为传输层安全(Transport Layer Security,TLS) SSL 和 TLS 都建立在 TCP/IP 的基础上,一些应用层协议,如

    2024年02月11日
    浏览(30)
  • Java网络编程基础

    Java网络编程基于TCP/UDP协议的基础之上,TCP/IP协议是一个协议簇。里面包括很多协议的,UDP只是其中的一个, 之所以命名为TCP/IP协议,因为TCP、IP协议是两个很重要的协议,就用他两命名了。那么首先我们先介绍一下TCP和UDP的特点: 1.TCP(Transmission Control Protocol,传输控制协议

    2024年02月08日
    浏览(28)
  • JAVA网络编程(一)

    定义 :在网络通信协议下,不同计算机上运行的程序,进行的数据传输。 应用场景 :即时通信,网游,邮件等 不管什么场景,都是计算机与计算机之间通过网络在进行数据传输 java提供一个java.net包,可以帮助我们开发网络应用程序。 CS架构是指在远端有一个服务器Server,

    2024年02月07日
    浏览(26)
  • 【Java】网络编程

    Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基 于Socket套接字的网络程序开发就是网络编程 流套接字 : 使用传输层TCP协议 特点 有连接,可靠传输,面向字节流,有接收缓冲区也有发送缓冲区,大小不限 对于字节流来说,可以简单

    2024年02月10日
    浏览(29)
  • Java的网络编程

    两台设备之间通过网络实现数据传输,将数据通过网络从一台设备传输到另一台设备 网络 两台或多台设备通过一定物理设备连接起来构成了网络 网络又分为: 局域网:覆盖范围最小,仅仅覆盖一个教室或一个机房 城域网:覆盖范围较大,可以覆盖一个城市 广域网:覆盖范围最大

    2024年02月12日
    浏览(26)
  • Java 网络编程(大全)

    读者手册(必读)_云边的快乐猫的博客-CSDN博客 一、1网络通信的基本模式分为两种 1.CS模式 (Client---客户端,Server---服务端)  客户端是需要程序员去开发的,例如日常使用的各种的APP,服务端就是服务器。 例子:端游,依赖特定的PC端才能玩。 2.BS模式 (Browser---浏览器,

    2024年02月12日
    浏览(29)
  • Java18:网络编程

    一.对象序列化: 1.对象流: ObjectInputStream 和 ObjectOutputStream 2.作用: ObjectOutputSteam:内存中的对象--存储中的文件,通过网络传输出去 ObjectInputStream:存储中的文件,通过网络传输出去--内存中的对象 3.对象的序列化机制: 对象序列化机制允许把内存中的java对象转换成平台无关

    2024年01月21日
    浏览(30)
  • Java——网络编程

    InetAddress类 java.net.InetAddress类用来封装计算机的IP地址和DNS(没有端口信息),它包括一个主机名和一个ip地址,是java对IP地址的高层表示。大多数其他网络类都要用到这个类,包括Sorket、ServerSocker、URL、DatagramSorket、DatagramPacket等 常用静态方法 getLocalHost()得到本机的InetAddress对象,其

    2024年03月16日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包