阻塞式IO和非阻塞式IO

什么是阻塞式IO,什么是非阻塞式IO?区分他们有何用?

 

       阻塞式IO:IO即input/output,阻塞式IO指的是“一旦输入/输出工作没有完成,则程序阻塞,直到输入/输出工作完成”。在目前,我们从书本上学到的语法用的基本都是阻塞式IO。比如c语言的stdio.h库的所有函数(包含scanf(),getchar(),gets()等函数),Java的BIO(比如各类输入输出流)。他们都是不见黄河心不死的好汉。在你满足他们的条件之前,不让你的程序继续往下跑。最简单的例子:c语言的scanf()函数——当你scanf()要求输入两个数字时,你只输入一个数字,它也不会让你继续执行接下来的代码的。

 

       非阻塞式IO:非阻塞式IO其实也并非完全非阻塞,通常都是通过设置超时来读取数据的。未超时之前,程序阻塞在读写函数上;超时后,结束本次读取,将已读到的数据返回。通过不断循环读取,就能够读到完整数据了。如果多次连续超时读到空数据的话,则可以断开。C语言的Socket可以使用setsockopt()来设置recv()超时(通常也就Socket需要考虑超时)。而JAVA有两种非阻塞式IO——AIO和NIO。说真的,我自己都没去看他们具体是怎么用的……所以我就不阐述了。

 

       有啥用:就说说非阻塞IO的严重缺点吧。如果你企图保持socket的长连接,Server在规定的时间内没有读到Client的心跳,然而你Server读取socket传来的数据,调用的输入函数是阻塞式IO(如下),那么即便Client没有心跳了(Client已经断开了连接),你的Server程序仍然会被阻塞在该输入函数下无法继续等待数据结束标志(比如换行符)。然而,由于Client已经断开了连接,所以Sever永远收不到数据……这个线程就卡在这里结束不了,单纯地浪费资源了。(这时候,心跳包机制也成了鸡肋)

 

以下是JAVA阻塞式IO例子代码:

 


 
  1. /*常见的阻塞式IO代码*/

  2. serverSocket = new ServerSocket(PORT);

  3. Socket client = serverSocket.accept();

  4.  
  5. BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));

  6.  
  7. while(1)

  8. {

  9. String reciver=in.readLine();//当输入流没有换行时,在此阻塞

  10. Thread.sleep(1);

  11. }

       那么,JAVA能否用阻塞式IO实现非阻塞式IO呢?

       我这么问肯定是可以的。

       之前用Java写的Server端代码,用的是一个连接对应一个线程的方式,发现Client结束长连接却不能终止Server的连接线程,但是又懒得去看NIO和AIO的资料,因为可能会动到之前写的代码(基本上把整个代码结构都推倒了),所以就偷了个懒,使用BIO来实现非阻塞式IO。


使用java的阻塞式IO实现非阻塞式IO

       实现:相信学过操作系统原理的人都听说过生产者消费者问题吧。我也称这种实现方式为生产者消费者方式,然而目的和内容大相径庭。操作系统中的生产者消费者问题主要解决的问题是资源共享问题。而我解决的是供需问题。实现步骤如下——①写一个生产者类作为消费者的子线程:使用队列存放每一次读到的数据,不论是心跳还是目标数据都塞入队列。当队列不为空的时候,表示该socket口仍在接受数据,否则已经断开。②父线程(也就是消费者)不断向生产者索取数据,如果生产者在n*T的时间间隔内不断地返回“队列为空”的状态,则断开该socket,终止该消费者线程。

 

       原理:通过父线程终止,强迫子线程(生产者类)抛出异常,从而终止输入输出流的持续阻塞状态。

 

以下是生产者类的代码:

 


 
  1. /*生产者类*/

  2. class dataProducer implements Runnable

  3. {

  4. BufferedReader in=null;

  5. Queue<String> inputQueue=new LinkedList<String>();

  6. public dataProducer(InputStream _inputer)

  7. {

  8. in=new BufferedReader(new InputStreamReader(_inputer));

  9. }

  10. public boolean isActive() //用于父线程判断生产者是否有数据可读

  11. {

  12. if(inputQueue.isEmpty())return false;

  13. else return true;

  14. }

  15. public String next() //获得队列的下一个数据

  16. {

  17. return inputQueue.remove();

  18. }

  19. public void run()

  20. {

  21. try

  22. {

  23. while(true)

  24. {

  25. String reciver=in.readLine();

  26. inputQueue.add(reciver);

  27. Thread.sleep(10);

  28. }

  29. }

  30. catch(IOException | InterruptedException e) //被强迫抛出异常后,该线程会执行以下代码

  31. {

  32. System.out.println("Forced interrupted the readline() successfully.");

  33. e.printStackTrace();

  34. }

  35. finally

  36. {

  37. try

  38. {

  39. if(in!=null)in.close();

  40. }

  41. catch (IOException e)

  42. {

  43. e.printStackTrace();

  44. }

  45. }

  46. }

  47. }


以下是父线程调用生产者类的代码:

 


 
  1. long lasstime=System.currentTimeMillis();

  2. long interval=0;

  3. try

  4. {

  5. dataProducer rdl=new dataProducer(client.getInputStream());

  6. Thread thrdl=new Thread(rdl);

  7. thrdl.start();

  8. while(interval<16000)

  9. {

  10. interval=System.currentTimeMillis()-lasttime;

  11. if(!rdl.isActive()){Thread.sleep(10);continue;}

  12. String reciver=rdl.next();

  13. if(reciver 是数据) //判断姿势取决于你的心跳包和数据包怎么设计

  14. {

  15. //处理数据;

  16. }

  17. lasttime=System.currentTimeMillis(); //更新lasttime

  18. Thread.sleep(1);

  19. }

  20. }

  21. catch(Exception e)

  22. {

  23. //异常处理

  24. }

  25. finally

  26. {

  27. //资源回收

  28. }


 

怎么使用c语言实现非阻塞式socket线程?  

       实现:C语言使用socket实现长连接的话,可以通过setsockopt()来设置recv()的超时,自动跳出阻塞重新进入循环。能够设置超时的话,将超时时间设成n*T,再加个判断就行了。需要注意的是,设置recv()超时的同时,accpet()阻塞也会超时。没力气贴代码了,感觉能够设置超时还是挺方便的。

 

--------------------- 本文来自 Leaviathan 的优快云 博客 ,全文地址请点击:https://blog.youkuaiyun.com/qq_21882325/article/details/67053462?utm_source=copy

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值