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网络编程精解》
在乎的是看风景的心情,旅行不会因为美丽的风景终止。