BIO:
是一种阻塞的IO
客户端:
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try {
Socket socket=new Socket("127.0.0.1",8080);//3.客户端Socket对象
Scanner scanner=new Scanner(System.in);
String text=scanner.nextLine();
socket.getOutputStream().write(text.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
static byte[] bytes=new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket=new ServerSocket();//1.这个Socket对象是用来监听的·listener;
serverSocket.bind(new InetSocketAddress(8080));
System.out.println("等待连接");
/*阻塞*/
Socket socket=serverSocket.accept();//2.这个Socket对象,是专门用来和客户端进行通信的Socket accept方法会导致阻塞,直到该服务器端接收到一个客户端对象,代码才会往下继续运行
System.out.println("连接成功");
System.out.println("等待读取数据");
/*阻塞 value表示读了多少字节,读了多少数据*/
int value=socket.getInputStream().read(bytes);//read会阻塞,直到客户端发送了数据,代码才会往下继续运行
System.out.println("读取数据成功");
String content=new String(bytes);
System.out.println(value+":"+content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在客户端与服务器端进行通信时,是有3个Socket对象的:1.客户端Socket对象;2.服务器端用来监听的Socket对象;3.服务器端用来通信的Socket对象;
上面这段代码中,服务器端只能接受一个客户端,原因就在于:(1)服务器端在获取客户端连接(accept)处是会阻塞的,直到获取到一个客户端的Socket对象,服务器端代码才往下继续执行;(2)服务器端在读取客户端发送的数据(read)处也是会阻塞的,直到读取到数据,服务器端代码才会往下继续执行;
以上两个原因导致当获取到一个客户端的Socket对象之后,服务器端被阻塞时,服务器端无法再接收其他的客户端对象,导致其无法并发处理,因此要想使其进行并发处理,就要使用多线程来处理;
但还有另外一种方式,使用一种类似于NIO的方式:使服务器端在获取客户端连接时,在accept的过程中,不会被阻塞,即使没有获取到客户端Socket对象,代码也能往下进行;且在读取客户端数据时,也不会被阻塞,即使没有读取到数据,使其仍能往下执行;
configureBlocking(false);的作用就是改变Socket对象的通信模式,false代表将其改为非阻塞模式;
NIO:
非阻塞的IO
客户端代码不变
客户端:
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try {
Socket socket=new Socket("127.0.0.1",8080);
Scanner scanner=new Scanner(System.in);
while(scanner.hasNext()){
String text=scanner.nextLine();
socket.getOutputStream().write(text.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashSet;
import java.util.Set;
public class Server {
private static Set<SocketChannel> set = new HashSet<>();//在该集合中存放所有的客户端对象
private static byte[] bytes=new byte[1024];
public static void main(String[] args) {
try {
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(8080));
serverSocket.configureBlocking(false);//设置为非阻塞的,这样子当服务器端在进行accept时,无论是否接收到客户端,代码都能往下运行
while (true) {
System.out.println("等待连接");
SocketChannel socket = serverSocket.accept();
if (socket == null) {
Thread.sleep(1000);
System.out.println("无人连接");
} else {
socket.configureBlocking(false);//将其设置为非阻塞,这样子,当读取客户端数据时,不会阻塞,代码会继续往下运行
set.add(socket);//将其添加到集合中
System.out.println("连接成功");
System.out.println("等待读取数据");
}
/*遍历整个集合,看所有的客户端是否有向服务器端发送数据*/
for (SocketChannel item : set) {
int value=item.read(ByteBuffer.wrap(bytes));
if(value>0) {
String content=new String(bytes);
System.out.println(content);
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
上面代码中:
(1)serverSocket.configureBlocking(false);//设置为非阻塞的,这样子当服务器端在进行accept时,无论是否接收到客户端,代码都能往下运行
(2) socket.configureBlocking(false);//将其设置为非阻塞,这样子,当读取客户端数据时,不会阻塞,不管客户端有没有发送数据,代码都会继续往下运行