服務端
public class Server {
// 通道管理器
private Selector selector;
/**
* 启动服务端测试
*
* @throws IOException
*/
public static void main(String[] args) throws IOException, InterruptedException {
Server server = new Server();
server.initServer(12345);
server.listen();
}
/**
* 获得一个ServerSocket通道,并对该通道做一些初始化的工作
*
* @param port
* 绑定的端口号
* @throws IOException
*/
public void initServer(int port) throws IOException {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞(与select一起使用必须为非阻塞)
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(new InetSocketAddress(port));
// 获得一个通道管理器
this.selector = Selector.open();
// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
// 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
*
* @throws IOException
*/
public void listen() throws IOException, InterruptedException {
System.out.println("服务端启动成功!");
// 轮询访问selector
while (true) {
// 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator<?> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 由于select操作只管对selectedKeys进行添加,所以key处理后我们需要从里面把key去掉
ite.remove();
handler(key);
}
System.out.println("結束本次select的查詢");
}
}
/**
* 处理请求
*
* @param key
* @throws IOException
*/
public void handler(SelectionKey key) throws IOException {
// 客户端请求连接事件
if (key.isAcceptable()) {
handlerAccept(key);
// 获得了可读的事件
} else if (key.isReadable()) {
handelerRead(key);
}
}
/**
* 处理连接请求
*
* @param key
* @throws IOException
*/
public void handlerAccept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false);
// 在这里可以给客户端发送信息哦
System.out.println("新的客户端连接");
// 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ);
}
/**
* 处理读的事件
*
* @param key
* @throws IOException
*/
public void handelerRead(SelectionKey key) throws IOException {
// 服务器可读取消息:得到事件发生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = 0;
try {
read = channel.read(buffer);
} catch (IOException e) {
key.cancel();
channel.close();
return ;
}
String content = "";
if(read > 0){
buffer.flip(); //为write()准备
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
content+=new String(bytes);
System.out.println("收到百果園客戶的問題:" + content);
//回应客户端
doWrite(channel);
}else{
System.out.println("客户端关闭");
key.cancel();
}
// 写完就把状态关注去掉,否则会一直触发写事件(改变自身关注事件)
key.interestOps(SelectionKey.OP_READ);
}
private void doWrite(SocketChannel sc) throws IOException{
String content = "百果園的水果是天下最好吃的。";
byte[] req =content.getBytes();
ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
byteBuffer.put(req);
byteBuffer.flip();
sc.write(byteBuffer);
if(!byteBuffer.hasRemaining()){
System.out.println("回答百果園客戶的問題:" + content);
}
}
}
客戶端
public class SocketClient {
/**
* 服务器地址
*/
public static final String IP_ADDR = "localhost";
private Selector selector = null;
private volatile boolean stop = false;
/**
* 服务器端口号
*/
public static final int PORT = 12345;
public static void main(String[] args) throws IOException {
System.out.println("客户端启动");
SocketClient client = new SocketClient();
client.init();
}
public void init() throws IOException {
selector = Selector.open();
SocketChannel channel = SocketChannel.open();
// 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接)
channel.configureBlocking(false);
if (channel.connect(new InetSocketAddress(12345))) {
channel.register(selector, SelectionKey.OP_READ);
//发送消息
doWrite(channel, "百果園的水果好吃嗎 ??");
} else {
channel.register(selector, SelectionKey.OP_CONNECT);
}
while (!stop) {
selector.select(1000);
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
handler(key);
}
}
}
private void handler(SelectionKey key) {
// OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true
if (key.isConnectable()) {
handlerConnection(key);
}else if (key.isReadable()) {
handlerReader(key);
}
}
/**
* 处理连接请求
*
* @param key
* @throws IOException
*/
public void handlerConnection(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
// 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功
// 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中)
try {
if (channel.finishConnect()) {
channel.register(selector, SelectionKey.OP_READ);
// new Thread(new DoWrite(channel)).start();
doWrite(channel, "百果園的水果好吃嗎 ??");
} else {
//链接失敗
channel.close();
key.cancel();
}
} catch (IOException e) {
//链接失败,进程推出或直接抛出IOException
System.exit(1);
}
}
/**
* 处理讀任務
*
* @param key
* @throws IOException
*/
public void handlerReader(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
//读取服务端的响应
ByteBuffer buffer = ByteBuffer.allocate(1024);
String content = "";
try {
int readBytes = channel.read(buffer);
if (readBytes > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
content += new String(bytes);
stop = true;
System.out.println(content);
key.interestOps(SelectionKey.OP_READ);
} else if (readBytes < 0) {
//对端链路关闭
key.cancel();
channel.close();
}
} catch (IOException e) {
//讀取數據出現IO Exception, 进程推出或直接抛出IOException
System.exit(1);
}
}
private void doWrite(SocketChannel sc,String data) throws IOException{
byte[] req =data.getBytes();
ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
byteBuffer.put(req);
byteBuffer.flip();
sc.write(byteBuffer);
if(!byteBuffer.hasRemaining()){
System.out.println("發送百果園客戶的問題: " + data);
}
}
}