流程:
● 在服务端创建ServerSocket server用于监听访问服务端8090端口的请求
● 在主线程中,server.accept()处会发生阻塞,一直阻塞到server.accept()成功返回数据(有个客户端对服务端8090端口发起了访问请求)。一旦server.accept()成功返回socket,意味着该socket成功与客户端建立起了连接。
● 接下来需要接收客户端发的数据,但是如果客户端一直不发数据,那么程序也会在这里阻塞,下一个client会进不来。于是抛出一个线程,不论客户端有没有发数据,程序都会继续往下运行。下一个client会有新的对应socket和线程。
● BIO的这种特性被称为“每线程,每连接”。优势是可以接收很多连接,劣势是线程内存浪费,CPU调度消耗。
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.linkedList;
public class NIOSocket {
public static void main(String[] args) throws Exception{
linkedList clients = new linkedList<>();
ServerSocketChannel ss = ServerSocketChannel.open();
ss.bind(new InetSocketAddress(9090));
ss.configureBlocking(false); // 重点,设置成了非阻塞
while (true) {
Thread.sleep(1000);
SocketChannel client = ss.accept(); // 这里不会阻塞,如果真的有客户端来请求,则返回客户端;如果没有,则返回null
if (client == null) {
System.out.println("null");
} else {
client.configureBlocking(false); // 重点,设置成了非阻塞
int port = client.socket().getPort();
System.out.println("client...port: "+port);
clients.add(client);
}
ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
// 遍历已有的客户端看有没有发数据
for (SocketChannel c: clients) {
int num = c.read(buffer);
if (num > 0) {
buffer.flip();
byte[] aaa = new byte[buffer.limit()];
buffer.get(aaa);
String b = new String(aaa);
System.out.println(c.socket().getPort()+":"+b);
buffer.clear();
}
}
}
}
}
NIO
NIO的思路就是,把BIO中会引起阻塞的两个地方都给改成了非阻塞,所以不再需要一堆的线程了,一个线程就可以了。
● 优点:避开了多线程导致的问题
● 缺点:如果连接数特别大,比如有一万个连接client,但是只有一两个在发数据,那么每次循环都要往内核发一万次请求,就会极大的浪费时间和资源(用户空间向内核空间的循环遍历,复杂度在系统调用上)。
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.linkedList;
public class NIOSocket {
public static void main(String[] args) throws Exception{
linkedList clients = new linkedList<>();
ServerSocketChannel ss = ServerSocketChannel.open();
ss.bind(new InetSocketAddress(9090));
ss.configureBlocking(false); // 重点,设置成了非阻塞
while (true) {
Thread.sleep(1000);
SocketChannel client = ss.accept(); // 这里不会阻塞,如果真的有客户端来请求,则返回客户端;如果没有,则返回null
if (client == null) {
System.out.println("null");
} else {
client.configureBlocking(false); // 重点,设置成了非阻塞
int port = client.socket().getPort();
System.out.println("client...port: "+port);
clients.add(client);
}
ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
// 遍历已有的客户端看有没有发数据
for (SocketChannel c: clients) {
int num = c.read(buffer);
if (num > 0) {
buffer.flip();
byte[] aaa = new byte[buffer.limit()];
buffer.get(aaa);
String b = new String(aaa);
System.out.println(c.socket().getPort()+":"+b);
buffer.clear();
}
}
}
}
}



