java网络编程系列之JavaIO的“前世”:BIO阻塞模型

2021-12-08 16:02:17 浏览数 (1)

java网络编程系列之JavaIO的“前世”:BIO阻塞模型

  • Scoket与ServerSocket
    • 服务器端: ServerSocket代码实战
    • 客户端: socket代码实战
    • 运行简单的服务器客户端实例
      • 补充知识点 > 1 --- 多重流嵌套(比如BufferedWrite)时各个流的关闭问题
    • 优化

Scoket与ServerSocket

  • bind:提供给ServerSocket端口进行绑定操作,客户端只需要向指定的端口发送数据,服务器就可以收到数据
  • accept: 阻塞式调用,等待客户端与其建立连接,如果没有客户端与其建立连接,那么accept函数就会阻塞住当前线程
  • connect:客户端socket通过指定的服务器的主机和端口,与指定的服务器进程相连接
  • 一旦服务器的accept函数,接收了对应的客户端连接,便会返回一个socket对象,就是服务器进程和指定客户端进行通信的一个端点
  • 连接建立成功后,变可以通过I/O流进行数据的传输
  • 当数据传输完毕后,客户端调用close函数,关闭对应的端点,服务器端如果此时尝试向客户端发送数据,便会抛出异常,因此服务器端也应该关闭端点

服务器端: ServerSocket代码实战

代码语言:javascript复制
public class Server
{
    public static void main(String[] args) {
     final  int DEFAULT_PORT=8080;
     ServerSocket serverSocket=null;
        try {
            //监听端口
            serverSocket=new ServerSocket(DEFAULT_PORT);
            System.out.println("服务器启动,监听端口为: " DEFAULT_PORT);
            //与客户端进行数据交换
            while(true)
            {
                //等待客户端连接--这里会阻塞住,知道有客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("客户端[" socket.getPort() "]已连接");
                //获取客户端的输入数据流
                BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //获取向客户端写入数据流
                BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                //读取客户端发送的消息
                String line = br.readLine();
               if(line!=null)
               {
                   System.out.println("客户端[" socket.getPort() "]发送的消息:" line);
                   //向客户端写入数据
                   bw.write("服务器: " line "n");
                   //刷新缓冲区的数据,确保全部写入
                   bw.flush();
               }
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            //关闭流---也可以用try with resources
            if(serverSocket!=null)
            {
                try {
                    serverSocket.close();
                      System.out.println("关闭服务端的serverSocket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端: socket代码实战

代码语言:javascript复制
//客户端
public class Client
{
    public static void main(String[] args) {
      //服务器的主机地址
        final String DEFAULT_SERVER_HOST="127.0.0.1";
        //服务器的端口
        final int DEFAULT_SERVER_PORT=8080;
        Socket socket=null;
        BufferedWriter bw = null;
        try {
            //创建sokcet
            socket=new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
           //创建IO流
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //让用户输入信息
            BufferedReader consoleBr=new BufferedReader(new InputStreamReader(System.in));
           String input=consoleBr.readLine();
           //发送消息给服务器
            bw.write(input "n");
            bw.flush();

            //读取服务器返回的消息
            String line = br.readLine();
            System.out.println(line);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
      //关闭最外层的writer,会自动去flush缓冲区里面的剩余内容
      //关闭writer,也就关闭了socket      
       if(bw!=null)
       {
           try {
               bw.close();
               System.out.println("关闭客户端的socket");
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
        }
    }
}

运行简单的服务器客户端实例

补充知识点 > 1 — 多重流嵌套(比如BufferedWrite)时各个流的关闭问题

代码语言:javascript复制
public
class FilterOutputStream extends OutputStream {
    protected OutputStream out;
    
    public FilterOutputStream(OutputStream out) {
	this.out = out;
    }
 
    public void flush() throws IOException {
	out.flush();
    }
 
    public void close() throws IOException {
	try {
	  flush();
	} catch (IOException ignored) {
	}
	out.close();
    }
}

这是jdk1.6 中FilterOutputStream流的部分实现代码(我是粘贴过来的)。从这段代码可以看出,嵌套流关闭时直接关闭的是被封装流,只是在关闭之前flush。

因此关闭最外层的流即可

也可以考虑使用try with resource 关闭流

代码语言:javascript复制
public static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

优化

上面的例子中,客户端发送一次数据给服务器端后,便会自动断开连接,这样显然不太好,所以我们下面进行了优化

代码语言:javascript复制
//客户端
public class Client
{
    public static void main(String[] args) {
      //服务器的主机地址
        final String DEFAULT_SERVER_HOST="127.0.0.1";
        //服务器的端口
        final int DEFAULT_SERVER_PORT=8080;
        //停止
        final String QUIT="quit";
        Socket socket=null;
        BufferedWriter bw = null;
        try {
            //创建sokcet
            socket=new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
           //创建IO流
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //让用户输入信息
            BufferedReader consoleBr=new BufferedReader(new InputStreamReader(System.in));
             String input=null;
           while(!QUIT.equals(input=consoleBr.readLine()))
          {
              //发送消息给服务器
              bw.write(input "n");
              bw.flush();

              //读取服务器返回的消息
              String line = br.readLine();
              System.out.println(line);
          }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
      //关闭最外层的writer,会自动去flush缓冲区里面的剩余内容
      //关闭writer,也就关闭了socket
       if(bw!=null)
       {
           try {
               bw.close();
               System.out.println("关闭客户端的socket");
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
        }
    }
}
代码语言:javascript复制
public class Server
{
    public static void main(String[] args) {
     final  int DEFAULT_PORT=8080;
     ServerSocket serverSocket=null;
        try {
            //监听端口
            serverSocket=new ServerSocket(DEFAULT_PORT);
            System.out.println("服务器启动,监听端口为: " DEFAULT_PORT);
            //等待客户端连接--这里会阻塞住,知道有客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("客户端[" socket.getPort() "]已连接");
            //获取客户端的输入数据流
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //获取向客户端写入数据流
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //读取客户端发送的消息
            String line=null;
            //与客户端进行数据交换
            while((line = br.readLine())!=null)
            {
                   System.out.println("客户端[" socket.getPort() "]发送的消息:" line);
                   //向客户端写入数据
                   bw.write("服务器: " line "n");
                   //刷新缓冲区的数据,确保全部写入
                   bw.flush();
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            //关闭流---也可以用try with resources
            if(serverSocket!=null)
            {
                try {
                    serverSocket.close();
                    System.out.println("关闭服务端的serverSocket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

0 人点赞