29 changed files with 664 additions and 201 deletions
@ -0,0 +1,39 @@
|
||||
package net.sopod.soim.common.util; |
||||
|
||||
import java.lang.reflect.Type; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Reflects |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 23:54 |
||||
*/ |
||||
public class Reflects { |
||||
|
||||
/** |
||||
* 获取父类上的泛型 |
||||
* @return 父类上的泛型 |
||||
*/ |
||||
public static List<String> getSuperclassGenericTypes(Class<?> clazz) { |
||||
// 获取 handler 的泛型消息
|
||||
Type superType = clazz.getGenericSuperclass(); |
||||
String typeName = superType.getTypeName(); |
||||
int idx = typeName.indexOf('<'); |
||||
if (idx == -1) { |
||||
// 父类没有泛型
|
||||
return Collections.emptyList(); |
||||
} |
||||
String genericName = typeName.substring(idx + 1, typeName.length() - 1); |
||||
// 父类只有一个泛型
|
||||
if (!genericName.contains(",")) { |
||||
return Collections.singletonList(genericName); |
||||
} |
||||
// 父类有多个泛型
|
||||
String[] genericNames = genericName.split(", "); |
||||
return Arrays.asList(genericNames); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,28 @@
|
||||
package net.sopod.soim.core.handler; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import net.sopod.soim.core.session.Account; |
||||
import net.sopod.soim.core.session.NetUser; |
||||
|
||||
/** |
||||
* AccountMessageHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 23:41 |
||||
*/ |
||||
public abstract class AccountMessageHandler<T> implements MessageHandler<T> { |
||||
|
||||
@Override |
||||
public final void exec(NetUser netUser, T msg) { |
||||
if (!netUser.isAccount()) { |
||||
throw new IllegalStateException("NetUser is not account!" + netUser); |
||||
} |
||||
MessageLite res = handle((Account) netUser, msg); |
||||
if (res != null) { |
||||
netUser.write(res); |
||||
} |
||||
} |
||||
|
||||
public abstract MessageLite handle(Account account, T msg); |
||||
|
||||
} |
||||
@ -0,0 +1,15 @@
|
||||
package net.sopod.soim.core.handler; |
||||
|
||||
import net.sopod.soim.core.session.NetUser; |
||||
|
||||
/** |
||||
* MessageHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 23:40 |
||||
*/ |
||||
public interface MessageHandler<T> { |
||||
|
||||
void exec(NetUser netUser, T msg); |
||||
|
||||
} |
||||
@ -0,0 +1,24 @@
|
||||
package net.sopod.soim.core.handler; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import net.sopod.soim.core.session.NetUser; |
||||
|
||||
/** |
||||
* NetUserMessageHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 23:40 |
||||
*/ |
||||
public abstract class NetUserMessageHandler<T> implements MessageHandler<T> { |
||||
|
||||
@Override |
||||
public final void exec(NetUser netUser, T msg) { |
||||
MessageLite res = handle(netUser, msg); |
||||
if (res != null) { |
||||
netUser.write(res); |
||||
} |
||||
} |
||||
|
||||
public abstract MessageLite handle(NetUser netUser, T msg); |
||||
|
||||
} |
||||
@ -0,0 +1,26 @@
|
||||
package net.sopod.soim.core.handler; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import net.sopod.soim.core.session.NetUser; |
||||
|
||||
/** |
||||
* ProtoMessageHandler |
||||
* <T extends MessageLite> |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 19:19 |
||||
*/ |
||||
public abstract class ProtoMessageHandler<T> { |
||||
|
||||
public final void exec(NetUser netUser, T msg) { |
||||
MessageLite res = handle(msg); |
||||
if (res != null) { |
||||
netUser.write(res); |
||||
} |
||||
} |
||||
|
||||
public abstract Class<T> type(); |
||||
|
||||
public abstract MessageLite handle(T msg); |
||||
|
||||
} |
||||
@ -0,0 +1,21 @@
|
||||
package net.sopod.soim.core.net; |
||||
|
||||
import io.netty.util.AttributeKey; |
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
/** |
||||
* AttributeKeys |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 23:26 |
||||
*/ |
||||
public interface AttributeKeys { |
||||
|
||||
/** channel 写失败次数 */ |
||||
AttributeKey<AtomicInteger> WRITE_FAIL_TIMES = AttributeKey.valueOf("WRITE_FAIL_TIMES"); |
||||
|
||||
/** channel 登录失败次数 */ |
||||
AttributeKey<AtomicInteger> LOGIN_FAIL_TIMES = AttributeKey.valueOf("LOGIN_FAIL_TIMES"); |
||||
|
||||
} |
||||
@ -1,52 +0,0 @@
|
||||
package net.sopod.soim.core.net; |
||||
|
||||
import io.netty.buffer.ByteBuf; |
||||
import io.netty.channel.ChannelHandlerContext; |
||||
import io.netty.channel.CombinedChannelDuplexHandler; |
||||
import io.netty.handler.codec.ByteToMessageDecoder; |
||||
import io.netty.handler.codec.MessageToByteEncoder; |
||||
import net.sopod.soim.data.serialize.ImMessage; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ImEntryCodec |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-28 11:29 |
||||
*/ |
||||
public class ImEntryCodec extends CombinedChannelDuplexHandler<ImEntryCodec.ImDecoder, ImEntryCodec.ImEncoder> { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImEntryCodec.class); |
||||
|
||||
public ImEntryCodec() { |
||||
super(new ImDecoder(), new ImEncoder()); |
||||
} |
||||
|
||||
public static class ImDecoder extends ByteToMessageDecoder { |
||||
@Override |
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception { |
||||
ImMessage message = ImMessage.read(byteBuf); |
||||
boolean isMagicError; |
||||
if ((isMagicError = (message == ImMessage.MAGIC_ERROR)) |
||||
|| message == ImMessage.PROTOCOL_ERROR) { |
||||
logger.warn("decode im message error: {}, remote={}, closing channel.", |
||||
isMagicError ? "MagicError" : "ProtocolError", |
||||
ctx.channel().remoteAddress()); |
||||
ctx.channel().close(); |
||||
return; |
||||
} |
||||
list.add(message); |
||||
} |
||||
} |
||||
|
||||
public static class ImEncoder extends MessageToByteEncoder<ImMessage> { |
||||
@Override |
||||
protected void encode(ChannelHandlerContext ctx, ImMessage imMessage, ByteBuf byteBuf) throws Exception { |
||||
imMessage.write(byteBuf); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,76 @@
|
||||
package net.sopod.soim.core.net; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import io.netty.buffer.ByteBuf; |
||||
import io.netty.buffer.Unpooled; |
||||
import io.netty.channel.ChannelHandlerContext; |
||||
import io.netty.channel.CombinedChannelDuplexHandler; |
||||
import io.netty.handler.codec.ByteToMessageDecoder; |
||||
import io.netty.handler.codec.MessageToByteEncoder; |
||||
import net.sopod.soim.data.proto.ProtoMessageManager; |
||||
import net.sopod.soim.data.serialize.ImMessage; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.lang.reflect.Type; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ImEntryCodec |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-28 11:29 |
||||
*/ |
||||
public class ImMessageCodec //extends MessageToMessageCodec<ByteBuf, MessageLite> {
|
||||
extends CombinedChannelDuplexHandler<ImMessageCodec.ProtoMsgDecoder, ImMessageCodec.ProtoMsgEncoder> { |
||||
public static void main(String[] args) throws ClassNotFoundException { |
||||
|
||||
Type superType = ImMessageCodec.class.getGenericSuperclass(); |
||||
String typeName = superType.getTypeName(); |
||||
int idx = typeName.indexOf('<'); |
||||
String genericName = typeName.substring(idx + 1, typeName.length() - 1); |
||||
System.out.println(genericName.trim()); |
||||
System.out.println(Arrays.toString(genericName.split(", "))); |
||||
} |
||||
private static final Logger logger = LoggerFactory.getLogger(ImMessageCodec.class); |
||||
|
||||
public ImMessageCodec() { |
||||
super(new ProtoMsgDecoder(), new ProtoMsgEncoder()); |
||||
} |
||||
|
||||
public static class ProtoMsgDecoder extends ByteToMessageDecoder { |
||||
@Override |
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception { |
||||
ImMessage message = ImMessage.read(byteBuf); |
||||
boolean isMagicError; |
||||
if ((isMagicError = (message == ImMessage.MAGIC_ERROR)) |
||||
|| message == ImMessage.PROTOCOL_ERROR) { |
||||
logger.warn("decode im message error: {}, remote={}, closing channel.", |
||||
isMagicError ? "MagicError" : "ProtocolError", |
||||
ctx.channel().remoteAddress()); |
||||
ctx.channel().close(); |
||||
return; |
||||
} |
||||
// 解码 protobuf 消息体
|
||||
int serviceNo = message.getServiceNo(); |
||||
byte[] protoByte = message.getBody(); |
||||
MessageLite protoClass = ProtoMessageManager.getProtoInstance(serviceNo); |
||||
MessageLite protoMsg = protoClass.getParserForType().parseFrom(protoByte); |
||||
list.add(protoMsg); |
||||
} |
||||
} |
||||
|
||||
public static class ProtoMsgEncoder extends MessageToByteEncoder<MessageLite> { |
||||
@Override |
||||
protected void encode(ChannelHandlerContext ctx, MessageLite message, ByteBuf byteBuf) throws Exception { |
||||
Integer serialNo = ProtoMessageManager.getSerialNo(message.getClass()); |
||||
// TODO unknow class serialNo
|
||||
ImMessage imMessage = new ImMessage() |
||||
.setServiceNo(serialNo) |
||||
.setBody(message.toByteArray()); |
||||
imMessage.write(byteBuf); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,28 +0,0 @@
|
||||
package net.sopod.soim.core.net; |
||||
|
||||
import io.netty.channel.ChannelHandlerContext; |
||||
import io.netty.channel.SimpleChannelInboundHandler; |
||||
import net.sopod.soim.data.constant.SerializeType; |
||||
import net.sopod.soim.data.serialize.ImMessage; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* ImMessageHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-28 13:27 |
||||
*/ |
||||
public class ImMessageHandler extends SimpleChannelInboundHandler<ImMessage> { |
||||
|
||||
@Override |
||||
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ImMessage imMessage) throws Exception { |
||||
byte[] body = imMessage.getBody(); |
||||
int serviceNo = imMessage.getServiceNo(); |
||||
|
||||
SerializeType serialize = SerializeType.getSerializeByOrdinal(imMessage.getSerializeType()); |
||||
Map data = serialize.getSerializer().deserialize(body, Map.class); |
||||
System.out.println(data); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,75 @@
|
||||
package net.sopod.soim.core.registry; |
||||
|
||||
import net.sopod.soim.common.util.ImClock; |
||||
import net.sopod.soim.common.util.Reflects; |
||||
import net.sopod.soim.core.handler.MessageHandler; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.context.ApplicationContext; |
||||
|
||||
import javax.annotation.Nullable; |
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.CountDownLatch; |
||||
|
||||
/** |
||||
* ProtoMessageDispatcher |
||||
* implements ApplicationContextAware |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 19:15 |
||||
*/ |
||||
public class ProtoMessageHandlerRegistry { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProtoMessageHandlerRegistry.class); |
||||
|
||||
private static final Map<Class<?>, MessageHandler<?>> TYPE_HANDLER_MAP = new HashMap<>(32); |
||||
|
||||
private static final CountDownLatch CONTEXT_AWARE_AWAIT = new CountDownLatch(1); |
||||
|
||||
/** |
||||
* spring ioc 容器中获取 msgType handler |
||||
* @param context spring ioc 上下文 |
||||
*/ |
||||
public static synchronized void registerHandlerWithApplicationContext(ApplicationContext context) { |
||||
if (CONTEXT_AWARE_AWAIT.getCount() <= 0) { |
||||
throw new IllegalStateException("proto message registry already initialed!"); |
||||
} |
||||
logger.info("proto message registry initial..."); |
||||
long start = ImClock.millis(); |
||||
Map<String, MessageHandler> beansOfType = context.getBeansOfType(MessageHandler.class); |
||||
Collection<MessageHandler> handlers = beansOfType.values(); |
||||
for (MessageHandler<?> handler : handlers) { |
||||
// 获取 handler 泛型
|
||||
List<String> genericTypes = Reflects.getSuperclassGenericTypes(handler.getClass()); |
||||
try { |
||||
Class<?> type = genericTypes.size() == 0 ? Object.class : Class.forName(genericTypes.get(0)); |
||||
MessageHandler<?> existHandler = TYPE_HANDLER_MAP.putIfAbsent(type, handler); |
||||
if (existHandler != null) { |
||||
// 消息类型有重复的 handler!
|
||||
throw new IllegalStateException("msg type " + type + " handler duplicate; " + |
||||
"[" + existHandler.getClass() + "] and [" + handler.getClass() + "]"); |
||||
} |
||||
} catch (ClassNotFoundException e) { |
||||
logger.warn("handler msgType class not found!", e); |
||||
} |
||||
} |
||||
CONTEXT_AWARE_AWAIT.countDown(); |
||||
logger.info("proto message registry complete, {} handlers at {}ms.", handlers.size(), ImClock.millis() - start); |
||||
} |
||||
|
||||
@Nullable |
||||
public static <T> MessageHandler<T> getTypeHandler(Class<T> type) { |
||||
if (CONTEXT_AWARE_AWAIT.getCount() > 0) { |
||||
try { |
||||
CONTEXT_AWARE_AWAIT.await(); |
||||
} catch (InterruptedException e) { |
||||
logger.error("proto message type dispatcher, wait context ready error!", e); |
||||
} |
||||
} |
||||
return (MessageHandler<T>) TYPE_HANDLER_MAP.get(type); |
||||
} |
||||
|
||||
} |
||||
@ -1,41 +0,0 @@
|
||||
package net.sopod.soim.core.registry; |
||||
|
||||
import net.sopod.soim.core.service.ReqHandler; |
||||
|
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
/** |
||||
* ServiceRegistry |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-28 14:30 |
||||
*/ |
||||
public class ServiceRegistry { |
||||
|
||||
private static final ConcurrentHashMap<Integer, Class<?>> serviceIdParamTypeMap; |
||||
|
||||
private static final ConcurrentHashMap<Class<?>, Integer> paramTypeServiceIdMap; |
||||
|
||||
private static final ConcurrentHashMap<Integer, ReqHandler<?>> serviceIdHandlers; |
||||
|
||||
static { |
||||
serviceIdParamTypeMap = new ConcurrentHashMap<>(); |
||||
paramTypeServiceIdMap = new ConcurrentHashMap<>(); |
||||
serviceIdHandlers = new ConcurrentHashMap<>(); |
||||
} |
||||
|
||||
private static final AtomicInteger serviceIdGen = new AtomicInteger(10000); |
||||
|
||||
private static <T> void registry(Class<T> paramType, ReqHandler<T> handler) { |
||||
int serviceId = serviceIdGen.getAndIncrement(); |
||||
serviceIdParamTypeMap.put(serviceId, paramType); |
||||
paramTypeServiceIdMap.put(paramType, serviceId); |
||||
serviceIdHandlers.put(serviceId, handler); |
||||
} |
||||
|
||||
public void aaa() { |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,13 +0,0 @@
|
||||
package net.sopod.soim.core.service; |
||||
|
||||
/** |
||||
* ReqHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-28 14:33 |
||||
*/ |
||||
public interface ReqHandler<T> { |
||||
|
||||
Object handle(T param); |
||||
|
||||
} |
||||
@ -0,0 +1,23 @@
|
||||
package net.sopod.soim.core.session; |
||||
|
||||
import io.netty.channel.Channel; |
||||
import io.netty.util.AttributeKey; |
||||
|
||||
public class Account extends NetUser { |
||||
|
||||
public static final AttributeKey<Account> ACCOUNT_KEY = AttributeKey.valueOf(Account.class, "ACCOUNT"); |
||||
|
||||
private long accountId; |
||||
|
||||
private String name; |
||||
|
||||
public Account(Channel channel) { |
||||
super(channel); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAccount() { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,50 @@
|
||||
package net.sopod.soim.core.session; |
||||
|
||||
import io.netty.channel.Channel; |
||||
import io.netty.util.AttributeKey; |
||||
|
||||
import java.lang.ref.WeakReference; |
||||
|
||||
public class NetUser { |
||||
|
||||
/** channel 绑定 netUser 对象 */ |
||||
public static final AttributeKey<NetUser> NET_USER_KEY = AttributeKey.valueOf(NetUser.class, "NET_USER"); |
||||
|
||||
private final WeakReference<Channel> channel; |
||||
|
||||
public NetUser(Channel channel) { |
||||
this.channel = new WeakReference<>(channel); |
||||
} |
||||
|
||||
public boolean isAccount() { |
||||
return false; |
||||
} |
||||
|
||||
public void write(Object message) { |
||||
write(message, false); |
||||
} |
||||
|
||||
public void writeNow(Object message) { |
||||
write(message, true); |
||||
} |
||||
|
||||
private void write(Object message, boolean now) { |
||||
Channel channel = this.channel.get(); |
||||
if (channel == null) { |
||||
return; |
||||
} |
||||
if (now) { |
||||
channel.writeAndFlush(message); |
||||
} else { |
||||
channel.write(message); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "NetUser{" + |
||||
"channel=" + channel + |
||||
'}'; |
||||
} |
||||
|
||||
} |
||||
@ -1,12 +0,0 @@
|
||||
package net.sopod.soim.data.proto; |
||||
|
||||
/** |
||||
* MessageHolder |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-08 18:02 |
||||
*/ |
||||
public class ProtoMessageHolder { |
||||
public static final String protoSerialNoTableName = "protoSerialNoTable.txt"; |
||||
|
||||
} |
||||
@ -0,0 +1,100 @@
|
||||
package net.sopod.soim.data.proto; |
||||
|
||||
import com.google.protobuf.GeneratedMessageV3; |
||||
import com.google.protobuf.MessageLite; |
||||
import net.sopod.soim.data.msg.hello.HelloPB; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import javax.annotation.Nullable; |
||||
import java.io.BufferedReader; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.lang.reflect.Method; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
/** |
||||
* MessageHolder |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-08 18:02 |
||||
*/ |
||||
public class ProtoMessageManager { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProtoMessageManager.class); |
||||
|
||||
public static final String protoSerialNoTableName = "protoSerialNoTable.txt"; |
||||
|
||||
private static final Map<Integer, String> serialNoTypeMap = new HashMap<>(32); |
||||
private static final Map<String, Integer> typeSerialNoMap = new HashMap<>(32); |
||||
private static Map<String, MessageLite> typeNameClazzMap = new ConcurrentHashMap<>(); |
||||
|
||||
static { |
||||
try { |
||||
init(); |
||||
} catch (IOException e) { |
||||
throw new IllegalStateException("protobuf序列号列表初始化失败", e); |
||||
} |
||||
} |
||||
|
||||
private static void init() throws IOException { |
||||
InputStream in = ProtoMessageManager.class.getClassLoader().getResourceAsStream(protoSerialNoTableName); |
||||
if (in == null) { |
||||
throw new IllegalStateException("classpath:" + protoSerialNoTableName + " 文件未找到"); |
||||
} |
||||
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); |
||||
BufferedReader bufReader = new BufferedReader(reader); |
||||
String line; |
||||
while (null != (line = bufReader.readLine())) { |
||||
int idx = line.indexOf('='); |
||||
String clazz = line.substring(0, idx); |
||||
Integer num = Integer.valueOf(line.substring(idx + 1)); |
||||
serialNoTypeMap.put(num, clazz); |
||||
typeSerialNoMap.put(clazz, num); |
||||
} |
||||
bufReader.close(); |
||||
reader.close(); |
||||
in.close(); |
||||
} |
||||
|
||||
@Nullable |
||||
public static MessageLite getProtoInstance(Integer serialNo) { |
||||
String clazz = serialNoTypeMap.get(serialNo); |
||||
if (clazz == null) { |
||||
logger.error("protoMsgDict serialNo proto class not found: {}", serialNo); |
||||
return null; |
||||
} |
||||
return typeNameClazzMap.computeIfAbsent(clazz, c -> { |
||||
try { |
||||
Class<?> type = Class.forName(c); |
||||
if (!MessageLite.class.isAssignableFrom(type)) { |
||||
return null; |
||||
} |
||||
return getDefaultInstance((Class<? extends MessageLite>) type); |
||||
} catch (ClassNotFoundException e) { |
||||
logger.error("protoMsgDict proto class not found: {}, {}", serialNo, c); |
||||
return null; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Nullable |
||||
public static Integer getSerialNo(Class<? extends MessageLite> type) { |
||||
return typeSerialNoMap.get(type.getName()); |
||||
} |
||||
|
||||
private static MessageLite getDefaultInstance(Class<? extends MessageLite> clazz) { |
||||
try { |
||||
Method getDefaultInstance = clazz.getDeclaredMethod("getDefaultInstance"); |
||||
return (MessageLite)getDefaultInstance.invoke(null); |
||||
} catch (Exception e) { |
||||
System.out.println("get instance exception:" + clazz.getName()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -1,2 +1,2 @@
|
||||
net.sopod.soim.data.msg.hello.HelloPB$World=10000 |
||||
net.sopod.soim.data.msg.hello.HelloPB$Hello=10001 |
||||
net.sopod.soim.data.msg.hello.HelloPB$Hello=10000 |
||||
net.sopod.soim.data.msg.hello.HelloPB$World=10001 |
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package net.sopod.soim.entry.config; |
||||
|
||||
import net.sopod.soim.core.registry.ProtoMessageHandlerRegistry; |
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* ApplicationContextInitialed |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 22:20 |
||||
*/ |
||||
@Configuration |
||||
public class ApplicationContextInitialed implements ApplicationContextAware { |
||||
|
||||
@Override |
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||
// 注册 protobuf 消息 handler
|
||||
ProtoMessageHandlerRegistry.registerHandlerWithApplicationContext(applicationContext); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,26 @@
|
||||
package net.sopod.soim.entry.handler; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import net.sopod.soim.core.handler.NetUserMessageHandler; |
||||
import net.sopod.soim.core.session.NetUser; |
||||
import net.sopod.soim.data.msg.hello.HelloPB; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* HelloHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 19:19 |
||||
*/ |
||||
@Service |
||||
public class HelloHandler extends NetUserMessageHandler<HelloPB.Hello> { |
||||
|
||||
@Override |
||||
public MessageLite handle(NetUser netUser, HelloPB.Hello msg) { |
||||
System.out.println("get hello message"); |
||||
System.out.println(msg); |
||||
System.out.println(msg.getStr()); |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -1,16 +0,0 @@
|
||||
package net.sopod.soim.entry.model; |
||||
|
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* A |
||||
* |
||||
* @author tmy |
||||
* @date 2022-03-27 17:01 |
||||
*/ |
||||
@Data |
||||
public class A { |
||||
|
||||
private String name; |
||||
|
||||
} |
||||
@ -0,0 +1,53 @@
|
||||
package net.sopod.soim.entry.server; |
||||
|
||||
import com.google.protobuf.MessageLite; |
||||
import io.netty.channel.Channel; |
||||
import io.netty.channel.ChannelHandlerContext; |
||||
import io.netty.channel.SimpleChannelInboundHandler; |
||||
import io.netty.util.Attribute; |
||||
import net.sopod.soim.core.handler.MessageHandler; |
||||
import net.sopod.soim.core.net.AttributeKeys; |
||||
import net.sopod.soim.core.registry.ProtoMessageHandlerRegistry; |
||||
import net.sopod.soim.core.session.NetUser; |
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
/** |
||||
* MessageLiteHandler |
||||
* |
||||
* @author tmy |
||||
* @date 2022-04-10 22:40 |
||||
*/ |
||||
public class InboundImMessageHandler extends SimpleChannelInboundHandler<MessageLite> { |
||||
|
||||
/** |
||||
* channel 建立连接,设置初始属性 |
||||
* TODO 登录倒计时 5s 断开连接, 登录失败次数 |
||||
*/ |
||||
@Override |
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
||||
Channel channel = ctx.channel(); |
||||
channel.attr(NetUser.NET_USER_KEY).set(new NetUser(channel)); |
||||
channel.attr(AttributeKeys.WRITE_FAIL_TIMES).set(new AtomicInteger()); |
||||
channel.attr(AttributeKeys.LOGIN_FAIL_TIMES).set(new AtomicInteger()); |
||||
|
||||
ctx.fireChannelActive(); |
||||
} |
||||
|
||||
@Override |
||||
public void channelInactive(ChannelHandlerContext ctx) { |
||||
|
||||
ctx.fireChannelInactive(); |
||||
} |
||||
|
||||
@Override |
||||
protected void channelRead0(ChannelHandlerContext ctx, MessageLite messageLite) throws Exception { |
||||
Attribute<NetUser> netUserAttr = ctx.channel().attr(NetUser.NET_USER_KEY); |
||||
NetUser netUser = netUserAttr.get(); |
||||
// TODO dispatcher
|
||||
MessageHandler<MessageLite> typeHandler = (MessageHandler<MessageLite>) ProtoMessageHandlerRegistry |
||||
.getTypeHandler(messageLite.getClass()); |
||||
typeHandler.exec(netUser, messageLite); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,28 @@
|
||||
package net.sopod.soim.entry.util; |
||||
|
||||
import io.netty.util.concurrent.FastThreadLocalThread; |
||||
|
||||
import java.util.concurrent.ThreadFactory; |
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
public class FastThreadLocalThreadFactory implements ThreadFactory { |
||||
private String name; |
||||
private int priority; |
||||
private AtomicInteger counter; |
||||
|
||||
public FastThreadLocalThreadFactory(String name, int priority) { |
||||
this.name = name; |
||||
this.priority = priority; |
||||
this.counter = new AtomicInteger(); |
||||
} |
||||
|
||||
@Override |
||||
public Thread newThread(Runnable runnable) { |
||||
FastThreadLocalThread thread = new FastThreadLocalThread( |
||||
runnable, |
||||
String.format(name, counter.incrementAndGet()) |
||||
); |
||||
thread.setPriority(priority); |
||||
return thread; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue