基于Java自身包实现消息方式的系统间通信的方式有:TCP/IP+BIO,TCP/IP+NIO,UDP/IP+BIO,UDP/IP+NIO.下面就这4种类型一一做个详细的介绍:
一.TCP/IP+BIO
在java中可基于Socket,ServerSocket来实现TCP/IP+BIO的系统间通信。Socket主要用于实现建立连接以及网络IO的操作,ServerSocket主要用于实现服务器端端口的监听及Socket对象的获取。基于Socket实现客户端的代码如下:
public class Client { /** * @param args */ public static void main(String[] args) throws Exception{ String host="127.0.0.1"; int port=9527; Socket socket=new Socket(host,port); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out=new PrintWriter(socket.getOutputStream(),true); BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in)); boolean flag=true; while(flag){ String command=systemIn.readLine(); if(command==null || "quit".equalsIgnoreCase(command.trim())){ flag=false; System.out.println("Client quit!"); out.println("quit"); out.close(); in.close(); socket.close(); continue; } out.println(command); String response=in.readLine(); System.out.println(response); } } }
服务器端代码如下:
public class Server { /** * @param args */ public static void main(String[] args) throws Exception{ int port=9527; ServerSocket ss=new ServerSocket(port); System.out.println("Server listen on port: "+port); Socket socket=ss.accept(); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out=new PrintWriter(socket.getOutputStream(),true); while(true){ String line=in.readLine(); if(line==null){ Thread.sleep(100); continue; } if("quit".equalsIgnoreCase(line.trim())){ in.close(); out.close(); ss.close(); System.out.println("Server has been shutdown!"); System.exit(0); } else{ System.out.println("Message from client: "+ line); out.println("Server response:"+line); Thread.sleep(100); } } } }
上面是基于Socket,ServerSocket实现的一个简单的系统间通信的例子。而在实际的系统中,通常要面对的是客户端同时要发送多个请求到服务器端,服务器端则同时要接受多个连接发送的请求,上面的代码显然是无法满足的。
为了满足客户端能同时发送多个请求到服务器端,最简单的方法就是生成多个Socket。但这里会产生两个问题:一是生成太多的Socket会消耗过多的本地资源,在客户端机器多,服务器端机器少的情况下,客户端生成太多Socket会
导致服务器端须要支撑非常高的连接数;二是生成Socket(建立连接)通常是比较慢的,因此频繁的创建会导致系统性能不足。鉴于这两个问题,通常采用连接池的方法来维护Socket是比较好的,一方面限制了能创建的Socket的个数;
另一个方面由于将Socket放入了池中,避免了重复创建Socket带来的性能下降的问题。数据库连接池就是这种方式的典型代表,但连接池的方式会带来另一个问题,连接池中的Socket的个数是有限的,但同时要用socket的请求可能会
很多,在这种情况下就会造成激烈的竞争和等待,还有一个需要注意的问题是合理控制等待响应的超时时间,如不设定会导致当服务器端处理变慢时,客户端相关的请求都在做无限的等待,二客户端的资源必然是有限的。因此这种情况
下很容易造成服务器端出问题时,客户端挂掉的现象。超时时间具体设置为多少取决于客户端能承受的请求量及服务器端的处理时间。既要保证性能,又要保证出错率不会过高,对于直接基于TCP/IP+BIO的方式,可采用Socket.setSotimeout来设置等待响应的超时时间。
二.TCP/IP+NIO
在java中可基于java.nio.channels中的Channel和Selector的相关类来实现TCP/IP+NIO方式的系统间通信。Channel有SocketChannel和ServerSocketChannnel两种,SocketChannel用于建立连接,监听事件及操作读写,
ServerSocketChannel用于监听端口及监听连接事件;程序通过Selector来获取是否有要处理的事件。基于这两个类实现客户端代码如下:
public class Client { public static void main(String[] args) throws Exception{ int port=9527; SocketChannel channel=SocketChannel.open(); channel.configureBlocking(false); SocketAddress target=new InetSocketAddress("127.0.0.1",port); channel.connect(target); Selector selector=Selector.open(); channel.register(selector, SelectionKey.OP_CONNECT); BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in)); while(true){ if(channel.isConnected()){ String command=systemIn.readLine(); channel.write(Charset.forName("UTF-8").encode(command)); if(command==null || "quit".equalsIgnoreCase(command.trim())){ systemIn.close(); channel.close(); selector.close(); System.out.println("Client quit!"); System.exit(0); } } int nKeys=selector.select(1000); if(nKeys>0){ for (SelectionKey key : selector.selectedKeys()) { if(key.isConnectable()){ SocketChannel sc=(SocketChannel) key.channel(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); sc.finishConnect(); } else if(key.isReadable()){ ByteBuffer buffer=ByteBuffer.allocate(1024); SocketChannel sc=(SocketChannel) key.channel(); int readBytes=0; try{ int ret=0; try{ while((ret=sc.read(buffer))>0){ readBytes+=ret; } } finally{ buffer.flip(); } if(readBytes>0){ System.out.println(Charset.forName("UTF-8").decode(buffer).toString()); buffer = null; } } finally{ if(buffer!=null){ buffer.clear(); } } } } selector.selectedKeys().clear(); } } } }
服务器端代码如下:
public class Server { public static void main(String[] args) throws Exception{ int port=9527; Selector selector=Selector.open(); ServerSocketChannel ssc=ServerSocketChannel.open(); ServerSocket serverSocket=ssc.socket(); serverSocket.bind(new InetSocketAddress(port)); System.out.println("Server listen on port: "+port); ssc.configureBlocking(false); ssc.register(selector, SelectionKey.OP_ACCEPT); while(true){ int nKeys=selector.select(1000); if(nKeys>0){ for (SelectionKey key : selector.selectedKeys()) { if(key.isAcceptable()){ ServerSocketChannel server=(ServerSocketChannel) key.channel(); SocketChannel sc=server.accept(); if(sc==null){ continue; } sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); } else if(key.isReadable()){ ByteBuffer buffer=ByteBuffer.allocate(1024); SocketChannel sc=(SocketChannel) key.channel(); int readBytes=0; String message=null; try{ int ret; try{ while((ret=sc.read(buffer))>0){ readBytes+=ret; } } catch(Exception e){ readBytes=0; // IGNORE } finally{ buffer.flip(); } if(readBytes>0){ message=Charset.forName("UTF-8").decode(buffer).toString(); buffer = null; } } finally{ if(buffer!=null){ buffer.clear(); } } if(readBytes>0){ System.out.println("Message from client: "+ message); if("quit".equalsIgnoreCase(message.trim())){ sc.close(); selector.close(); System.out.println("Server has been shutdown!"); System.exit(0); } String outMessage="Server response:"+message; sc.write(Charset.forName("UTF-8").encode(outMessage)); } } } selector.selectedKeys().clear(); } } } }
三.UDP/IP+BIO
服务器端代码如下:
public class Server { /** * @param args */ public static void main(String[] args) throws Exception{ int port=9527; int aport=9528; DatagramSocket server=new DatagramSocket(port); DatagramSocket client=new DatagramSocket(); InetAddress serverAddress=InetAddress.getByName("localhost"); byte[] buffer=new byte[65507]; DatagramPacket packet=new DatagramPacket(buffer,buffer.length); while(true){ server.receive(packet); String line=new String(packet.getData(),0,packet.getLength(),"UTF-8"); if("quit".equalsIgnoreCase(line.trim())){ server.close(); System.exit(0); } else{ System.out.println("Message from client: "+ line); packet.setLength(buffer.length); String response="Server response"+line; byte[] datas=response.getBytes("UTF-8"); DatagramPacket responsePacket=new DatagramPacket(datas,datas.length,serverAddress,aport); client.send(responsePacket); Thread.sleep(100); } } } }
客户端代码如下:
public class Client { /** * @param args */ public static void main(String[] args) throws Exception{ int port=9527; int aport=9528; DatagramSocket serverSocket=new DatagramSocket(aport); byte[] buffer=new byte[65507]; DatagramPacket receivePacket=new DatagramPacket(buffer,buffer.length); DatagramSocket socket=new DatagramSocket(); InetAddress server=InetAddress.getByName("localhost"); BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in)); boolean flag=true; while(flag){ String command=systemIn.readLine(); byte[] datas=command.getBytes("UTF-8"); DatagramPacket packet=new DatagramPacket(datas,datas.length,server,port); socket.send(packet); if(command==null || "quit".equalsIgnoreCase(command.trim())){ flag=false; System.out.println("Client quit!"); socket.close(); continue; } serverSocket.receive(receivePacket); String receiveResponse=new String(receivePacket.getData(),0,receivePacket.getLength(),"UTF-8"); System.out.println(receiveResponse); } } }
四.UDP/IP+NIO
服务器端代码如下:
public class Server { public static void main(String[] args) throws Exception{ int rport=9527; int sport=9528; DatagramChannel sendChannel=DatagramChannel.open(); sendChannel.configureBlocking(false); SocketAddress target=new InetSocketAddress("127.0.0.1",sport); sendChannel.connect(target); DatagramChannel receiveChannel=DatagramChannel.open(); DatagramSocket serverSocket=receiveChannel.socket(); serverSocket.bind(new InetSocketAddress(rport)); System.out.println("Data receive listen on port: "+rport); receiveChannel.configureBlocking(false); Selector selector=Selector.open(); receiveChannel.register(selector, SelectionKey.OP_READ); while(true){ int nKeys=selector.select(1000); if(nKeys>0){ for (SelectionKey key : selector.selectedKeys()) { if(key.isReadable()){ ByteBuffer buffer=ByteBuffer.allocate(1024); DatagramChannel dc=(DatagramChannel) key.channel(); dc.receive(buffer); buffer.flip(); String message=Charset.forName("UTF-8").decode(buffer).toString(); System.out.println("Message from client: "+ message); if("quit".equalsIgnoreCase(message.trim())){ dc.close(); selector.close(); sendChannel.close(); System.out.println("Server has been shutdown!"); System.exit(0); } String outMessage="Server response��"+message; sendChannel.write(Charset.forName("UTF-8").encode(outMessage)); } } selector.selectedKeys().clear(); } } } }
客户端代码如下:
public class Client { public static void main(String[] args) throws Exception{ int rport=9528; int sport=9527; DatagramChannel receiveChannel=DatagramChannel.open(); receiveChannel.configureBlocking(false); DatagramSocket socket=receiveChannel.socket(); socket.bind(new InetSocketAddress(rport)); Selector selector=Selector.open(); receiveChannel.register(selector, SelectionKey.OP_READ); DatagramChannel sendChannel=DatagramChannel.open(); sendChannel.configureBlocking(false); SocketAddress target=new InetSocketAddress("127.0.0.1",sport); sendChannel.connect(target); BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in)); while(true){ String command=systemIn.readLine(); sendChannel.write(Charset.forName("UTF-8").encode(command)); if(command==null || "quit".equalsIgnoreCase(command.trim())){ systemIn.close(); sendChannel.close(); selector.close(); System.out.println("Client quit!"); System.exit(0); } int nKeys=selector.select(1000); if(nKeys>0){ for (SelectionKey key : selector.selectedKeys()) { if(key.isReadable()){ ByteBuffer buffer=ByteBuffer.allocate(1024); DatagramChannel dc=(DatagramChannel) key.channel(); dc.receive(buffer); buffer.flip(); System.out.println(Charset.forName("UTF-8").decode(buffer).toString()); buffer = null; } } selector.selectedKeys().clear(); } } } }