package cn.rntd.ha.socket;
import cn.rntd.common.util.LogUtil;
import cn.rntd.common.util.execption.CustomException;
import cn.rntd.common.util.map.EMap;
import cn.rntd.core.mode.Mode;
import cn.rntd.core.pool.ExecutorServiceUtil;
import cn.rntd.entity.po.ai.UserEnergy;
import cn.rntd.ha.mapper.DialogMapper;
import cn.rntd.ha.mapper.WeChatMapper;
import cn.rntd.ha.service.DialogService;
import cn.rntd.ha.service.EnergyService;
import cn.rntd.ha.service.PHPService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.net.InetSocketAddress;
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.nio.charset.Charset;
import java.util.*;
/**
* @Title: socket服务
* @Description:
* @params:
* @author oak
* @date 2023/8/16 11:48
*/
@Component
public class NIOServer {
private static final int BUF_SIZE = 1024;
private static final int PORT = 80;
private static final int TIMEOUT = 3000;//3秒遍历
public void run(ApplicationArguments args) throws Exception {
Selector selector = null;
ServerSocketChannel ssc = null;
try {
//4.获取选择器
selector = Selector.open();
//1.获取通道
ssc = ServerSocketChannel.open();
//2.切换非阻塞模式
ssc.configureBlocking(false);
//3.绑定连接
ssc.socket().bind(new InetSocketAddress(PORT));
//5.将通道注入到选择器上,并且指定“监听接收事件”
ssc.register(selector, SelectionKey.OP_ACCEPT);
LogUtil.info("socket启动了");
//6.轮询获取选择器上已经“准备就绪”的事件
while (true) {
if (selector.select(TIMEOUT) == 0) {
LogUtil.info(".");
continue;
}
//7.
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
//8.获取准备“就绪”的事件
SelectionKey key = iter.next();
if (key.isAcceptable()) {
handleAccept(key);
}
if (key.isReadable()) {
handleRead(key);
}
if (key.isValid() && key.isWritable()) {
handleWrite(key);
}
if (key.isValid() && key.isConnectable()) {
System.out.println("isConnectable = true");
}
iter.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (selector != null) {
selector.close();
}
if (ssc != null) {
ssc.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
//10.若准备就绪,获取客户端连接
SocketChannel sc = ssChannel.accept();
//11.设置非阻塞模式
sc.configureBlocking(false);
//12.将该通道注册到选择器上
sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));
}
public void handleRead(SelectionKey key) throws IOException {
//13.获取当前选择器上“读就绪”状态的通道
SocketChannel sc = (SocketChannel) key.channel();
//14.读取数据
//如果是第二次及以上问问题,则此时key中bytebuffer的容量可能为回答问题时最后的两三个字节,必须重新设置bytebuffer大小
// ByteBuffer readBuffer = (ByteBuffer) key.attachment();
ByteBuffer readBuffer = ByteBuffer.allocate(BUF_SIZE);
// long bytesRead = sc.read(readBuffer );
// while (bytesRead > 0) {
// readBuffer .flip();
// char[] ch = {};
// int i = 0;
// while (readBuffer .hasRemaining()) {
// ch[i] = (char) readBuffer .get();
// i++;
// }
// System.out.println(ch.toString());
// System.out.println();
// readBuffer.clear();
// bytesRead = sc.read(readBuffer );
// }
// if (bytesRead == -1) {
// sc.close();
// }
String msg = "";
Charset charset = Charset.forName("utf-8");
while(sc.read(readBuffer) > 0){
//将缓存切换为读模式
readBuffer.flip();
//解析出客户端发送的数据
msg += charset.decode(readBuffer);
System.out.println(msg);
}
if(msg.isEmpty()){
return;
}
readBuffer.clear();
// 读完数据后,为 SelectionKey 注册可写事件
if (!isInterest(key, SelectionKey.OP_WRITE)) {
key.interestOps(key.interestOps() + SelectionKey.OP_WRITE);
}
//TODO 根据获取的wxUserId ,调取php接口,返回的数据,循环 attach
try{
//线程外转换,msg传递不进去
JSONObject jo = JSONObject.parseObject(msg);
EMap eMap = new EMap();
eMap.put("question",jo.getString("question"));
eMap.put("sceneId",jo.getString("sceneId"));
eMap.put("wxUserId",jo.getString("wxUserId"));
ExecutorServiceUtil.getPool().submit(new Thread(()->{
try{
dialogService.asynAnswer(key,eMap);
// 关闭写模式。注销掉是因为,selector内没遍历完全部的写,这里就关闭,导致数据没有全部发送出去,等下次再打开,数据就不连续
// key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
// sc.shutdownOutput();
}catch (CustomException c){
//能量不足
Map res = new HashMap();
res.put("state","-2");
res.put("answer","您的能量不足!不能问问题!");
byte[] b = JSON.toJSONBytes(res);
ByteBuffer writeBuffer = ByteBuffer.allocate(b.length);
writeBuffer.put(b);
key.attach(writeBuffer);
c.printStackTrace();
}catch (Exception e){
Map res = new HashMap();
res.put("state","-1");
res.put("answer","服务出现问题!");
byte[] b = JSON.toJSONBytes(res);
ByteBuffer writeBuffer = ByteBuffer.allocate(b.length);
writeBuffer.put(b);
key.attach(writeBuffer);
e.printStackTrace();
}finally {
Map res = new HashMap();
res.put("state","1");
res.put("answer","回答结束");
byte[] b = JSON.toJSONBytes(res);
ByteBuffer writeBuffer = ByteBuffer.allocate(b.length);
writeBuffer.put(b);
key.attach(writeBuffer);
}
}));
}catch (Exception e){
e.printStackTrace();
}
}
@Autowired
EnergyService energyService;
@Autowired
DialogService dialogService;
@Autowired
WeChatMapper weChatMapper;
/**
* @Title: asynAnswer
* @Description:
* @params: [key, json]
* @author oak
* @date 2023/8/15 16:19
*/
@Transactional
public void asynAnswer(SelectionKey key,EMap eMap) throws Exception{
String question = eMap.getString("question");
long sceneId = eMap.getLong("sceneId", 0l);//0代表非场景会话
long wxUserId = eMap.getLong("wxUserId");
UserEnergy ue = weChatMapper.queryEnergy(wxUserId);
//1、先扣钱
energyService.deductEne(eMap);
//2、调用ai接口
String answer = PHPService.chatApiStream(question,ue.getToken(),key);
//3、保存会话记录
Map<String,String> dialog = new HashMap<>();
dialog.put("user",question);
dialog.put("ai",answer);
eMap.put("dialog", JSON.toJSONString(dialog));
dialogService.saveDialog(eMap);
}
// @Mode
// @Transactional
// public String sendDatail(EMap eMap){
// String question = eMap.getString("question");
// long sceneId = eMap.getLong("sceneId", 0l);//0代表非场景会话
// long wxUserId = eMap.getLong("wxUserId");
//
// UserEnergy ue = weChatMapper.queryEnergy(wxUserId);
// //先扣钱
// energyService.deductEne(eMap);
//
// //TODO ai接口
// String answer = phpService.chatApi(question,ue.getToken());
//
// return answer;
// }
public static void handleWrite(SelectionKey key) throws IOException {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.flip();
SocketChannel sc = (SocketChannel) key.channel();
while (buf.hasRemaining()) {
sc.write(buf);
}
buf.compact();
// key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
}
// 判断 SelectionKey 对某个事件是否感兴趣
private static boolean isInterest(SelectionKey selectionKey, int event) {
int interestSet = selectionKey.interestOps();
boolean isInterest = (interestSet & event) == event;
return isInterest;
}
}
记录NIO实现
最新推荐文章于 2024-08-03 14:16:19 发布