可中断套接字(网络)

  当连接到一个套接字时,当前线程将会被阻塞直到建立连接或产生超时为止。同样地,当通过套接字读写数据时,当前线程也会被阻塞知道操作成功或产生超时为止。
  在交互式的应用中,也许会考虑为用户提供一个选项,用以取消那些不会成功的连接。但是当线程因套接字长时间无法响应而发生阻塞时,无法通过调用interrupt来解除阻塞。
  为了中断套接字操作,可以使用java.nio包提供的一个特性——SocketChannel 类。可以使用如下方法打开SocketChannel:

 

SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));

 

  通道(channel)并没有与之相关联的流。实际上,它所拥有的read和write方法都是通过使用Buffer对象来实现的。ReadableByteChannel接口和WritableByteChannel接口都声明了这两个方法。

  如果不想处理缓冲区,可以使用Scanner类从SocketChannel中读取信息,因为Scanner有一个带有ReadableByteChannel参数的构造器。

Scanner in = new Scanner(channel);

  通过调用静态方法Channels.newOutputStream,可以将通道转换成输出流。

OutputStream outStream = Channels.newOutputStream(channel);

  上述操作都是必须做的。假设线程正在执行打开、读取或写入操作,此时如果线程发生中断,那么这些操作将不会陷入阻塞,而是以抛出异常的方式结束。

  下面的程序对比了可中断套接字和阻塞套接字:服务器将连续发送数字,并在每发送十个数字之后停滞一下。点击两个按钮中的任何一个,都会启动一个线程来连连接服务器并打印输出。第一个线程使用可中断套接字,而第二个线程使用阻塞套接字。如果在第一批的十个数字的读取过程中点击“Cancel”按钮,这两个线程都会中断。

  但是,在第一批十个数字之后,就只能中断第一个线程了,第二个线程将保持阻塞直到服务器最终关闭连接。

package interruptible;



import java.awt.*;

import java.awt.event.*;

import java.util.*;

import java.net.*;

import java.io.*;

import java.nio.channels.*;

import javax.swing.*;



/**

 * This program shows how to interrupt a socket channel.

 * @author Cay Horstmann

 * @version 1.03 2012-06-04

 */

public class InterruptibleSocketTest

{

   public static void main(String[] args)

   {

      EventQueue.invokeLater(new Runnable()

         {

            public void run()

            {

               JFrame frame = new InterruptibleSocketFrame();

               frame.setTitle("InterruptibleSocketTest");

               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

               frame.setVisible(true);

            }

         });

   }

}



class InterruptibleSocketFrame extends JFrame

{

   public static final int TEXT_ROWS = 20;

   public static final int TEXT_COLUMNS = 60;



   private Scanner in;

   private JButton interruptibleButton;

   private JButton blockingButton;

   private JButton cancelButton;

   private JTextArea messages;

   private TestServer server;

   private Thread connectThread;



   public InterruptibleSocketFrame()

   {

      JPanel northPanel = new JPanel();

      add(northPanel, BorderLayout.NORTH);



      messages = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);

      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 (IOException e)

                        {

                           messages.append("\nInterruptibleSocketTest.connectInterruptibly: " + 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)

                        {

                           messages.append("\nInterruptibleSocketTest.connectBlocking: " + 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();

      pack();

   }



   /**

    * Connects to the test server, using interruptible I/O

    */

   public void connectInterruptibly() throws IOException

   {

      messages.append("Interruptible:\n");

      try (SocketChannel channel = SocketChannel.open(new InetSocketAddress("localhost", 8189)))

      {

         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

      {

         EventQueue.invokeLater(new Runnable()

         {

            public void run()

            {

               messages.append("Channel closed\n");

               interruptibleButton.setEnabled(true);

               blockingButton.setEnabled(true);

            }

         });

      }

   }



   /**

    * Connects to the test server, using blocking I/O

    */

   public void connectBlocking() throws IOException

   {

      messages.append("Blocking:\n");

      try (Socket sock = new Socket("localhost", 8189))

      {

         in = new Scanner(sock.getInputStream());

         while (!Thread.currentThread().isInterrupted())

         {

            messages.append("Reading ");

            if (in.hasNextLine())

            {

               String line = in.nextLine();

               messages.append(line);

               messages.append("\n");

            }

         }

      }

      finally

      {

         EventQueue.invokeLater(new Runnable()

         {

            public void run()

            {

               messages.append("Socket closed\n");

               interruptibleButton.setEnabled(true);

               blockingButton.setEnabled(true);

            }

         });      

      }

   }



   /**

    * A multithreaded server that listens to port 8189 and sends numbers to the client, simulating a

    * hanging server after 10 numbers.

    */

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

         }

      }

   }



   /**

    * This class handles the client input for one server socket connection.

    */

   class TestServerHandler implements Runnable

   {

      private Socket incoming;

      private int counter;



      /**

       * Constructs a handler.

       * @param i the incoming socket

       */

      public TestServerHandler(Socket i)

      {

         incoming = i;

      }



      public void run()

      {

         try 

         {

            try

            {

               OutputStream outStream = incoming.getOutputStream();

               PrintWriter out = new PrintWriter(outStream, true /* autoFlush */);

               while (counter < 100)

               {

                  counter++;

                  if (counter <= 10) out.println(counter);

                  Thread.sleep(100);

               }

            }

            finally

            {

               incoming.close();

               messages.append("Closing server\n");

            }

         }

         catch (Exception e)

         {

            messages.append("\nTestServerHandler.run: " + e);

         }

      }

   }

}

 

 

你可能感兴趣的:(套接字)