当连接到一个套接字时,当前线程将会被阻塞知道建立连接或产生超时位置。同样地,当通过套接字读写数据时,当前线程也会被阻塞知道操作成功或产生超时为止。
在交互式的应用中,也许会考虑为用户提供一个功能,用以取消那些不会成功的连接。但是当线程因套接字长时间无法响应而发生阻塞时,无法通过调用interrupt来接触阻塞。
为了中断套接字操作,可以使用java.nio包提供的一个特性 SocketChannel 类。
例: 打开SocketChannel
SocketChannel channe = SocketChannel.open(new InetSocketAddress(host, port));
通道(Channel)并没有与之相关联的流。实际上,它所拥有的read和write方法都是通过调用buffer对象来实现的。ReadableByteChannel接口和WritableByteChannel接口都继承了Channel。
如果不想处理缓存,可以使用Scanner类来读取信息,因为Scanner有一个带有ReadableByteChannel参数的构造器:
例:Scanner(ReadableByteChannel source)构造一个新的 Scanner,它生成的值是从指定信道扫描的。
Scanner in = new Scanner(channel);
通过调用静态方法Channels.newOutputStream(),可以从通道中获取输出流。
OutputStream outStream = Channels.newOutputStream(channel);
上述操作都是必须做的,假设线程正在执行打开、读取或写入操作,此时如果线程发生中断,那么这些操作将不会陷入阻塞,而是以抛出异常的方式结束。
例:分别通过SocketChannel和InputStream读取
SocketChannel:
messages.append("Blocking:\n"); SocketChannel channel = SocketChannel.open( new InetSocketAddress("localhost", 8189)); try{ in = new Scanner(channel); while(!Thread.currentThread().isInterrupted()){ if(in.hasNextLine()){ String line = in.nextLine(); } } }finally{ channel.close(); }
InputStream:
Socket socket = new Socket("localhost", 8189); try{ in = new Scanner(socket.getInputStream()); while(!Thread.currentThread().isInterrupted()){ if(in.hasNextLine()){ String line = in.nextLine(); } } }finally{ socket.close(); }
DEMO
import java.awt.EventQueue; import javax.swing.JFrame; public class InterruptibleSocketTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable(){ public void run(){ JFrame frame = new InterruptibleSocketFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } }
import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.channels.SocketChannel; import java.util.Scanner; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class InterruptibleSocketFrame extends JFrame { public static final int WIDTH = 300; public static final int HEIGHT = 300; private Scanner in; private JButton interruptibleButton; private JButton blockingButton; private JButton cancelButton; private JTextArea messages; private TestServer server; private Thread connectThread; public InterruptibleSocketFrame(){ setSize(WIDTH, HEIGHT); setTitle("InterruptibleSocketTest"); JPanel northPanel = new JPanel(); add(northPanel, BorderLayout.NORTH); messages = new JTextArea(); add(new JScrollPane(messages)); interruptibleButton = new JButton("Interruptible"); blockingButton = new JButton("Blocking"); northPanel.add(interruptibleButton); northPanel.add(blockingButton); interruptibleButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ interruptibleButton.setEnabled(false); blockingButton.setEnabled(false); cancelButton.setEnabled(true); connectThread = new Thread(new Runnable(){ public void run(){ try{ connectInterruptibly(); }catch(Exception e){ messages.append("\nInterruptibleSocketTest.connectInterruptible" + e); } } }); connectThread.start(); } }); blockingButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ interruptibleButton.setEnabled(false); blockingButton.setEnabled(false); cancelButton.setEnabled(true); connectThread = new Thread(new Runnable(){ public void run(){ try{ connectBlocking(); }catch(IOException e){ e.printStackTrace(); messages.append("\nInterruptibleSocketTest.connectInterruptible" + e); } } }); connectThread.start(); } }); cancelButton = new JButton("Cancel"); cancelButton.setEnabled(false); northPanel.add(cancelButton); cancelButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ connectThread.interrupt(); cancelButton.setEnabled(false); } }); server = new TestServer(); new Thread(server).start(); } public void connectInterruptibly() throws IOException{ messages.append("Blocking:\n"); SocketChannel channel = SocketChannel.open( new InetSocketAddress("localhost", 8189)); try{ in = new Scanner(channel); while(!Thread.currentThread().isInterrupted()){ messages.append("Reading "); if(in.hasNextLine()){ String line = in.nextLine(); messages.append(line); messages.append("\n"); } } }finally{ channel.close(); EventQueue.invokeLater(new Runnable(){ public void run(){ messages.append("Channel closed\n"); interruptibleButton.setEnabled(true); blockingButton.setEnabled(true); } }); } } public void connectBlocking() throws IOException{ messages.append("Blocking:\n"); Socket socket = new Socket("localhost", 8189); try{ in = new Scanner(socket.getInputStream()); while(!Thread.currentThread().isInterrupted()){ messages.append("Reading "); if(in.hasNextLine()){ String line = in.nextLine(); messages.append(line); messages.append("\n"); } } }finally{ socket.close(); EventQueue.invokeLater(new Runnable(){ public void run(){ messages.append("Socket closed\n"); interruptibleButton.setEnabled(true); blockingButton.setEnabled(true); } }); } } class TestServer implements Runnable{ public void run(){ try{ ServerSocket s = new ServerSocket(8189); while(true){ Socket incoming = s.accept(); Runnable r = new TestServerHandler(incoming); Thread t = new Thread(r); t.start(); } }catch(IOException e){ messages.append("\nTestServer.run: " + e); } } } class TestServerHandler implements Runnable{ private Socket incoming; private int counter; public TestServerHandler(Socket i){ incoming = i; } public void run(){ try{ OutputStream outStream = incoming.getOutputStream(); PrintWriter out = new PrintWriter(outStream, true); while(counter<100){ counter++; if(counter <= 10){ out.println(counter); } Thread.sleep(100); incoming.close(); messages.append("Closing server\n"); } }catch(Exception e){ messages.append("\nTestServerHandler.run: " + e); } } } }