示例代码见附件:很遗憾,太懒,代码内容还是我分的包,如果你要运行的话,还需要自己下一点小的功夫,改变一下包路径。
Server端代码:
package com.henushang.socket.chapter4nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class EchoServerNoBlock {
private int port = 8000;
private ServerSocketChannel serverSocketChannel;
Selector selector;
public EchoServerNoBlock() throws Exception {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.configureBlocking(false);// 设置通信为非阻塞模式,没有这一配置,则无法实现非阻塞
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("等待连接...");
}
public void service() throws Exception {
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
SelectionKey key = null;
while (iterator.hasNext()) {
try {
key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
System.out.println("accept");
ServerSocketChannel ssc = (ServerSocketChannel) key
.channel();
SocketChannel sc = ssc.accept();
System.out.println("接收到链接"
+ sc.socket().getInetAddress() + ":"
+ sc.socket().getPort());
sc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.register(selector, SelectionKey.OP_READ
| SelectionKey.OP_WRITE, buffer);
}
if (key.isReadable()) {
System.out.println("read");
receive(key);
}
if (key.isWritable()) {
send(key);
}
} catch (Exception e) {
e.printStackTrace();
if (key != null) {
key.cancel();
key.channel().close();
}
}
}
}
}
/**
* 发送消息处理方法
* @param key
* @throws Exception
* @author henushang
*/
public void send(SelectionKey key) throws Exception {
ByteBuffer buffer = (ByteBuffer) key.attachment();
SocketChannel socketChannel = (SocketChannel) key.channel();
buffer.flip();
String data = decode(buffer);
if (data.indexOf("\r\n") == -1) {
return;
}
String outputData = data.substring(0, data.indexOf("\n") + 1);
System.out.println(outputData);
ByteBuffer outputBuffer = encode("henushang:" + outputData);
while (outputBuffer.hasRemaining()) {
socketChannel.write(outputBuffer);
}
ByteBuffer temp = encode(outputData);
buffer.position(temp.limit());
buffer.compact();
if (outputData.equals("byt\r\n")) {
key.cancel();
socketChannel.close();
System.out.println("关闭连接");
}
}
/**
* 接受链接处理方法
* @param key
* @throws IOException
* @author henushang
*/
public void receive(SelectionKey key) throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
ByteBuffer readBuffer = ByteBuffer.allocate(32);
sc.read(readBuffer);
readBuffer.flip();
buffer.limit(readBuffer.capacity());
buffer.put(readBuffer);
}
static Charset charset = Charset.forName("UTF-8");
/**
* 解码
* @param byteBuffer
* @return
* @author henushang
*/
public static String decode(ByteBuffer byteBuffer) {
CharBuffer charBuffer = charset.decode(byteBuffer);
return charBuffer.toString();
}
/**
* 编码
* @param msg
* @return
* @author henushang
*/
public static ByteBuffer encode(String msg) {
return charset.encode(msg);
}
}
客户端代码:
package com.henushang.socket.chapter4nio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import com.henushang.socket.util.SocketUtils;
public class EchoClient {
private SocketChannel socketChannel;
private int port = 8000;
public EchoClient() throws Exception {
socketChannel = SocketChannel.open();
InetAddress inetAddress = InetAddress.getLocalHost();
InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, port);
socketChannel.connect(inetSocketAddress);
System.out.println("准备连接服务器");
}
public static void main(String[] args) throws Exception {
new EchoClient().talk();
}
public void talk() {
try {
BufferedReader reader = SocketUtils.getReader(socketChannel.socket());
PrintWriter pw = SocketUtils.getWriter(socketChannel.socket());
BufferedReader localreaderReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while ((msg = localreaderReader.readLine()) != null) {
System.out.println(msg);
pw.println(msg);
pw.flush();
System.out.println(reader.readLine());
if ("bye".equals(msg)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally{
SocketUtils.close(socketChannel);
}
}
}
工具类代码:
package com.henushang.socket.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.channels.SocketChannel;
public class SocketUtils {
public static PrintWriter getWriter(Socket socket) throws IOException {
OutputStream os = socket.getOutputStream();
return new PrintWriter(os);
}
public static PrintWriter getWriter(SocketChannel socketChannel) throws IOException {
return getWriter(socketChannel.socket());
}
public static BufferedReader getReader(Socket socket) throws IOException{
InputStream is = socket.getInputStream();
return new BufferedReader(new InputStreamReader(is, "UTF-8"));
}
public static BufferedReader getReader(SocketChannel socketChannel) throws IOException{
return getReader(socketChannel.socket());
}
public static void close(Socket socket) {
try {
if (socket != null) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(SocketChannel socketChannel) {
try {
if (socketChannel != null) {
socketChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String echo(String msg) {
return "echo:" + msg;
}
}
这种方式是一个线程在不停的循环检测监听到的事件,然后分别作出相应的处理。可是这种方式只适合那种处理过程时间短的情况,如果某一个操作处理的事件太长的话,则会让其他的事件一直处于等待状态,比如发送或者接收一个很大的文件,这样显然是很不合适的。
使用NIO的NoBlocking的操作方式固然减少了系统使用多线程建立连接的开销,但是也并不是完全适合任何情况的。
所以,在几种方式中没有最好的,只有最合适的。
而做程序也是一样的,没有那个语言是最好的,只有最合适的语言。