本篇包含了入门小栗子以及一些问题的思考

BIO

package com.demo.bio;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner; /**
* 问题:开启多个客户端,只有服务端发送足够条数的消息,客户端才会收到
*/
public class Server { public static void main(String[] args) throws Exception {
new Server().startServer();
} public void startServer() throws IOException {
ServerSocket serverSocket = new ServerSocket(9999); while (true){
Socket client = serverSocket.accept();
System.err.println("Client:" + client.getInetAddress().getHostAddress());
OutputStream out = client.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);
writer.println("Hello!We are already connected!say 'bye' to close"); new Thread(new SocketReadThread(client)).start();
new Thread(new SocketWriteThread(client)).start();
} }
} /**
* 读线程
*/
class SocketReadThread implements Runnable{ private Socket socket; public SocketReadThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(in, "UTF-8");
boolean bye = false;
while (!bye && scanner.hasNextLine()){
String line = scanner.nextLine();
System.out.println("Client Msg[" + socket + "]:" + line);
if(line.trim().equals("bye")){
bye = true;
}
}
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
} }
} /**
* 写线程
*/
class SocketWriteThread implements Runnable{ private Socket socket; public SocketWriteThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);
Scanner scanIn = new Scanner(System.in);
while (true){
String line = scanIn.nextLine();
writer.println(line);
if (socket.isClosed()){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} }
}
package com.demo.bio;

import java.io.*;
import java.net.Socket;
import java.util.Scanner; /**
* 客户端
*/
public class Client { public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true); new Thread(new SocketReceiveThread(socket)).start();
Scanner scanIn = new Scanner(System.in);
while (!socket.isClosed()){
String line = scanIn.nextLine();
writer.println(line);
if(line.trim().equals("bye")){
socket.close();
}
}
} }
class SocketReceiveThread implements Runnable{ private Socket socket; public SocketReceiveThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(in, "UTF-8");
boolean bye = false;
while (!bye && scanner.hasNextLine()){
String line = scanner.nextLine();
System.out.println("Server Msg:" + line);
if(line.trim().equals("bye")){
bye = true;
}
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
} }
}

BIO没什么难的,同步阻塞。上面实现的主要就是服务器和客户端你一句我一句,巴拉巴拉巴拉

NIO

我要实现一个客户端服务器通信的例子,我的第一个版本

package com.demo.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 问题:启动服务器,没有启动客户端的时候,阻塞在selector.select();直到有客户端连接才会向下走。
* 启动客户端:获取到客户端的消息,并读取显示;然后写一条数据给客户端;然后进入了写操作模块,等待写入,阻塞。
* 这个时候,客户端已经经过了读取操作,并且没有读到数据,也进入了写操作模块,等待写入,阻塞。这就解释了为什么客户端收不到服务器的第一条消息。
* 客户端写入:客户端输入数据,发送给服务器,离开写操作模块,进入下一轮循环,然后进入读操作模块,读取到服务器的第一条消息并显示。
* 服务器接收:此时服务器并没有收到客户端的消息,因为此时还在写操作模块阻塞,所以想要读取到数据,就要向客户端发送数据,以离开写操作模块,进入下一轮循环。
* 这就解释了:为什么要先写入才能读取的数据。
*/
public class Server { private boolean isFirst = true;
private ServerSocketChannel ssc = null;
private Selector selector = null; public Server(int port) throws IOException {
ssc = ServerSocketChannel.open();
selector = Selector.open();
InetSocketAddress inetAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); ssc.socket().bind(inetAddress);
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
System.out.println("等待客户端连接...");
selector.select();
System.out.println("捕获客户端连接...");
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//连接事件
if(key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
channel.accept().configureBlocking(false).register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//System.out.println(channel.toString() + "-已连接");
}
//读数据
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bf = ByteBuffer.allocate(1024);
channel.read(bf);
System.out.println("来自客户端数据:" + new String(bf.array()));
// 只有第一次通信返回消息
if(isFirst){
isFirst = false;
ByteBuffer bst = ByteBuffer.wrap("Hi!".getBytes());
channel.write(bst);
}
}
//写数据
if(key.isWritable()){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
channel.write(bst);
// key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove(); } }
} public static void main(String[] args) {
try {
Server server = new Server(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
package com.demo.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; public class Client { private SocketChannel sc = null;
private Selector selector = null; public Client(int port) throws IOException {
sc = SocketChannel.open();
selector = Selector.open();
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer bf = ByteBuffer.wrap("Hello".getBytes());
sc.write(bf); listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer dst = ByteBuffer.allocate(1024);
channel.read(dst);
System.out.println("来自服务器:" + new String(dst.array()));
}
if(key.isWritable()){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
channel.write(bst);
}
iterator.remove();
} }
} public static void main(String[] args) {
try {
Client client = new Client(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

上面例子的问题在注释里已经详细描述了,不信可以运行一下,下面是修正版,把写操作放在一个独立的线程里

package com.demo.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 修正版
*/
public class ServerRevision { private boolean isFirst = true;
private ServerSocketChannel ssc = null;
private Selector selector = null; public ServerRevision(int port) throws IOException {
ssc = ServerSocketChannel.open();
selector = Selector.open();
InetSocketAddress inetAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); ssc.socket().bind(inetAddress);
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
System.out.println("等待客户端连接...");
selector.select();
System.out.println("捕获客户端连接...");
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//连接事件
if(key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
channel.accept().configureBlocking(false).register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//System.out.println(channel.toString() + "-已连接");
}
//读数据
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bf = ByteBuffer.allocate(1024);
channel.read(bf);
System.out.println("来自客户端数据:" + new String(bf.array()));
// 只有第一次通信返回消息
if(isFirst){
isFirst = false;
ByteBuffer bst = ByteBuffer.wrap("Hi!".getBytes());
channel.write(bst);
}
}
//写数据
if(key.isWritable()){
System.out.println("[服务器]写就绪...");
new Thread(new DealWrite(key)).start();
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove(); } }
} public static void main(String[] args) {
try {
ServerRevision server = new ServerRevision(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class DealWrite implements Runnable{ private SelectionKey key; public DealWrite(SelectionKey key) {
this.key = key;
} @Override
public void run() {
while (true){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
try {
channel.write(bst);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.demo.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 修正版
*/
public class ClientRevision { private SocketChannel sc = null;
private Selector selector = null; public ClientRevision(int port) throws IOException {
sc = SocketChannel.open();
selector = Selector.open();
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer bf = ByteBuffer.wrap("Hello".getBytes());
sc.write(bf); listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer dst = ByteBuffer.allocate(1024);
channel.read(dst);
System.out.println("来自服务器:" + new String(dst.array()));
}
if(key.isWritable()){
System.out.println("[客户端]写就绪...");
new Thread(new DealWrite(key)).start();
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove();
} }
} public static void main(String[] args) {
try {
ClientRevision client = new ClientRevision(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

目前只是测试了服务器-客户端一对一的通信,不知道一个服务器对多个客户端会出什么bug

NIO稍微有些复杂吧,不过核心的就三个Selector、Channel、Buffer,NIO是同步非阻塞的。

最新文章

  1. [iOS]技巧集锦:UICollectionView内容下沉64像素原因和解决方案
  2. swiper的使用
  3. uva133-S.B.S.
  4. javaNIO学习
  5. js一些算法实现
  6. [Spring入门学习笔记][Spring Boot]
  7. Linux 下 Hadoop java api 问题
  8. POJ_1556_The Doors_判断线段相交+最短路
  9. springboot 静态注入 单例
  10. 工控随笔_17_西门子_WinCC的VBS脚本_06_过程和函数
  11. 如何将vue/html在Hbuilder里进行apk打包封装生成直接可用程序
  12. sublime快捷键的使用
  13. MVC EF 执行SQL语句(转载)
  14. 遍历map并判断key的值
  15. Windows和pthread中提供的自旋锁
  16. 【Devops】【docker】【CI/CD】jenkins源码管理,添加SSH地址后报错+Jenkins构建报错:Please make sure you have the correct access rights and the repository exists.
  17. Lomboz插件
  18. QSocket类
  19. #C语言初学记录(位运算)
  20. VS2010/MFC编程入门之四十八(字体和文本输出:文本输出)

热门文章

  1. There is insufficient memory for the Java Runtime Environment to continue.
  2. Reactive Extensions (Rx) 入门(2) —— 安装 Reactive Extensions
  3. Django 基础篇(二)视图与模板
  4. 如何优化SpringBoot的项目的启动速度
  5. nginx location配置说明
  6. SpringCloud-Feign声明式服务调用
  7. Java手机号隐藏中间4位和邮箱隐藏,身份证隐藏
  8. malloc分配到一块内存,读写操作时却发生segmentation falt的奇怪问题。
  9. Centos 7.x卸载ibus黑屏修复及fcitx搜狗拼音安装方法
  10. C#利用iTextSharp将datatable数据转化为PDF文件