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

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java网络编程系列之JavaIO的“前世”:BIO阻塞模型相关的知识,希望对你有一定的参考价值。


Scoket与ServerSocket

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

服务器端: ServerSocket代码实战

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代码实战

//客户端
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)时各个流的关闭问题

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 关闭流

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);
    


优化

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

//客户端
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();
           
       
        
    

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();
                
            
        
    



以上是关于java网络编程系列之JavaIO的“前世”:BIO阻塞模型的主要内容,如果未能解决你的问题,请参考以下文章

小师妹学JavaIO之:文件File和路径Path

小师妹学JavaIO之:文件File和路径Path

JVM系列第1讲:Java 语言的前世今生

javaio流之字节输入流:java.io.InputStream类及子类java.io.FileInputStream

javaio流之字符输入流:java.io.Reader类及子类的子类java.io.FileReader

javaIO流之字节到字符流的转换流