Java Socket构建阻塞的TCP通信

来源:岁月联盟 编辑:zhuzhu 时间:2009-01-15

  1.服务器端

  1.创建ServerSocket对象,可在构造子中指定监听的端口;

private int port = 8000;
private ServerSocket serverSocket;
……
serverSocket = new ServerSocket(port);

  2.服务器端调用ServerSocket对象的accept()方法,该方法一直监听端口,等待客户的连接请求,如果接收到一个连接请求,accept()方法就会返回一个Socket对象,这个Socket对象与客户端的Socket对象将形成一条通信线路;

Socket socket = null;
socket = serverSocket.accept(); // 等待客户连接

  3.Socket类提供了getInputStream()方法和getOutputStream()方法。

  InputStream socketIn = socket.getInputStream();

  OutputStream socketOut = socket.getOutputStream();

  源代码EchoServer.java

public class EchoServer {
  private int port = 8000;
  private ServerSocket serverSocket;
  public EchoServer() throws IOException {
    serverSocket = new ServerSocket(port);
    System.out.println("Server Start");
  }
  public String echo(String msg) {
    return "echo:" + msg;
  }
  private PrintWriter getWriter(Socket socket) throws IOException {
    OutputStream socketOut = socket.getOutputStream();
    return new PrintWriter(socketOut, true);
  }
  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(socketIn));
  }
  public void service() {
    while (true) {
      Socket socket = null;
      try {
       socket = serverSocket.accept(); // 等待客户连接
       System.out.println("New connection accepted "
           + socket.getInetAddress() + ":" + socket.getPort());
       BufferedReader br = getReader(socket);
       PrintWriter pw = getWriter(socket);
       String msg = null;
       while ((msg = br.readLine()) != null) {
         System.out.println(msg);
         pw.println(echo(msg));
         if (msg.equals("bye")) // 如果客户发送的消息为“bye”,就结束通信
           break;
       }
      } catch (IOException e) {
       e.printStackTrace();
      } finally {
       try {
         if (socket != null)
           socket.close(); // 断开连接
       } catch (IOException e) {
         e.printStackTrace();
       }
      }
    }
  }
  public static void main(String args[]) throws IOException {
    new EchoServer().service();
  }
}

  2.客户端

  1.创建一个Socket对象,指定服务器端的地址和端口;

private String host = "localhost";
private int port = 8000;
private Socket socket;
……
socket = new Socket(host, port);

  这里作为客户端,它的端口是由操作系统随机产生的。

  2.Socket类提供了getInputStream()方法和getOutputStream()方法。

  InputStream socketIn = socket.getInputStream();

  OutputStream socketOut = socket.getOutputStream();

  源代码EchoClient.java

public class EchoClient {
  private String host = "localhost";
  private int port = 8000;
  private Socket socket;
  public EchoClient() throws IOException {
    socket = new Socket(host, port);
  }
  public static void main(String args[]) throws IOException {
    new EchoClient().talk();
  }
  private PrintWriter getWriter(Socket socket) throws IOException {
    OutputStream socketOut = socket.getOutputStream();
    return new PrintWriter(socketOut, true);
  }
  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(socketIn));
  }
  public void talk() throws IOException {
    try {
      BufferedReader br = getReader(socket);
      PrintWriter pw = getWriter(socket);
      BufferedReader localReader = new BufferedReader(
         new InputStreamReader(System.in));
      String msg = null;
      while ((msg = localReader.readLine()) != null) {
       pw.println(msg);
       System.out.println(br.readLine());
       if (msg.equals("bye"))
         break;
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
       socket.close();
      } catch (IOException e) {
       e.printStackTrace();
      }
    }
  }
}

  3.关闭Socket

  1.关闭Socket的代码;

try {
    ……
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    try {
      socket.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  Socket类提供3个状态测试方法。

  -isClosed():如果Socket已经连接到远程主机,并且还没有关闭,则返回true;

  -isConnected():如果Socket曾经连接到远程主机,则返回true;

  -isBound():如果Socket已经与一个本地端口绑定,则返回true。

  判断一个Socket对象当前是否处于连接状态,

  Boolean isConnected = socket.isConnected() && !socket.isClosed();

  2.处理关闭

  (1)当进程A与进程B交换的是字符流,并且是一行一行地读写数据时,可以事先约定一个特殊的标志。

BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while ((msg = br.readLine()) != null) {
  System.out.println(msg);
  pw.println(echo(msg));
  if (msg.equals("bye")) // 如果客户发送的消息为“bye”,就结束通信
    break;
}

  (2)进程A先发送一个消息,告诉进程B所发送的正文长度,然后发送正文。进程B只要读取完该长度的数据就可以停止读数据。

  (3)进程A发送完所有数据后,关闭Socket。当进程B读入进程A发送的所有数据后,再次执行输入流的read()方法时,该方法返回-1.

InputStream socketIn = socket.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = -1;
while ((len = socketIn.read(buff)) != -1) {
  buffer.write(buff, 0, len);
}
System.out.println(new String(buffer.toByteArray()));

  (4)当调用Socket的close()方法关闭Socket时,它的输入流和输出流都被关闭。如果仅仅希望关闭输入或输出流其中之一,可调用半关闭方法:shutdownInput()和shutdownOutput()。先后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭输入流和输出流,并不等价于调用close()方法。在通信结束后仍然需要调用close()方法,因为该方法才会释放Socket占用的资源。

  4.多线程服务器

  EchoServer只能顺序的处理Client端的请求,这里使用ExecutorService指定一个线程池用于处理连接请求。

private ExecutorService executorService; // 线程池
private final int POOL_SIZE = 4; // 单个CPU时线程池中工作线程的数目
…….
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
       .availableProcessors()* POOL_SIZE);
……
try {
    socket = serverSocket.accept();
    executorService.execute(new Handler(socket));
  } catch (IOException e) {
    e.printStackTrace();
  }

  Hander类封装了原来处理连接请求的逻辑,只要当前线程池中有空闲的线程,就可以用于处理请求。

 源代码MultiEchoServer.java

public class MultiEchoServer {
  private int port = 8000;
  private ServerSocket serverSocket;
  private ExecutorService executorService; // 线程池
  private final int POOL_SIZE = 4; // 单个CPU时线程池中工作线程的数目
  public MultiEchoServer() throws IOException {
    serverSocket = new ServerSocket(port);
    executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
       .availableProcessors()
       * POOL_SIZE);
    System.out.println("Server Start");
  }
  public void service() {
    while (true) {
      Socket socket = null;
      try {
       socket = serverSocket.accept();
       executorService.execute(new Handler(socket));
      } catch (IOException e) {
       e.printStackTrace();
      }
    }
  }
  public static void main(String args[]) throws IOException {
    new MultiEchoServer().service();
  }
}
class Handler implements Runnable {
  private Socket socket;
  public Handler(Socket socket) {
    this.socket = socket;
  }
  private PrintWriter getWriter(Socket socket) throws IOException {
    OutputStream socketOut = socket.getOutputStream();
    return new PrintWriter(socketOut, true);
  }
  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(socketIn));
  }
  public String echo(String msg) {
    return "echo:" + msg;
  }
  public void run() {
    try {
      System.out.println("New connection accepted "
         + socket.getInetAddress() + ":" + socket.getPort());
      BufferedReader br = getReader(socket);
      PrintWriter pw = getWriter(socket);
      String msg = null;
      while ((msg = br.readLine()) != null) {
       System.out.println(msg);
       pw.println(echo(msg));
       if (msg.equals("bye"))
         break;
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
       if (socket != null)
         socket.close();
      } catch (IOException e) {
       e.printStackTrace();
      }
    }
  }
}

  参考 孙卫琴,《Java网络编程精解》

  本文出自 “子 孑” 博客,请务必保留此出处http://zhangjunhd.blog.51cto.com/113473/124742