创建阻塞的 EchoClient
客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求
public class EchoClient { private SocketChannel socketChannel = null; public EchoClient() throws IOException { socketChannel = SocketChannel.open(); InetAddress ia = InetAddress,getLocalHost(); InetSocketAddress isa = new InetSocketAddress(ia,8000); socketChannel.connect(isa); //连接服务器 } 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(socketChannel.socket()); PrintWriter pw = getWriter(socketChannel.socket()); BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in)); String msq = null; while((msg = localReader.readLine()) != null) { pw.println(msg); System.out.println(br.readLine()); if(msq.equals("bye")) { break; } } } catch(IOException e) { e.printStackTrace(); } finally { try { socketChannel.close(); } catch(IOException e) { e.printStackTrace(); } } }}
创建非阻塞的 EchoClient
对于客户与服务器之间的通信,按照它们收发数据的协调程度来区分,可分为同步通信和异步通信
同步通信指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数据后,再发送下一批数据。同步通信要求一个 IO 操作完成之后,才能完成下一个 IO 操作,用阻塞模式更容易实现
异步通信指发送数据和接收数据的操作互不干扰,各自独立进行。异步通信允许发送数据和接收数据的操作各自独立进行,用非阻塞模式更容易实现
值得注意的是,通信的两端并不要求都采用同样的通信方式,当一方采用同步通信时,另一方可以采用异步通信
public class EchoClient { private SocketChannel socketChannel = null; private ByteBuffer sendBuffer = ByteBuffer.allocate(1024); private ByteBuffer receiveBuffer = ByteBuffer.allocate(1024); private Charset charset = Charset.forName("GBK"); private Selector selector; public EchoClient() throws IOException { socketChannel = SocketChannel.open(); InetAddress ia = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(ia, 8000); socketChannel.connect(isa); //采用阻塞模式连接服务器 socketChannel.configureBlocking(false); //设置为非阻塞模式 selector = Selector.open(); } public static void main(String args[]) throws IOException { final EchoClient client = new EchoClient(); Thread receiver=new Thread() { public void run() { client.receiveFromUser(); //接收用户向控制台输入的数据 } }; receiver.start(); client.talk(); } /** 接收用户从控制台输入的数据,放到sendBuffer中 */ public void receiveFromUser() { try { BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in)); String msg = null; while((msg = localReader.readLine()) != null) { synchronized(sendBuffer) { sendBuffer.put(encode(msg + "\r\n")); } if (msg.equals("bye")) { break; } } } catch(IOException e) { e.printStackTrace(); } } //接收和发送数据 public void talk() throws IOException { socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); while (selector.select() > 0 ) { Set readyKeys = selector.selectedKeys(); Iterator it = readyKeys.iterator(); while (it.hasNext()) { SelectionKey key = null; try { key = (SelectionKey) it.next(); it.remove(); if (key.isReadable()) { receive(key); } if (key.isWritable()) { send(key); } } catch(IOException e) { e.printStackTrace(); try { if(key != null) { key.cancel(); key.channel().close() ; } } catch(Exception ex) { e.printStackTrace(); } } } } } public void send(SelectionKey key) throws IOException { //发送sendBuffer的数据 SocketChannel socketChannel = (SocketChannel)key.channel(); synchronized(sendBuffer) { sendBuffer.flip(); //把极限设为位置,把位置设为0 socketChannel.write(sendBuffer); //发送数据 sendBuffer.compact(); //删除已经发送的数据 } } public void receive(SelectionKey key) throws IOException { //接收EchoServer发送的数据,把它放到receiveBuffer //如果receiveBuffer有一行数据,就打印这行数据,然后把它从receiveBuffer删除 SocketChannel socketChannel = (SocketChannel) key.channel(); socketChannel.read(receiveBuffer): receiveBuffer.flip(); String receiveData = decode (receiveBuffer); if(receiveData.indexOf("\n") == -1) return; String outputData = receiveData.substring(0, receiveData.indexOf("\n") + 1): System.out.print(outputData); if(outputData.equals("echo:bye\r\n")) { key.cancel(): socketChannel.close(); selector.close(); System.exit(0); } ByteBuffer temp = encode(outputData); receiveBuffer.position(temp.limit()); receiveBuffer.compact(): //删除已经打印的数据 } //解码 public String decode(ByteBuffer buffer) { CharBuffer charBuffer= charset.decode(buffer); return charBuffer.toString(); } //编码public ByteBuffer encode(String str) { return charset.encode(str); }}