1. NIO完成网络编程
1.1 Selector选择器老大
Selector
选择器,网络编程使用NIO的大哥!!!
服务器可以执行一个线程,运行Selector程序,进行监听操作。
新连接, 已经连接, 读取数据,写入数据
Selector常用方法:
public static Selector Open();
得到一个选择器对象
public int select(long timeout);
监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集
合中,参数是一个超时时间
public Set<SelectionKey> selectionKeys();
返回当前Selector内部集合中保存的所有SelectionKey
1.2 SelectionKey
SelectionKey
表示Selector和网络通道直接的关系
int OP_ACCEPT; 16 需要连接
int OP_CONNECT; 8 已经连接
int OP_READ; 1 读取操作
int OP_WRITE; 4 写入操作
SelectionKey
public abstract Selector selector();
得到与之关联的 Selector 对象
public abstract SelectableChannel channel();
得到与之关联的通道
public final Object attachment();
得到与之关联的共享数据
public abstract SelectionKey interestOps(int ops);
设置或改变监听事件
public final boolean isAcceptable();
是否可以 accept
public final boolean isReadable();
是否可以读
public final boolean isWritable();
是否可以写
1.3 ServerSocketChannel
ServerSocketChannel
服务端Socket程序对应的Channel通道
常用方法:
public static ServerSocketChannel open();
开启服务器ServerSocketChannel通道,等于开始服务器程序
public final ServerSocketChannel bind(SocketAddress local);
设置服务器端端口号
public final SelectableChannel configureBlocking(boolean block);
设置阻塞或非阻塞模式, 取值 false 表示采用非阻塞模式
public SocketChannel accept();
[非阻塞]
获取一个客户端连接,并且得到对应的操作通道
public final SelectionKey register(Selector sel, int ops);
[重点方法]
注册当前选择器,并且选择监听什么事件
1.4 SocketChannel
SocketChannel
客户端Socket对应的Channel对象
常用方法:
public static SocketChannel open();
打卡一个Socket客户端Channel对象
public final SelectableChannel configureBlocking(boolean block)
这里可以设置是阻塞状态,还是非阻塞状态
false,表示非阻塞
public boolean connect(SocketAddress remote);
连接服务器
public boolean finishConnect();
如果connect连接失败,可以通过finishConnect继续连接
public int write(ByteBuffer buf);
写入数据到缓冲流中
public int read(ByteBuffer buf); 、
从缓冲流中读取数据
public final SelectionKey register(Selector sel, int ops, Object attechment);
注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attachment参数
public final void close();
关闭SocketChannel
1.5 使用NIO完成一个客户端和服务器
1.5.1 首先完成客户端
package com. qfedu. a_tcpnio;
import java. io. IOException;
import java. net. InetSocketAddress;
import java. nio. ByteBuffer;
import java. nio. channels. SocketChannel;
import java. util. Scanner;
import java. util. concurrent. ThreadPoolExecutor;
public class TcpNioClient {
public static void main ( String[ ] args) throws IOException, InterruptedException {
SocketChannel socket = SocketChannel. open ( ) ;
socket. configureBlocking ( false ) ;
InetSocketAddress address = new InetSocketAddress ( "192.168.31.154" , 8848 ) ;
if ( ! socket. connect ( address) ) {
while ( ! socket. finishConnect ( ) ) {
System. out. println ( "保持呼叫服务器状态,但是我还能做点别的事情~~~ 等待2s继续申请连接~~~" ) ;
Thread. sleep ( 2000 ) ;
}
}
ByteBuffer buffer = ByteBuffer. wrap ( "你好,服务器,我在等你..." . getBytes ( ) ) ;
socket. write ( buffer) ;
new Scanner ( System. in) . nextLine ( ) ;
}
}
1.5.2 再来完成服务端
1. 开启服务器
ServerScoketChannel
2. 开启Selector 大哥
Selector对象
3. 服务器ServerSocketChannel bind监听端口号
8848端口
4. 设置非阻塞状态
configureBlocking(false)
5. Selector 注册--> ServerSocketChannel
register(selector, OP_ACCEPT);
6. Selector大哥开始忙活
6.1 获取连接,注册对应的Socket
6.2 监听读写事件
6.2.1 读数据。客户端发送数据到服务器
6.2.2 写数据。发送数据数据给客户端
package com. qfedu. a_tcpnio;
import java. io. IOException;
import java. net. InetSocketAddress;
import java. nio. ByteBuffer;
import java. nio. channels. *;
import java. util. Iterator;
import java. util. Set;
public class TcpNioServer {
public static void main ( String[ ] args) throws IOException {
ServerSocketChannel serverSocket = ServerSocketChannel. open ( ) ;
Selector selector = Selector. open ( ) ;
serverSocket. bind ( new InetSocketAddress ( 8848 ) ) ;
serverSocket. configureBlocking ( false ) ;
serverSocket. register ( selector, SelectionKey. OP_ACCEPT) ;
while ( true ) {
if ( 0 == selector. select ( 1000 ) ) {
System. out. println ( "ServerSocket提示,当前没有客户端搭理我,我自己默默的画圈圈~~~" ) ;
continue ;
}
Iterator< SelectionKey> selectionKeys = selector. selectedKeys ( ) . iterator ( ) ;
while ( selectionKeys. hasNext ( ) ) {
SelectionKey selectionKey = selectionKeys. next ( ) ;
if ( selectionKey. isAcceptable ( ) ) {
System. out. println ( "客户端请求连接!!!" ) ;
SocketChannel socket = serverSocket. accept ( ) ;
socket. configureBlocking ( false ) ;
socket. register ( selector, SelectionKey. OP_READ, ByteBuffer. allocate ( 1024 * 4 ) ) ;
}
if ( selectionKey. isReadable ( ) ) {
SocketChannel socket = ( SocketChannel) selectionKey. channel ( ) ;
ByteBuffer buffer = ( ByteBuffer) selectionKey. attachment ( ) ;
socket. read ( buffer) ;
System. out. println ( "客户端发送数据:" + new String ( buffer. array ( ) ) ) ;
}
selectionKeys. remove ( ) ;
}
}
}
}
2. NIO完成一个TCP聊天室
2.1 NIO TCP聊天室客户端完成
package com. qfedu. b_niochat;
import java. io. IOException;
import java. net. InetSocketAddress;
import java. nio. ByteBuffer;
import java. nio. channels. SocketChannel;
public class ChatClient {
private static final String HOST = "192.168.31.154" ;
private static final int PORT = 8848 ;
private SocketChannel socket;
private String userName;
public ChatClient ( String userName) throws IOException, InterruptedException {
socket = SocketChannel. open ( ) ;
socket. configureBlocking ( false ) ;
InetSocketAddress address = new InetSocketAddress ( HOST, PORT) ;
if ( ! socket. connect ( address) ) {
while ( ! socket. finishConnect ( ) ) {
System. out. println ( "服务器请求连接失败,等待2s继续请求连接..." ) ;
Thread. sleep ( 2000 ) ;
}
}
this . userName = userName;
System. out. println ( "客户端 " + userName + " 准备就绪" ) ;
}
public void sendMsg ( String message) throws IOException {
if ( "close" . equals ( message) ) {
socket. close ( ) ;
return ;
}
message = userName + ":" + message;
ByteBuffer buffer = ByteBuffer. wrap ( message. getBytes ( ) ) ;
socket. write ( buffer) ;
}
public void receiveMsg ( ) throws IOException {
ByteBuffer buffer = ByteBuffer. allocate ( 1024 ) ;
int length = socket. read ( buffer) ;
if ( length > 0 ) {
System. out. println ( new String ( buffer. array ( ) ) ) ;
}
}
}
2.2 NIO TCP聊天室服务端完成
package com. qfedu. b_niochat;
import java. io. IOException;
import java. net. InetSocketAddress;
import java. nio. ByteBuffer;
import java. nio. channels. *;
import java. util. Iterator;
import java. util. Set;
public class ChatServer {
private ServerSocketChannel serverSocket;
private Selector selector;
private static final int PORT = 8848 ;
public ChatServer ( ) throws IOException {
serverSocket = ServerSocketChannel. open ( ) ;
selector = Selector. open ( ) ;
serverSocket. bind ( new InetSocketAddress ( PORT) ) ;
serverSocket. configureBlocking ( false ) ;
serverSocket. register ( selector, SelectionKey. OP_ACCEPT) ;
}
public void start ( ) {
try {
while ( true ) {
if ( 0 == selector. select ( 2000 ) ) {
System. out. println ( "服务器默默的等待连接,无人访问..." ) ;
continue ;
}
Iterator< SelectionKey> iterator = selector. selectedKeys ( ) . iterator ( ) ;
while ( iterator. hasNext ( ) ) {
SelectionKey key = iterator. next ( ) ;
if ( key. isAcceptable ( ) ) {
SocketChannel socket = serverSocket. accept ( ) ;
socket. configureBlocking ( false ) ;
socket. register ( selector, SelectionKey. OP_READ) ;
broadcast ( socket, socket. getRemoteAddress ( ) . toString ( ) + "上线了" ) ;
}
if ( key. isReadable ( ) ) {
readMsg ( key) ;
}
iterator. remove ( ) ;
}
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
public void readMsg ( SelectionKey key) throws IOException {
SocketChannel socket = ( SocketChannel) key. channel ( ) ;
ByteBuffer buffer = ByteBuffer. allocate ( 1024 ) ;
int length = socket. read ( buffer) ;
if ( length > 0 ) {
String message = new String ( buffer. array ( ) ) ;
broadcast ( socket, message) ;
}
}
public void broadcast ( SocketChannel self, String message) throws IOException {
Set< SelectionKey> keys = selector. keys ( ) ;
for ( SelectionKey key : keys) {
SelectableChannel channel = key. channel ( ) ;
if ( channel instanceof SocketChannel && ! channel. equals ( self) ) {
SocketChannel socketChannel = ( SocketChannel) channel;
ByteBuffer buffer = ByteBuffer. wrap ( message. getBytes ( ) ) ;
socketChannel. write ( buffer) ;
}
}
}
public static void main ( String[ ] args) throws IOException {
new ChatServer ( ) . start ( ) ;
}
}
##### 2.3 NIO TCP聊天室客户端线程代码实现
```java
package com. qfedu. b_niochat;
import java. io. IOException;
import java. util. Scanner;
public class ChatClientThread {
public static void main ( String[ ] args) throws IOException, InterruptedException {
Scanner scanner = new Scanner ( System. in) ;
System. out. println ( "请输入用户名:" ) ;
String userName = scanner. nextLine ( ) ;
if ( 0 == userName. length ( ) ) {
return ;
}
ChatClient chatClient = new ChatClient ( userName) ;
new Thread ( ( ) - > {
while ( true ) {
try {
chatClient. receiveMsg ( ) ;
Thread. sleep ( 2000 ) ;
} catch ( IOException | InterruptedException e) {
e. printStackTrace ( ) ;
}
}
} ) . start ( ) ;
while ( scanner. hasNextLine ( ) ) {
String msg = scanner. nextLine ( ) ;
chatClient. sendMsg ( msg) ;
}
}
}