-
查看本机IP地址,在控制台输入:
-
ipconfig
-
检查网络是否连通,在控制台输入:
-
ping 空格 IP地址
ping 220.181.57.216
ping www.baidu.com
端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
InetAddress类的方法
-
static InetAddress getLocalHost() 获得本地主机IP地址对象
-
static InetAddress getByName(String host) 根据IP地址字符串或主机名获得对应的IP地址对象
-
String getHostName();获得主机名
-
String getHostAddress();获得IP地址字符串
-
Socket : 一个该类的对象就代表一个客户端程序。
-
Socket(String host, int port)
根据ip地址字符串和端口号创建客户端Socket对象-
注意事项:只要执行该方法,就会立即连接指定的服务器程序,如果连接不成功,则会抛出异常。 如果连接成功,则表示三次握手通过。
-
-
OutputStream getOutputStream();
获得字节输出流对象 -
InputStream getInputStream();
获得字节输入流对象 -
void close();
关闭Socket, 会自动关闭相关的流 -
补充:关闭通过socket获得的流,会关闭socket,关闭socket,同时也会关闭通过socket获得的流
-
-
ServerSocket : 一个该类的对象就代表一个服务器端程序。
-
ServerSocket(int port);
根据指定的端口号开启服务器。 -
Socket accept();
等待客户端连接并获得与客户端关联的Socket对象 如果没有客户端连接,该方法会一直阻塞 -
void close();
关闭ServerSocket
-
TCP通信案例1
客户端:
1.创建Socket对象,指定要连接的服务器的ip地址和端口号
2.通过Socket对象获得字节输出流对象
3.使用字节输出流写字符串数据到连接通道中
4.释放资源
服务器:
1.创建ServerSocket对象,指定服务器的端口号
2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
3.通过返回的Socket对象获得字节输入流
4.读数据
5.释放资源
客户端代码实现
public class Client {
public static void main(String[] args) throws Exception{
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",6666);
//2.通过Socket对象获得字节输出流对象
OutputStream os = socket.getOutputStream();
//3.使用字节输出流写字符串数据到连接通道中
os.write("服务器你好,今晚约吗?".getBytes());
//4.释放资源
socket.close();
}
}
服务端代码实现
public class Server {
public static void main(String[] args) throws Exception{
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(6666);
//2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
Socket socket = ss.accept();
//3.通过返回的Socket对象获得字节输入流
InputStream is = socket.getInputStream();
//4.读数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("服务器接收到的信息:"+new String(bys,0,len));
//5.释放资源
socket.close();
ss.close();
}
}
TCP通信案例2
-
客户端向服务器发送字符串数据,服务器回写字符串数据给客户端(模拟聊天)
客户端:
1.创建Socket对象,指定要连接的服务器的ip地址和端口号
2.通过Socket对象获得字节输出流对象
3.使用字节输出流写字符串数据到连接通道中
4.通过Socket对象获得字节输入流对象
5.读服务器回写的数据
6.释放资源
服务器:
1.创建ServerSocket对象,指定服务器的端口号
2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
3.通过返回的Socket对象获得字节输入流
4.读数据
5.通过返回的Socket对象获得字节输出流
6.使用字节输出流写字符串数据到连接通道中
7.释放资源
实现
TCP客户端代码
public class Client {
public static void main(String[] args) throws Exception{
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",6666);
//2.通过Socket对象获得字节输出流对象
OutputStream os = socket.getOutputStream();
//3.使用字节输出流写字符串数据到连接通道中
os.write("服务器你好,今晚约吗?".getBytes());
//4.通过Socket对象获得字节输入流对象
InputStream is = socket.getInputStream();
//5.读服务器回写的数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("客户端接收到的信息:"+new String(bys,0,len));
//6.释放资源
socket.close();
}
}
-
服务端代码实现
public class Server {
public static void main(String[] args) throws Exception{
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(6666);
//2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
Socket socket = ss.accept();
//3.通过返回的Socket对象获得字节输入流
InputStream is = socket.getInputStream();
//4.读数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("服务器接收到的信息:"+new String(bys,0,len));
//5.通过返回的Socket对象获得字节输出流
OutputStream os = socket.getOutputStream();
//6.使用字节输出流写字符串数据到连接通道中
os.write("客户端你好,今晚小树林见!".getBytes());
//7.释放资源
socket.close();
ss.close();
}
}
扩展模拟循环聊天
public class Server {
public static void main(String[] args) throws Exception{
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(6666);
//2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
Socket socket = ss.accept();
// 循环
while (true) {
//3.通过返回的Socket对象获得字节输入流
InputStream is = socket.getInputStream();
//4.读数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("服务器接收到的信息:" + new String(bys, 0, len));
//5.通过返回的Socket对象获得字节输出流
OutputStream os = socket.getOutputStream();
//6.使用字节输出流写字符串数据到连接通道中
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要对客户端说的话:");
String msg = sc.next();
os.write(msg.getBytes());
//7.释放资源
//socket.close();
//ss.close();
}
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception{
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",6666);
// 循环
while (true) {
//2.通过Socket对象获得字节输出流对象
OutputStream os = socket.getOutputStream();
//3.使用字节输出流写字符串数据到连接通道中
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要对服务器说的话:");
String msg = sc.next();
os.write(msg.getBytes());
//4.通过Socket对象获得字节输入流对象
InputStream is = socket.getInputStream();
//5.读服务器回写的数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("客户端接收到的信息:"+new String(bys,0,len));
//6.释放资源
//socket.close();
}
}
}
文件上传案例
public class Server {
public static void main(String[] args) throws Exception {
//服务器:
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(7777);
//2.调用accept方法,接收请求建立连接,返回Socket对象
Socket socket = ss.accept();
//3.通过返回的Socket对象获得字节输入流对象
InputStream is = socket.getInputStream();
//4.创建字节输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\bbb\\mm2.jpg");
//5.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
//6.循环读
while ((len = is.read(bys)) != -1) {
//7.写数据
fos.write(bys,0,len);
}
//8.释放资源
fos.close();
socket.close();
ss.close();
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception{
//客户端:
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",7777);
//2.创建字节输入流对象,关联要上传的文件路径
FileInputStream fis = new FileInputStream("day12\\aaa\\mm.jpg");
//3.通过Socket对象获得字节输出流对象
OutputStream os = socket.getOutputStream();
//4.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
//6.循环读
while ((len = fis.read(bys)) != -1) {
//7.写数据
os.write(bys,0,len);
}
//8.释放资源
fis.close();
socket.close();
}
}
文件上传成功后服务器回写字符串数据
public class Server {
public static void main(String[] args) throws Exception {
//服务器:
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(7777);
//2.调用accept方法,接收请求建立连接,返回Socket对象
Socket socket = ss.accept();
//3.通过返回的Socket对象获得字节输入流对象
InputStream is = socket.getInputStream();
//4.创建字节输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\bbb\\mm4.jpg");
//5.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
System.out.println("服务器1");
//6.循环读
// 循环读客户端写过来的数据
while ((len = is.read(bys)) != -1) {// 卡
//7.写数据
fos.write(bys,0,len);
}
System.out.println("服务器2");
//8.通过Socket获得字节输出流对象
OutputStream os = socket.getOutputStream();
//9.回写上传成功信息给客户端
os.write("文件上传成功!".getBytes());
//10.释放资源
fos.close();
socket.close();
ss.close();
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception{
//客户端:
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",7777);
//2.创建字节输入流对象,关联要上传的文件路径
FileInputStream fis = new FileInputStream("day12\\aaa\\mm.jpg");
//3.通过Socket对象获得字节输出流对象
OutputStream os = socket.getOutputStream();
//4.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
//6.循环读
while ((len = fis.read(bys)) != -1) {
//7.写数据
os.write(bys,0,len);
}
// 问题原因:客户端不再写数据过来了,而服务器不知道,所以服务器一直在等待读客户端写过来的数据
// 解决办法: 客户端要告诉服务器,客户端不再写数据过来了
// 禁止客户端写数据的功能
socket.shutdownOutput();
System.out.println("客户端1");
//8.通过Socket获得字节输入流对象
InputStream is = socket.getInputStream();
//9.读服务器回写的信息
int len2 = is.read(bys);// 卡
System.out.println("服务器回写的信息是:"+new String(bys,0,len2));
//10.释放资源
fis.close();
socket.close();
}
}
优化文件上传案例
public class Server {
public static void main(String[] args) throws Exception {
//服务器:
//1.创建ServerSocket对象,指定服务器的端口号
ServerSocket ss = new ServerSocket(7777);
// 循环
while (true) {
//2.调用accept方法,接收请求建立连接,返回Socket对象
Socket socket = ss.accept();
// 开启线程,实现上传任务
new Thread(new Runnable() {
@Override
public void run() {
try{
//3.通过返回的Socket对象获得字节输入流对象
InputStream is = socket.getInputStream();
//4.创建字节输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\bbb\\" + System.currentTimeMillis() + ".jpg");
//5.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
System.out.println("服务器1");
//6.循环读
// 循环读客户端写过来的数据
while ((len = is.read(bys)) != -1) {// 卡
//7.写数据
fos.write(bys, 0, len);
}
System.out.println("服务器2");
//8.通过Socket获得字节输出流对象
OutputStream os = socket.getOutputStream();
//9.回写上传成功信息给客户端
os.write("文件上传成功!".getBytes());
//10.释放资源
fos.close();
socket.close();
//ss.close();
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
}
NIO
-
同步: 调用方法之后,必须要得到一个返回值
-
异步: 调用方法之后,没有返回值,但是会有回调函数,回调函数指的是满足条件之后会自动执行的方法
阻塞与非阻塞:在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立完毕,或者数据读取、写入操作完成;而非阻塞则是不管IO操作是否结束,直接返回,相应操作在后台继续处理
Buffer类(缓冲区)
方式一:在堆中创建缓冲区:public static ByteBuffer allocate(int capacity)
方式二: 在系统内存创建缓冲区:public static ByteBuffer allocatDirect(int capacity)
方式三:通过数组创建缓冲区:public static ByteBuffer wrap(byte[] arr)
添加数据-put
-
public ByteBuffer put(byte b):向当前可用位置添加数据。
-
public ByteBuffer put(byte[] byteArray):向当前可用位置添加一个byte[]数组
-
public ByteBuffer put(byte[] byteArray,int offset,int len):添加一个byte[]数组的一部分
-
public byte[] array(); 获取封装的字节数组
限制-limit
public class Test2_limit {
public static void main(String[] args) {
// 创建ByteBuffer对象
ByteBuffer b = ByteBuffer.allocate(10);
System.out.println("limit:" + b.limit());// 10
// 添加单个byte数据
b.put((byte) 10);
b.put((byte) 20);
b.put((byte) 30);
System.out.println("limit:" + b.limit());// 10
// 修改limit
b.limit(3);
System.out.println("limit:" + b.limit());// 3
//b.put((byte) 30);// 报错,因为3索引这个位置是不能使用了BufferOverflowException异常
}
}
位置-position
-
有两个相关方法:
-
public int position():获取当前可写入位置索引。
-
public Buffer position(int p):更改当前可写入位置索引。
-
public class Test5_mark {
public static void main(String[] args) {
// 创建ByteBuffer对象
ByteBuffer b = ByteBuffer.allocate(10);// 添加单个byte数据
b.put((byte) 10);
b.put((byte) 20);
b.put((byte) 30);
System.out.println("position:"+b.position());// 3// 标记一下
b.mark();b.put((byte) 10);
b.put((byte) 20);
b.put((byte) 30);
System.out.println("position:"+b.position());// 6// 重置一下
b.reset();
System.out.println("position:"+b.position());// 3// 往索引为3的位置添加元素
b.put((byte) 100);
// [10,20,30,100,20,30,0,0,0,0]
System.out.println("封装的数组:"+ Arrays.toString(b.array()));}
} -
clear和flip
- - public Buffer clear():还原缓冲区的状态。
- 将position设置为:0
- 将限制limit设置为容量capacity;
- 丢弃标记mark。
- public Buffer flip():缩小limit的范围。
- 将当前position位置设置为0;
- 将limit设置为当前position位置; -
Channel(通道)
-
public int read(ByteBuffer b)
读数据,把读到的数据放在b数组中,返回读取到的字节个数,如果读到文件的末尾就返回-1 -
public int write(ByteBuffer b)
写数据,把数据写到目的地文件中,写的是position到limit之间的数据 -
FileChannel:从文件读写数据的 输入流和输出流
-
DatagramChannel:读写UDP网络协议数据 Datagram
-
SocketChannel:读写TCP网络协议数据 Socket
-
ServerSocketChannel:可以监听TCP连接 ServerSocket
-
FileChannel类的基本使用
- FileInputStream fis=new FileInputStream("数据源文件路径");
FileOutputStream fos=new FileOutputStream("目的地文件路径");
//获得传输通道channel
FileChannel inChannel=fis.getChannel();
FileChannel outChannel=fos.getChannel(); -
使用FileChannel类完成文件的复制
-
public int read(ByteBuffer b)
读数据,把读到的数据放在b数组中,返回读取到的字节个数,如果读到文件的末尾就返回-1 -
public int write(ByteBuffer b)
写数据,把数据写到目的地文件中
-
FileChannel结合MappedByteBuffer实现高效读写
-
可以将文件直接映射至内存,把硬盘中的读写变成内存中的读写, 所以可以提高大文件的读写效率。
-
可以调用FileChannel的map()方法获取一个MappedByteBuffer,map()方法的原型:
MappedByteBuffer map(MapMode mode, long position, long size);
说明:将节点中从position开始的size个字节映射到返回的MappedByteBuffer中。
MapMode中定义的静态常量,这里以FileChannel举例: 1). FileChannel.MapMode.READ_ONLY:得到的镜像只能读不能写(只能使用get之类的读取Buffer中的内容);
2). FileChannel.MapMode.READ_WRITE:得到的镜像可读可写(既然可写了必然可读),对其写会直接更改到存储节点;
3). FileChannel.MapMode.PRIVATE:得到一个私有的镜像,其实就是一个(position, size)区域的副本罢了,也是可读可写,只不过写不会影响到存储节点,就是一个普通的ByteBuffer了!!
public class Test {
public static void main(String[] args) throws Exception {
// 1.创建RandomAccessFile对象,r表示读,rw表示读写
RandomAccessFile r1 = new RandomAccessFile("H:\\a.zip", "r");
RandomAccessFile r2 = new RandomAccessFile("day\aaa\\a.zip", "rw");
// 2.根据RandomAccessFile对象获取FileChanel对象
FileChannel c1 = r1.getChannel();
FileChannel c2 = r2.getChannel();
// 3.获取数据源文件的字节大小
long size = c1.size();
// 定义一个变量,表示每次平均映射的字节大小
long everySize = 500 * 1024 * 1024;
// 映射的总次数:
long count = size % everySize == 0 ? size / everySize : size / everySize + 1;
// 循环映射
for (long i = 0; i < count; i++) {
//起始位置:
long start = i * everySize;
//每次真正映射的字节大小:
long trueSize = size - start > everySize ? everySize : size - start;
// 4.映射操作
MappedByteBuffer m1 = c1.map(FileChannel.MapMode.READ_ONLY, start, trueSize);
MappedByteBuffer m2 = c2.map(FileChannel.MapMode.READ_WRITE, start, trueSize);
// 5.复制字节数据
for (long j = 0; j < trueSize; j++) {
byte b = m1.get();
m2.put(b);
}
}
// 6.释放资源
c2.close();
c1.close();
r2.close();
r1.close();
}
}
ServerSocketChannel和SocketChannel创建连接
public class Server {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 2.绑定端口号
ssc.bind(new InetSocketAddress(8888));
// 3.等待连接
System.out.println("等待连接...");
SocketChannel socketChannel = ssc.accept();// 阻塞
System.out.println("连接成功...");
}
}
public class Server {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 2.绑定端口号
ssc.bind(new InetSocketAddress(8888));
// 设置非阻塞
ssc.configureBlocking(false);
// 3.等待连接
System.out.println("等待连接...");
SocketChannel socketChannel = ssc.accept();// 非阻塞
System.out.println("连接成功...");
}
}
NIO网络编程收发信息
public class Server {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 2.绑定端口号
ssc.bind(new InetSocketAddress(8888));
// 3.建立连接
SocketChannel sc = ssc.accept();
// 4.接收数据
ByteBuffer b = ByteBuffer.allocate(1024);
int len = sc.read(b);
System.out.println("服务器接收到的数据是:"+new String(b.array(),0,len));
// 5.释放资源
sc.close();
ssc.close();
}
}
public class Client {
public static void main(String[] args) throws Exception{
// 1.打开客户端通道
SocketChannel sc = SocketChannel.open();
// 2.连接服务器
sc.connect(new InetSocketAddress("127.0.0.1",8888));
// 3.写数据给服务器
byte[] bytes = "服务器你好,今晚约吗?".getBytes();
ByteBuffer b = ByteBuffer.wrap(bytes);
sc.write(b);
// 4.释放资源
sc.close();
}
}
Selector(选择器)
选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。
"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。
选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。
"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。
如果不使用“多路复用”,服务器端需要开很多线程处理每个端口的请求。如果在高并发环境下,造成系统性能下降。
使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势
选择器Selector的获取和注册
Selector selector = Selector.open();
channel.configureBlocking(false);// 一定要设置非阻塞
SelectionKey key =channel.register(selector,SelectionKey.OP_ACCEPT);
public class Test1 {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 2.绑定端口号
ssc.bind(new InetSocketAddress(6666));
// 3.设置非阻塞
ssc.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
System.out.println(1);
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println(2);
}
}
public class Test2 {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
// 2.绑定端口号
ssc1.bind(new InetSocketAddress(7777));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(9999));
// 3.设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
ssc3.register(selector, SelectionKey.OP_ACCEPT);
System.out.println(1);
}
}
Selector的select()方法:---->面试
public class Test1_select方法 {
public static void main(String[] args) throws Exception{
// 1.打开服务器通道
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
// 2.绑定端口号
ssc1.bind(new InetSocketAddress(7777));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(9999));
// 3.设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
ssc3.register(selector, SelectionKey.OP_ACCEPT);
while (true){
System.out.println(1);
// 监听客户端的请求
selector.select();
System.out.println(2);
}
}
}
Selector的keys()方法
public class Test1_select方法 {
public static void main(String[] args) throws Exception {
// 1.打开服务器通道
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
// 2.绑定端口号
ssc1.bind(new InetSocketAddress(7777));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(9999));
// 3.设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
ssc3.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("已注册的所有通道集合:" + selector.keys().size());
while (true) {
System.out.println(1);
// 监听客户端的请求
selector.select();
System.out.println(2);
System.out.println("已注册的所有通道集合:" + selector.keys().size());
}
}
}
Selector的selectedKeys()方法
public class Test2_selectedKeys方法 {
public static void main(String[] args) throws Exception {
// 1.打开服务器通道
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
// 2.绑定端口号
ssc1.bind(new InetSocketAddress(7777));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(9999));
// 3.设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
ssc3.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("已注册的所有通道集合:" + selector.keys().size());
while (true) {
System.out.println(1);
// 监听客户端的请求
selector.select();
System.out.println(2);
// 处理客户端的请求
// 获取已连接的所有通道集合
Set<SelectionKey> keys = selector.selectedKeys();
// 循环遍历所有通道集合
for (SelectionKey key : keys) {
// SelectionKey-->其实就是封装了ServerSocketChannel
// 通过SelectionKey获得ServerSocketChannel
// SelectableChannel是ServerSocketChannel的父类
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
// 处理客户端的请求,建立连接
SocketChannel sc = ssc.accept();
// 接收客户端发过来的数据
ByteBuffer b = ByteBuffer.allocate(1024);
int len = sc.read(b);
System.out.println("服务器接收到的数据是:" + new String(b.array(), 0, len));
// 处理完了,释放资源
sc.close();
}
System.out.println("已注册的所有通道集合:" + selector.keys().size());
}
}
}
Selector多路复用
public class Test {
public static void main(String[] args) throws Exception {
// 1.打开服务器通道
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
// 2.绑定端口号
ssc1.bind(new InetSocketAddress(7777));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(9999));
// 3.设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
// 4.获取选择器
Selector selector = Selector.open();
// 5.把服务器通道注册到选择器上
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
ssc3.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("已注册的所有通道集合:" + selector.keys().size());
while (true) {
System.out.println(1);
// 监听客户端的请求
selector.select();
System.out.println(2);
// 处理客户端的请求
// 获取已连接的所有通道集合
Set<SelectionKey> keys = selector.selectedKeys();
// 循环遍历所有通道集合
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
// SelectionKey-->其实就是封装了ServerSocketChannel
// 通过SelectionKey获得ServerSocketChannel
// SelectableChannel是ServerSocketChannel的父类
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
// 处理客户端的请求,建立连接
SocketChannel sc = ssc.accept();// null
// 接收客户端发过来的数据
ByteBuffer b = ByteBuffer.allocate(1024);
int len = sc.read(b);
System.out.println("服务器接收到的数据是:" + new String(b.array(), 0, len));
// 处理完了,释放资源
sc.close();
// 移除keys集合中已处理的服务器通道--->迭代器
it.remove();
}
System.out.println("已注册的所有通道集合:" + selector.keys().size());
}
}
}
NIO2-AIO(异步、非阻塞)
public class Server {
public static void main(String[] args) throws Exception{
// 1.打开异步服务器通道
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
// 2.绑定端口号
assc.bind(new InetSocketAddress(7777));
// 3.接收请求,建立连接
System.out.println(1);
assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
// 连接成功,就会来到这里
System.out.println(2);
}
@Override
public void failed(Throwable exc, Object attachment) {
// 连接失败,就会来到这里
System.out.println(3);
}
});
System.out.println(4);
while (true){
}
}
}
AIO 异步非阻塞连接和异步读
public class Server {
public static void main(String[] args)throws Exception{
// 1.打开异步服务器通道
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
// 2.绑定端口号
assc.bind(new InetSocketAddress(7777));
// 3.接收请求,建立连接
System.out.println(1);
assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel asc, Object attachment) {
// 连接成功
System.out.println(2);
// 创建ByteBuffer对象
ByteBuffer b = ByteBuffer.allocate(1024);
// 异步读
asc.read(b, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer len, Object attachment) {
// 读成功
System.out.println(5);
System.out.println("服务器接收到的信息:"+new String(b.array(),0,len));
}
@Override
public void failed(Throwable exc, Object attachment) {
// 读失败
System.out.println(6);
}
});
System.out.println(7);
}
@Override
public void failed(Throwable exc, Object attachment) {
// 连接失败
System.out.println(3);
}
});
System.out.println(4);
// 为了让程序不结束
while (true){
}
}
}