看以下代码:
public class OioServer {
public static void main(String[] args) {
// 创建socket服务 监听10101端口
try {
ServerSocket serverSocket = new ServerSocket(10101);
System.out.println("服务器启动了");
while (true) {
// 获取一个套接字(阻塞)
Socket socket = serverSocket.accept();
System.out.println("来一个新的客户端");
handler(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void handler(Socket socket) {
try {
byte[] bytes = new byte[1024];
// 获取输入流
InputStream inputStream = socket.getInputStream();
while (true) {
// 读取数据(阻塞)
int read = inputStream.read(bytes);
if (read != -1) {
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("socket关闭");
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}复制代码
说明一下:该段代码中的2个阻塞点:
1. server.accept(),该方法用于获取一个socket,在未接收到客户端连接的时候它会一直阻塞
2.inputStream.read(bytes),当客户端连接成功后,程序阻塞等待读取数据发送的数据
将上述的服务端代码启动起来。
通过telnet去连接 发送数据
然后回车:
看服务端的变化:
这个时候,在DOS窗口,按下Ctrl+】
发送命令:send hello
查看服务端:成功接收到hello
想一下,这个时候在过来一个客户端去连接该服务器。会有什么变化。
重新打开一个DOS窗口
回车:
查看服务端:没有变化
在单线程的io中,在长连接的时候客户端的socket独占线程,这造成其他的客户端连接到该服务端,它也无法获取到socket并进行数据读取。
传统io单线程处理的弊端:一个服务端无法为多个客户端提供服务。
运行结果:
这样就成功的解决掉了不能为多个客户端服务的问题,但是新的问题就来了,如果来一个客户端,新启一个线程,你的电脑会承受不住的。
由此,对传统IO改进,就有了NIO
NIO被称为非阻塞IO。来看看和传统IO的区别
传统IO:
如果将整个服务端程序看成是一家餐厅的话,serverSocket就是大门,而客人是客户端,服务员就是处理读写操作的线程。传统的处理方式是,当大门(serverSocket),来一个客人(client),餐厅(sever)就为他分配一个服务员(thread),这一个服务员为一名客人服务,这样明显会造成人力浪费,是一种非常不可取的方式。
看看NIO怎么处理:
还是刚才那个餐厅和服务员的比喻。nio新加入了几个概念,一个是channel(通道)相当于socket,一个是selector(多路复用器)。当大门(serverSocketChannel)检测到有新的客人(client)上门的时候,餐厅不再是直接给他派遣一个服务员,而是让他在服务员那里去登记一下(就像我们点餐一样,点好了拿着点餐的牌号,然后等着服务员根据订单给你上菜就好了),然后由一个服务员根据你登记的需要服务的事项(在nio中成为状态)为所有客人服务。这样一个服务端就可以为多个客户端提供服务了,大大的节约了资源。
关注51码农网,寻找一起进步的同学。www.51manong.com
51码农网 程序员社群学习打卡集聚地
关注51码农网官方微信公众号: