一、SocketChanel
1、创建SocketChannel
有两种方法可以获取一个SocketChannel实例1. 通过静态方法open打开一个
SocketChannel socketChannel = SocketChannel.open();
ServerSocketChannel接受一个连接请求后等到
SocketChannel socketChannel = serverSocketChannel.accept();
获取到实例后可通过connect方法与服务端建立连接:
socketChannel.connect(new InetSocketAddress(1234));
socketChannel.connect(new InetSocketAddress(1234));
2、读写数据
SocketChannel的读写数据与其他通道没有区别,读数据使用多个read方法,将数据读取如一个buffer中,返回一个int值,表示成功读取的字节数,返回-1表示读取到了数据流的末尾了;sizeBuffer.clear();
int read = socketChannel.read(sizeBuffer);
使用write方法将buffer中的数据写入到通道中,同样需要循环写入;
buffer.flip();
while (buffer.hasRemaining()) {
socketChannel.write(dest);
}
buffer.flip();
while (buffer.hasRemaining()) {
socketChannel.write(dest);
}
3、非阻塞模式
非阻塞就是普通套接字的区别了,SocketChannel在非阻塞模式下,许多方法都是可能直接返回不等待的:
1. connect方法,可能没有完成连接建立就已经返回,需要使用finishConnect,判断是否完成了连接;
2. read方法,可能没有读取任何就返回了(不是到数据末尾),需要通过返回值判断;
3. write方法,与阻塞模式一样,需要在循环中写入;
二、ServerSocketChannel
1、创建ServerSocketChannel
通过静态方法open获取一个实例,并使用bind方法绑定地址:ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(1234));
完成绑定后,可以通过accept方法,接受客户端连接请求,与客户端建立连接后,获取到一个SocketChannel实例,通过这个实例与建立连接的客户端进行通信;
2、非阻塞模式
ServerSocketChannel同样有非阻塞模式,此时,accept方法在没有连接请求是,可能返回Null,需要做判断处理;三 实例
服务端代码:import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
public static int flag = 1;
public static void main(String[] args) throws IOException {
// 1. 定义ServerSocketChannel 并监听本机指定的端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 设置非阻塞模式
serverSocketChannel.configureBlocking(false);
// 3. 检索与此通道关联的服务器套接字
ServerSocket serverSocket = serverSocketChannel.socket();
// 4. 进行服务的绑定
serverSocket.bind(new InetSocketAddress(8889));
// 5. 定义Selector
Selector selector = Selector.open();
// 6. 注册selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 7.保持Server运行
while (true) {
// 7.1 返回读事件已经就绪的那些通道。 并进行判断
if (selector.select(1000 * 4) == 0) {
log("服务端在等待....");
continue;
}
// 7.2 一旦调用了select()方法,并且返回值表明有一个或更多个通道就绪了
// 然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key
// set)”中的就绪通道。
// --- 返回此选择器的已选择键集。
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeySet.iterator();
SocketChannel client = null;
// 7.3 遍历获取
while (iterator.hasNext()) {
SelectionKey myKey = iterator.next();
// 7.4 此键的通道是否已准备好接受新的套接字连接。
if (myKey.isAcceptable()) {
// 7.4.1接受到此通道套接字的连接。
// 此方法返回的套接字通道(如果有)将处于阻塞模式。
client = serverSocketChannel.accept();
// 7.4.2配置为非阻塞
client.configureBlocking(false);
Socket socket = client.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr + " \t Connection Accepted: \n");
// 7.4.3注册到selector,等待连接
client.register(selector, SelectionKey.OP_READ);
}
if (myKey.isReadable()) {
// 7.5.1 返回为之创建此键的通道。
client = (SocketChannel) myKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 3);
// 7.5.2 将缓冲区清空以备下次读取
byteBuffer.clear();
// 读取服务器发送来的数据到缓冲区中
int readCount = -1;
readCount = client.read(byteBuffer);
// 如果没有数据 则关闭
if (readCount == -1) {
Socket socket = client.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr);
client.close();
myKey.cancel();
return;
}
// 有数据 则打印输出
String receiveText = new String( byteBuffer.array(),0,readCount);
log("服务器端接受客户端数据--:"+receiveText);
}
// 移除
iterator.remove();
}
}
}
private static void log(String str) {
System.out.println(str);
}
}
客户端代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
public class NIOClient {
public static void main(String[] args) throws IOException, InterruptedException {
// 1.在客户端定义ScoketChannel 连接指定的服务端
InetSocketAddress crunchifyAddr = new InetSocketAddress("localhost", 8889);
SocketChannel crunchifyClient = SocketChannel.open(crunchifyAddr);
log("Connecting to Server on port 8889...");
// 2.定义发送的数据
ArrayList<String> companyDetails = new ArrayList<String>();
companyDetails.add("Lenovo");
companyDetails.add("Samsung");
companyDetails.add("Huawei");
companyDetails.add("Facebook");
companyDetails.add("Twitter");
companyDetails.add("IBM");
companyDetails.add("Google");
// 3.循环发送
for (String companyName : companyDetails) {
// 3.1 将字符转转换为字节
byte[] message = new String(companyName).getBytes("UTF-8");
// 3.2 定义Buffer 并将字节数组数据封装在Buffer中
ByteBuffer buffer = ByteBuffer.wrap(message);
// 3.3 Channel读取Buffer中的数据
crunchifyClient.write(buffer);
log("sending: " + companyName);
// 清空Buffer
buffer.clear();
// 等待2000毫秒
Thread.sleep(2000);
}
// 关闭Channel
crunchifyClient.close();
}
private static void log(String str) {
System.out.println(str);
}
}