1.传统Socket
传统Socket单线程只能处理一个连接,要处理多个连接需要借助线程或者线程池,但是这样比较消耗资源。
public class ServerSocketDemo {
public static void main(String[] args) throws IOException {
new ServerSocketDemo().start(8080);
}
private void start(int port) throws IOException {
ServerSocket serverSocket = new ServerSocket(port,2000);
System.out.println("server is start!");
//线程池可以处理多个连接比较消耗性能
ExecutorService executorService= Executors.newCachedThreadPool();
try {
while (true) {
//accept 会阻塞
Socket socket = serverSocket.accept();
System.out.println("有客户端连接来了"+socket.toString());
executorService.execute(new SocketHandler(socket));
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
} finally {
serverSocket.close();
}
}
}
public class SocketHandler implements Runnable {
private Socket socket;
public SocketHandler(Socket socket) {
this.socket = socket;
}
public void run() {
InputStream in = null;
OutputStream out = null;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
byte[] bytes = new byte[1024];
while (true) {
//会阻塞
int n = in.read(bytes);
if (n == -1) {
break;
}
System.out.println(new String(bytes, 0, n));
out.write(bytes,0,n);
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
} finally {
try {
System.out.print("关闭 socket");
socket.close();
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
try {
if(in!=null) {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
try {
if(out!=null) {
out.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
}
2.NIO
java nio 借鉴了Linux下select、poll、epoll模型;其性能有很大的提高。
public class ServerSocketChannelDemo {
private Selector selector;
public void initServer(int port) throws IOException {
//创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
this.selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws IOException {
System.out.println("server is start!");
while (true) {
//这条语句会阻塞
selector.select();
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
handler(key);
}
}
}
public void handler(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
handlerAccept(key);
} else if (key.isReadable()) {
handlerReader(key);
}
}
public void handlerAccept(SelectionKey key) throws IOException {
ServerSocketChannel sever = (ServerSocketChannel) key.channel();
SocketChannel channel = sever.accept();
channel.configureBlocking(false);
System.out.println("有客服端连接来了" + channel.toString());
channel.register(this.selector, SelectionKey.OP_READ);
}
public void handlerReader(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
//不会阻塞
int n = socketChannel.read(buffer);
System.out.println(n);
if (n > 0) {
byte[] data = buffer.array();
System.out.println("服务端收到信息:" + new String(data, 0, n));
buffer.flip();
socketChannel.write(buffer);
} else {
System.out.println("clinet is close");
key.cancel();
}
}
public static void main(String[] args) throws IOException {
ServerSocketChannelDemo sever = new ServerSocketChannelDemo();
sever.initServer(8081);
sever.listen();
}
}
NIO 的基本知识可以参考:
IO 模型
缓冲区
通道
值得说明是:
selector.select()是阻塞的,selector.select(1000)是非阻塞的(1000 表示等待时间)、selector.selectNow()也是非阻塞的、selector.wakeup()可以唤醒selector。
SelectionKey.OP_WRITE一般很少使用OP_WRITE表示底层缓冲区是否有空间,是则响应返还true。