3.0版本大重构中

This commit is contained in:
wangchao 2020-01-12 19:51:24 +08:00
parent 9da70fa0f8
commit cf6437fe4f
136 changed files with 3463 additions and 3107 deletions

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.j-im</groupId>
<artifactId>jim-parent</artifactId>
<version>2.6.0.v20190114-RELEASE</version>
<version>3.0.0.v20200101-RELEASE</version>
<relativePath>../jim-parent/pom.xml</relativePath>
</parent>
<dependencies>

View File

@ -3,11 +3,10 @@ package org.jim.client;
import java.nio.ByteBuffer;
import org.jim.common.ImConst;
import org.jim.common.Protocol;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.exception.ImDecodeException;
import org.tio.core.intf.AioHandler;
import org.tio.core.intf.Packet;
import org.jim.common.packets.Command;
@ -52,7 +51,7 @@ public class HelloClientAioHandler implements AioHandler,ClientAioHandler
}
@Override
public TcpPacket decode(ByteBuffer buffer,int limit, int position, int readableLength,ChannelContext channelContext) throws AioDecodeException {
public TcpPacket decode(ByteBuffer buffer,int limit, int position, int readableLength,ChannelContext channelContext) throws ImDecodeException {
TcpPacket tcpPacket = TcpServerDecoder.decode(buffer, channelContext);
return tcpPacket;
}

View File

@ -1,7 +1,7 @@
package org.jim.client;
import org.jim.common.ImConst;
import org.jim.common.ImAio;
import org.jim.common.Jim;
import org.jim.common.packets.ChatBody;
import org.jim.common.packets.Command;
import org.jim.common.packets.LoginReqBody;
@ -63,7 +63,7 @@ public class HelloClientStarter {
byte[] loginBody = new LoginReqBody("hello_client","123").toByte();
TcpPacket loginPacket = new TcpPacket(Command.COMMAND_LOGIN_REQ,loginBody);
//先登录;
ImAio.send(clientChannelContext, loginPacket);
Jim.send(clientChannelContext, loginPacket);
ChatBody chatBody = ChatBody.newBuilder()
.setFrom("hello_client")
.setTo("admin")
@ -72,6 +72,6 @@ public class HelloClientStarter {
.setGroup_id("100")
.setContent("Socket普通客户端消息测试!").build();
TcpPacket chatPacket = new TcpPacket(Command.COMMAND_CHAT_REQ,chatBody.toByte());
ImAio.send(clientChannelContext, chatPacket);
Jim.send(clientChannelContext, chatPacket);
}
}

View File

@ -0,0 +1,25 @@
package org.jim.client;
import org.jim.common.ImHandler;
import org.jim.common.config.ImConfig;
import org.jim.common.listener.ImListener;
/**
* @ClassName ImClientConfig
* @Description TODO
* @Author WChao
* @Date 2020/1/4 10:42
* @Version 1.0
**/
public class ImClientConfig extends ImConfig {
@Override
public ImHandler getImHandler() {
return null;
}
@Override
public ImListener getImListener() {
return null;
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.j-im</groupId>
<artifactId>jim-parent</artifactId>
<version>2.6.0.v20190114-RELEASE</version>
<version>3.0.0.v20200101-RELEASE</version>
<relativePath>../jim-parent/pom.xml</relativePath>
</parent>
<dependencies>
@ -76,6 +76,18 @@
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -1,478 +0,0 @@
package org.jim.common;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.cluster.ImCluster;
import org.jim.common.config.DefaultImConfigBuilder;
import org.jim.common.listener.ImBindListener;
import org.jim.common.packets.Client;
import org.jim.common.packets.User;
import org.jim.common.utils.ImKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Aio;
import org.tio.core.ChannelContext;
import org.tio.core.ChannelContextFilter;
import org.tio.core.GroupContext;
import org.tio.utils.lock.SetWithLock;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
/**
* 版本: [1.0]
* 功能说明:
* @author : WChao 创建时间: 2017年9月22日 上午9:07:18
*/
public class ImAio {
public static ImConfig imConfig = null;
private static Logger log = LoggerFactory.getLogger(ImAio.class);
/**
* 功能描述[根据用户ID获取当前用户]
* @authorWChao 创建时间: 2017年9月18日 下午4:34:39
* @param userId
* @return
*/
public static User getUser(String userId){
SetWithLock<ChannelContext> userChannelContexts = ImAio.getChannelContextsByUserId(userId);
if(Objects.isNull(userChannelContexts)) {
return null;
}
ReadLock readLock = userChannelContexts.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> userChannels = userChannelContexts.getObj();
if(CollectionUtils.isEmpty(userChannels)) {
return null;
}
User user = null;
for(ChannelContext channelContext : userChannels){
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
Client client = imSessionContext.getClient();
user = client.getUser();
if(user != null){
return user;
}
}
return user;
}finally {
readLock.unlock();
}
}
/**
* 功能描述[根据用户ID获取当前用户所在通道集合]
* @authorWChao 创建时间: 2017年9月18日 下午4:34:39
* @param userId
* @return
*/
public static SetWithLock<ChannelContext> getChannelContextsByUserId(String userId){
SetWithLock<ChannelContext> channelContexts = Aio.getChannelContextsByUserid(imConfig.getGroupContext(), userId);
return channelContexts;
}
/**
* 功能描述[获取所有用户(在线+离线)]
* @authorWChao 创建时间: 2017年9月18日 下午4:31:54
* @return
*/
public static List<User> getAllUser(){
List<User> users = new ArrayList<User>();
SetWithLock<ChannelContext> allChannels = Aio.getAllChannelContexts(imConfig.getGroupContext());
if(allChannels == null) {
return users;
}
ReadLock readLock = allChannels.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> userChannels = allChannels.getObj();
if(CollectionUtils.isEmpty(userChannels)) {
return users;
}
for(ChannelContext channelContext : userChannels){
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
Client client = imSessionContext.getClient();
if(client != null && client.getUser() != null){
User user = ImKit.copyUserWithoutUsers(client.getUser());
users.add(user);
}
}
}finally {
readLock.unlock();
}
return users;
}
/**
* 功能描述[获取所有在线用户]
* @authorWChao 创建时间: 2017年9月18日 下午4:31:42
* @return
*/
public static List<User> getAllOnlineUser(){
List<User> users = new ArrayList<User>();
SetWithLock<ChannelContext> onlineChannels = Aio.getAllConnectedsChannelContexts(imConfig.getGroupContext());
if(onlineChannels == null) {
return users;
}
ReadLock readLock = onlineChannels.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> userChannels = onlineChannels.getObj();
for(ChannelContext channelContext : userChannels){
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
if(imSessionContext != null){
Client client = imSessionContext.getClient();
if(client != null && client.getUser() != null){
User user = ImKit.copyUserWithoutUsers(client.getUser());
users.add(user);
}
}
}
}finally {
readLock.unlock();
}
return users;
}
/**
* 根据群组获取所有用户;
* @param group
* @return
*/
public static List<User> getAllUserByGroup(String group){
SetWithLock<ChannelContext> withLockChannels = Aio.getChannelContextsByGroup(imConfig.getGroupContext(), group);
if(withLockChannels == null){
return null;
}
ReadLock readLock = withLockChannels.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = withLockChannels.getObj();
if(CollectionUtils.isEmpty(channels)){
return null;
}
List<User> users = new ArrayList<User>();
Map<String,User> userMaps = new HashMap<String,User>();
for(ChannelContext channelContext : channels){
String userId = channelContext.getUserid();
User user = getUser(userId);
if(user != null && userMaps.get(userId) == null){
userMaps.put(userId, user);
users.add(user);
}
}
userMaps = null;
return users;
}finally{
readLock.unlock();
}
}
/**
* 功能描述[发送到群组(所有不同协议端)]
* @authorWChao 创建时间: 2017年9月21日 下午3:26:57
* @param group
* @param packet
*/
public static void sendToGroup(String group, ImPacket packet){
if(packet.getBody() == null) {
return;
}
SetWithLock<ChannelContext> withLockChannels = Aio.getChannelContextsByGroup(imConfig.getGroupContext(), group);
if(withLockChannels == null){
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToGroup(imConfig.getGroupContext(), group, packet);
}
return;
}
ReadLock readLock = withLockChannels.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = withLockChannels.getObj();
if(CollectionUtils.isNotEmpty(channels)){
for(ChannelContext channelContext : channels){
send(channelContext,packet);
}
}
}finally{
readLock.unlock();
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToGroup(imConfig.getGroupContext(), group, packet);
}
}
}
/**
* 发送到指定通道;
* @param channelContext
* @param packet
*/
public static boolean send(ChannelContext channelContext,ImPacket packet){
ImPacket respPacket = initAndSetConvertPacket(channelContext , packet);
if(respPacket == null){
return false;
}
return Aio.send(channelContext,respPacket);
}
/**
* 阻塞发送确认把packet发送到对端后再返回
* @param channelContext
* @param packet
* @return
*/
public static boolean bSend(ChannelContext channelContext , ImPacket packet){
ImPacket respPacket = initAndSetConvertPacket(channelContext , packet);
if(respPacket == null){
return false;
}
return Aio.bSend(channelContext,respPacket);
}
/**
* 发送到指定用户;
* @param userId
* @param packet
*/
public static void sendToUser(String userId,ImPacket packet){
if(StringUtils.isEmpty(userId)) {
return;
}
SetWithLock<ChannelContext> toChannelContexts = getChannelContextsByUserId(userId);
if(toChannelContexts == null || toChannelContexts.size() < 1){
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToUser(imConfig.getGroupContext(), userId, packet);
}
return;
}
ReadLock readLock = toChannelContexts.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = toChannelContexts.getObj();
for(ChannelContext channelContext : channels){
send(channelContext, packet);
}
}finally{
readLock.unlock();
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToUser(imConfig.getGroupContext(), userId, packet);
}
}
}
/**
* 发送到指定ip对应的集合
* @param groupContext
* @param ip
* @param packet
*/
public static void sendToIp(GroupContext groupContext, String ip, ImPacket packet) {
sendToIp(groupContext, ip, packet, null);
}
public static void sendToIp(GroupContext groupContext, String ip, ImPacket packet, ChannelContextFilter channelContextFilter) {
sendToIp(groupContext, ip, packet, channelContextFilter, false);
}
private static Boolean sendToIp(GroupContext groupContext, String ip, ImPacket packet, ChannelContextFilter channelContextFilter, boolean isBlock) {
try{
SetWithLock<ChannelContext> setWithLock = groupContext.ips.clients(groupContext, ip);
if (setWithLock == null) {
log.info("{}, 没有ip为[{}]的对端", groupContext.getName(), ip);
return false;
} else {
Boolean ret = sendToSet(groupContext, setWithLock, packet, channelContextFilter, isBlock);
return ret;
}
}finally{
ImCluster cluster = imConfig.getCluster();
if (cluster != null && !packet.isFromCluster()) {
cluster.clusterToIp(groupContext, ip, packet);
}
}
}
public static Boolean sendToSet(GroupContext groupContext, SetWithLock<ChannelContext> setWithLock, ImPacket packet, ChannelContextFilter channelContextFilter, boolean isBlock){
Lock lock = setWithLock.getLock().readLock();
lock.lock();
try {
Set<ChannelContext> sets = (Set<ChannelContext>) setWithLock.getObj();
for (ChannelContext channelContext : sets) {
SetWithLock<ChannelContext> convertSet = new SetWithLock<ChannelContext>(new HashSet<ChannelContext>());
convertSet.add(channelContext);
ImPacket resPacket = ImKit.ConvertRespPacket(packet, packet.getCommand(), channelContext);
Aio.sendToSet(groupContext, convertSet, resPacket, channelContextFilter);
}
}finally {
lock.unlock();
}
return true;
}
/**
* 转换协议包同时设置Packet包信息;
* @param channelContext
* @param packet
* @return
*/
private static ImPacket initAndSetConvertPacket(ChannelContext channelContext , ImPacket packet){
if(channelContext == null) {
return null;
}
ImPacket respPacket = ImKit.ConvertRespPacket(packet,packet.getCommand(),channelContext);
if(respPacket == null){
log.error("转换协议包为空,请检查协议!");
return null;
}
respPacket.setSynSeq(packet.getSynSeq());
if(imConfig == null){
imConfig = new DefaultImConfigBuilder().setGroupContext(channelContext.getGroupContext()).build();
}
return respPacket;
}
/**
* 绑定用户;
* @param channelContext
* @param userId
*/
public static void bindUser(ChannelContext channelContext,String userId){
bindUser(channelContext, userId,null);
}
/**
* 绑定用户,同时可传递监听器执行回调函数
* @param channelContext
* @param userId
* @param bindListener(绑定监听器回调)
*/
public static void bindUser(ChannelContext channelContext,String userId,ImBindListener bindListener){
Aio.bindUser(channelContext, userId);
if(bindListener != null){
try {
bindListener.onAfterUserBind(channelContext, userId);
} catch (Exception e) {
log.error(e.toString(),e);
}
}
}
/**
* 解绑用户
* @param userId
*/
public static void unbindUser(String userId){
unbindUser(userId, null);
}
/**
* 解除绑定用户,同时可传递监听器执行回调函数
* @param userId
* @param bindListener(解绑定监听器回调)
*/
public static void unbindUser(String userId,ImBindListener bindListener){
Aio.unbindUser(imConfig.getGroupContext(), userId);
if(bindListener != null){
try {
SetWithLock<ChannelContext> userChannelContexts = ImAio.getChannelContextsByUserId(userId);
if(userChannelContexts == null || userChannelContexts.size() == 0) {
return;
}
ReadLock readLock = userChannelContexts.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = userChannelContexts.getObj();
for(ChannelContext channelContext : channels){
bindListener.onAfterUserBind(channelContext, userId);
}
}finally{
readLock.unlock();
}
} catch (Exception e) {
log.error(e.toString(),e);
}
}
}
/**
* 绑定群组;
* @param channelContext
* @param group
*/
public static void bindGroup(ChannelContext channelContext,String group){
bindGroup(channelContext, group,null);
}
/**
* 绑定群组,同时可传递监听器执行回调函数
* @param channelContext
* @param group
* @param bindListener(绑定监听器回调)
*/
public static void bindGroup(ChannelContext channelContext,String group,ImBindListener bindListener){
Aio.bindGroup(channelContext, group);
if(bindListener != null){
try {
bindListener.onAfterGroupBind(channelContext, group);
} catch (Exception e) {
log.error(e.toString(),e);
}
}
}
/**
* 与指定群组解除绑定
* @param userId
* @param group
*/
public static void unbindGroup(String userId,String group){
unbindGroup(userId, group, null);
}
/**
* 与指定群组解除绑定,同时可传递监听器执行回调函数
* @param userId
* @param group
* @param bindListener(解绑定监听器回调)
*/
public static void unbindGroup(String userId,String group,ImBindListener bindListener){
SetWithLock<ChannelContext> userChannelContexts = ImAio.getChannelContextsByUserId(userId);
if(userChannelContexts == null || userChannelContexts.size() == 0) {
return;
}
ReadLock readLock = userChannelContexts.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = userChannelContexts.getObj();
for(ChannelContext channelContext : channels){
Aio.unbindGroup(group, channelContext);
if(bindListener != null){
try {
bindListener.onAfterGroupUnbind(channelContext, group);
} catch (Exception e) {
log.error(e.toString(),e);
}
}
}
}finally{
readLock.unlock();
}
}
/**
* 移除用户, 和close方法一样只不过不再进行重连等维护性的操作
* @param userId
* @param remark
*/
public static void remove(String userId,String remark){
SetWithLock<ChannelContext> userChannelContexts = getChannelContextsByUserId(userId);
if(userChannelContexts != null && userChannelContexts.size() > 0){
ReadLock readLock = userChannelContexts.getLock().readLock();
readLock.lock();
try{
Set<ChannelContext> channels = userChannelContexts.getObj();
for(ChannelContext channelContext : channels){
remove(channelContext, remark);
}
}finally{
readLock.unlock();
}
}
}
/**
* 移除指定channel, 和close方法一样只不过不再进行重连等维护性的操作
* @param channelContext
* @param remark
*/
public static void remove(ChannelContext channelContext,String remark){
Aio.remove(channelContext, remark);
}
}

View File

@ -0,0 +1,68 @@
package org.jim.common;
import org.jim.common.config.ImConfig;
import org.jim.common.protocol.IProtocol;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
import org.tio.utils.prop.MapWithLockPropSupport;
import org.tio.utils.thread.pool.AbstractQueueRunnable;
/**
* @ClassName ImChannelContext
* @Description TODO
* @Author WChao
* @Date 2020/1/5 23:40
* @Version 1.0
**/
public abstract class ImChannelContext extends MapWithLockPropSupport implements ImConst{
protected ImConfig imConfig;
protected ChannelContext tioChannelContext;
public ImChannelContext(ImConfig imConfig, ChannelContext tioChannelContext) {
this.imConfig = imConfig;
this.tioChannelContext = tioChannelContext;
}
public ImConfig getImConfig() {
return imConfig;
}
public void setImConfig(ImConfig imConfig) {
this.imConfig = imConfig;
}
public void setSessionContext(ImSessionContext imSessionContext){
this.setAttribute(Key.IM_CHANNEL_SESSION_CONTEXT_KEY, imSessionContext);
}
public ImSessionContext getSessionContext(){
return (ImSessionContext)this.getAttribute(Key.IM_CHANNEL_SESSION_CONTEXT_KEY);
}
public String getId() {
return tioChannelContext.getId();
}
public Node getClientNode(){
return tioChannelContext.getClientNode();
}
public void setPacketNeededLength(Integer packetNeededLength) {
this.tioChannelContext.setPacketNeededLength(packetNeededLength);
}
public String getUserId(){
return tioChannelContext.userid;
}
public void setUserId(String userId){
tioChannelContext.setUserid(userId);
}
public ChannelContext getTioChannelContext() {
return tioChannelContext;
}
}

View File

@ -1,32 +0,0 @@
/**
*
*/
package org.jim.common;
import java.util.concurrent.ThreadPoolExecutor;
import org.tio.client.ClientGroupContext;
import org.tio.client.ReconnConf;
import org.tio.client.intf.ClientAioHandler;
import org.tio.client.intf.ClientAioListener;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
/**
* @author WChao
*
*/
public class ImClientGroupContext extends ClientGroupContext{
public ImClientGroupContext(ClientAioHandler aioHandler,ClientAioListener aioListener) {
super(aioHandler, aioListener);
}
public ImClientGroupContext(ClientAioHandler aioHandler,ClientAioListener aioListener, ReconnConf reconnConf,SynThreadPoolExecutor tioExecutor, ThreadPoolExecutor groupExecutor) {
super(aioHandler, aioListener, reconnConf, tioExecutor, groupExecutor);
}
public ImClientGroupContext(ClientAioHandler aioHandler,ClientAioListener aioListener, ReconnConf reconnConf) {
super(aioHandler, aioListener, reconnConf);
}
}

View File

@ -1,52 +0,0 @@
/**
*
*/
package org.jim.common;
import org.jim.common.config.Config;
import org.jim.common.http.HttpConfig;
import org.jim.common.ws.WsServerConfig;
/**
* @author WChao
*
*/
public class ImConfig extends Config{
/**
* http相关配置;
*/
private HttpConfig httpConfig;
/**
* WebSocket相关配置;
*/
private WsServerConfig wsServerConfig;
public ImConfig() {
this.httpConfig = new HttpConfig();
this.wsServerConfig = new WsServerConfig();
}
public ImConfig(String bindIp,Integer bindPort){
this.bindIp = bindIp;
this.bindPort = bindPort;
this.httpConfig = new HttpConfig();
this.wsServerConfig = new WsServerConfig();
}
public HttpConfig getHttpConfig() {
return httpConfig;
}
public void setHttpConfig(HttpConfig httpConfig) {
this.httpConfig = httpConfig;
}
public WsServerConfig getWsServerConfig() {
return wsServerConfig;
}
public void setWsServerConfig(WsServerConfig wsServerConfig) {
this.wsServerConfig = wsServerConfig;
}
}

View File

@ -7,52 +7,296 @@ package org.jim.common;
*/
public interface ImConst
{
public static final String AUTH_KEY = "authKey";
public static final int SERVER_PORT = 8888;
interface Key{
String IM_CHANNEL_CONTEXT_KEY = "im_channel_context_key";
String IM_CHANNEL_SESSION_CONTEXT_KEY = "im_channel_session_context_key";
/**
* 存放HttpConfig
*/
String HTTP_SERVER_CONFIG = "JIM_HTTP_SERVER_CONFIG";
}
public static final String CHARSET = "utf-8";
interface Topic{
String REDIS_CLUSTER_TOPIC_SUFFIX = "REDIS_";
String JIM_CLUSTER_TOPIC = "JIM_CLUSTER";
}
public static final String TO = "to";
interface Protocol{
/**
* 心跳字节
*/
byte HEARTBEAT_BYTE = -128;
public static final String CHANNEL = "channel";
/**
* 握手字节
*/
byte HANDSHAKE_BYTE = -127;
public static final String PACKET = "packet";
/**
* 协议版本号
*/
byte VERSION = 0x01;
public static final String STATUS = "status";
String WEB_SOCKET = "ws";
public static final String HTTP_REQUEST = "httpRequest";
String HTTP = "http";
public static final String CHAT_QUEUE = "chat_queue";
String TCP = "tcp";
public static final String STORE = "store";
String COOKIE_NAME_FOR_SESSION = "jim-s";
/**
* 消息体最多为多少,只支持多少M数据
*/
int MAX_LENGTH_OF_BODY = (int) (1024 * 1024 * 2.1);
public static final String PUSH = "push";
/**
* 消息头最少为多少个字节,1+1+2+(2+4)
*/
int LEAST_HEADER_LENGTH = 4;
public static final String CHAT = "chat";
/**
* 加密标识位mask1为加密否则不加密
*/
byte FIRST_BYTE_MASK_ENCRYPT = -128;
public static final String GROUP = "group";
/**
* 压缩标识位mask1为压缩否则不压缩
*/
byte FIRST_BYTE_MASK_COMPRESS = 0B01000000;
public static final String USER = "user";
/**
* 是否有同步序列号标识位mask如果有同步序列号则消息头会带有同步序列号否则不带
*/
byte FIRST_BYTE_MASK_HAS_SYNSEQ = 0B00100000;
public static final String TERMINAL = "terminal";
/**
* 是否是用4字节来表示消息体的长度
*/
byte FIRST_BYTE_MASK_4_BYTE_LENGTH = 0B00010000;
public static final String INFO = "info";
/**
* 版本号mask
*/
byte FIRST_BYTE_MASK_VERSION = 0B00001111;
}
public static final String FRIENDS = "friends";
interface Http{
/**
* 请求体的格式
* @author wchao
* 2017年6月28日 上午10:03:12
*/
enum RequestBodyFormat {
URLENCODED, MULTIPART, TEXT
}
public static final String ONLINE = "online";
/**
*Accept-Language : zh-CN,zh;q=0.8
Sec-WebSocket-Version : 13
Sec-WebSocket-Extensions : permessage-deflate; client_max_window_bits
Upgrade : websocket
Host : t-io.org:9321
Accept-Encoding : gzip, deflate, sdch
User-Agent : Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Origin : http://www.t-io.org:9292
Sec-WebSocket-Key : kmCL2C7q9vtNSMyHpft7lw==
Connection : Upgrade
Cache-Control : no-cache
Pragma : no-cache
* @author wchao
* 2017年5月27日 下午2:11:57
*/
interface RequestHeaderKey {
String Cookie = "Cookie".toLowerCase();//Cookie: $Version=1; Skin=new;
String Origin = "Origin".toLowerCase(); //http://127.0.0.1
String Sec_WebSocket_Key = "Sec-WebSocket-Key".toLowerCase(); //2GFwqJ1Z37glm62YKKLUeA==
String Cache_Control = "Cache-Control".toLowerCase(); //no-cache
String Connection = "Connection".toLowerCase(); //Upgrade, keep-alive
String User_Agent = "User-Agent".toLowerCase(); //Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3088.3 Safari/537.36
String Sec_WebSocket_Version = "Sec-WebSocket-Version".toLowerCase(); //13
String Host = "Host".toLowerCase(); //127.0.0.1:9321
String Pragma = "Pragma".toLowerCase(); //no-cache
String Accept_Encoding = "Accept-Encoding".toLowerCase(); //gzip, deflate, br
String Accept_Language = "Accept-Language".toLowerCase(); //zh-CN,zh;q=0.8,en;q=0.6
String Upgrade = "Upgrade".toLowerCase(); //websocket
String Sec_WebSocket_Extensions = "Sec-WebSocket-Extensions".toLowerCase(); //permessage-deflate; client_max_window_bits
String Content_Length = "Content-Length".toLowerCase(); //65
String Content_Type = "Content-Type".toLowerCase();// : application/x-www-form-urlencodedapplication/x-www-form-urlencoded; charset=UTF-8multipart/form-data; boundary=----WebKitFormBoundaryuwYcfA2AIgxqIxA0
String If_Modified_Since = "If-Modified-Since".toLowerCase(); //与Last-Modified配合
/**
* 值为XMLHttpRequest则为Ajax
*/
String X_Requested_With = "X-Requested-With".toLowerCase();//XMLHttpRequest
}
public static final String OFFLINE = "offline";
/**
*
* @author wchao
* 2017年6月27日 下午8:23:58
*/
interface RequestHeaderValue {
interface Connection {
String keep_alive = "keep-alive".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
String close = "close".toLowerCase();
}
public static final String ON = "on";
//application/x-www-form-urlencodedmultipart/form-datatext/plain
interface Content_Type {
/**
* 普通文本一般会是json或是xml
*/
String text_plain = "text/plain".toLowerCase();
/**
* 文件上传
*/
String multipart_form_data = "multipart/form-data".toLowerCase();
/**
* 普通的key-value
*/
String application_x_www_form_urlencoded = "application/x-www-form-urlencoded".toLowerCase();
}
}
public static final String OFF = "off";
interface ResponseHeaderKey {
String Set_Cookie = "Set-Cookie".toLowerCase(); //Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
String Content_Length = "Content-Length".toLowerCase(); //65
public static final String JIM = "JIM";
String Connection = "Connection".toLowerCase(); //Upgrade, keep-alive
String Keep_Alive = "Keep-Alive".toLowerCase(); //Keep-Alive:timeout=20
String Sec_WebSocket_Accept = "Sec-WebSocket-Accept".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
public static final String CONVERTER = "converter";
/**
* Content-Disposition: attachment;filename=FileName.txt
* 文件下载
*/
String Content_disposition = "Content-disposition".toLowerCase();
/**
* 文档的编码Encode方法只有在解码之后才可以得到Content-Type头指定的内容类型
* 利用gzip压缩文档能够显著地减少HTML文档的下载时间
* Java的GZIPOutputStream可以很方便地进行gzip压缩但只有Unix上的Netscape和Windows上的IE 4IE 5才支持它
* 因此Servlet应该通过查看Accept-Encoding头即request.getHeader("Accept-Encoding")检查浏览器是否支持gzip
* 为支持gzip的浏览器返回经gzip压缩的HTML页面为其他浏览器返回普通页面
*/
String Content_Encoding = "Content-Encoding".toLowerCase();
/**
* 表示后面的文档属于什么MIME类型Servlet默认为text/plain但通常需要显式地指定为text/html
* 由于经常要设置Content-Type因此HttpServletResponse提供了一个专用的方法setContentType
*/
String Content_Type = "Content-Type".toLowerCase();
/**
* 当前的GMT时间你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦
*/
String Date = "Date".toLowerCase();
/**
* 应该在什么时候认为文档已经过期从而不再缓存它
*/
String Expires = "Expires".toLowerCase();
/**
* 文档的最后改动时间客户可以通过If-Modified-Since请求头提供一个日期该请求将被视为一个条件GET
* 只有改动时间迟于指定时间的文档才会返回否则返回一个304Not Modified状态Last-Modified也可用setDateHeader方法来设置
*/
String Last_Modified = "Last-Modified".toLowerCase();
/**
* 表示客户应当到哪里去提取文档Location通常不是直接设置的而是通过HttpServletResponse的sendRedirect方法该方法同时设置状态代码为302
*/
String Location = "Location".toLowerCase();
/**
* 表示浏览器应该在多少时间之后刷新文档以秒计除了刷新当前文档之外你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面
注意这种功能通常是通过设置HTML页面HEAD区的META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path"实现这是因为自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要但是对于Servlet来说直接设置Refresh头更加方便
public static final String BASE_ASYNC_CHAT_MESSAGE_PROCESSOR = "base_async_chat_message_processor";
注意Refresh的意义是"N秒之后刷新本页面或访问指定页面"而不是"每隔N秒刷新本页面或访问指定页面"因此连续刷新要求每次都发送一个Refresh头而发送204状态代码则可以阻止浏览器继续刷新不管是使用Refresh头还是META HTTP-EQUIV="Refresh" ...
注意Refresh头不属于HTTP 1.1正式规范的一部分而是一个扩展但Netscape和IE都支持它
*/
String Refresh = "Refresh".toLowerCase();
/**
* 服务器名字Servlet一般不设置这个值而是由Web服务器自己设置
*/
String Server = "Server".toLowerCase();
/**
*
*/
String Access_Control_Allow_Origin = "Access-Control-Allow-Origin".toLowerCase(); //value: *
/**
*
*/
String Access_Control_Allow_Headers = "Access-Control-Allow-Headers".toLowerCase(); //value: x-requested-with,content-type
/**
* 是否是从缓存中获取的数据tio-httpserver特有的头部信息
*/
String tio_from_cache = "tio-from-cache";
}
/**
*
* @author wchao
* 2017年6月27日 下午8:24:02
*/
interface ResponseHeaderValue {
interface Connection {
String keep_alive = "keep-alive".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
String close = "close".toLowerCase();
}
}
/**
*
*/
String SERVER_INFO = "jim-http-server/0.0.1";
/**
* 默认规定连接到本服务器的客户端统一用utf-8
*/
String CHARSET_NAME = "utf-8";
}
String AUTH_KEY = "authKey";
int SERVER_PORT = 8888;
String CHARSET = "utf-8";
String TO = "to";
String CHANNEL = "channel";
String PACKET = "packet";
String STATUS = "status";
String HTTP_REQUEST = "httpRequest";
String STORE = "store";
String PUSH = "push";
String CHAT = "chat";
String GROUP = "group";
String USER = "user";
String TERMINAL = "terminal";
String INFO = "info";
String FRIENDS = "friends";
String ONLINE = "online";
String OFFLINE = "offline";
String JIM = "JIM";
String CONVERTER = "converter";
String BASE_ASYNC_CHAT_MESSAGE_PROCESSOR = "base_async_chat_message_processor";
}

View File

@ -5,10 +5,8 @@ package org.jim.common;
import java.nio.ByteBuffer;
import org.jim.common.exception.ImDecodeException;
import org.tio.core.ChannelContext;
import org.tio.core.exception.AioDecodeException;
/**
* 版本: [1.0]
* 功能说明:
@ -16,5 +14,5 @@ import org.tio.core.exception.AioDecodeException;
*/
public interface ImDecoder {
public ImPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException;
public ImPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws ImDecodeException;
}

View File

@ -0,0 +1,47 @@
package org.jim.common;
import org.jim.common.config.ImConfig;
import org.jim.common.exception.ImDecodeException;
import org.jim.common.exception.ImException;
import java.nio.ByteBuffer;
/**
* @ClassName ImHandler
* @Description TODO
* @Author WChao
* @Date 2020/1/6 2:09
* @Version 1.0
**/
public interface ImHandler {
/**
* 根据ByteBuffer解码成业务需要的Packet对象.
* 如果收到的数据不全导致解码失败请返回null在下次消息来时框架层会自动续上前面的收到的数据
* @param buffer 参与本次希望解码的ByteBuffer
* @param limit ByteBuffer的limit
* @param position ByteBuffer的position不一定是0哦
* @param readableLength ByteBuffer参与本次解码的有效数据= limit - position
* @param imChannelContext
* @return
* @throws ImDecodeException
*/
ImPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ImChannelContext imChannelContext) throws ImDecodeException;
/**
* 编码
* @param imPacket
* @param imConfig
* @param imChannelContext
* @return
* @author: WChao
*/
ByteBuffer encode(ImPacket imPacket, ImConfig imConfig, ImChannelContext imChannelContext);
/**
* 处理消息包
* @param imPacket
* @param imChannelContext
* @throws ImException
* @author: WChao
*/
void handler(ImPacket imPacket, ImChannelContext imChannelContext) throws ImException;
}

View File

@ -1,5 +1,6 @@
package org.jim.common;
import org.tio.core.ChannelContext;
import org.tio.core.intf.Packet;
import org.jim.common.packets.Command;
@ -8,7 +9,7 @@ import org.jim.common.packets.Command;
* @author WChao
*
*/
public class ImPacket extends Packet
public class ImPacket extends Packet implements ImConst
{
private static final long serialVersionUID = 2000118564569232098L;
/**
@ -24,6 +25,8 @@ public class ImPacket extends Packet
*/
private Command command;
protected ImChannelContext imChannelContext;
public ImPacket(){}
public ImPacket(byte[] body){
@ -109,7 +112,7 @@ public class ImPacket extends Packet
*/
public int calcHeaderLength(boolean is4byteLength)
{
int ret = Protocol.LEAST_HEADER_LENGHT;
int ret = Protocol.LEAST_HEADER_LENGTH;
if (is4byteLength)
{
ret += 2;
@ -165,4 +168,11 @@ public class ImPacket extends Packet
this.status = status;
}
public ImChannelContext getImChannelContext() {
return imChannelContext;
}
public void setImChannelContext(ImChannelContext imChannelContext) {
this.imChannelContext = imChannelContext;
}
}

View File

@ -2,15 +2,13 @@ package org.jim.common;
import org.jim.common.packets.Client;
import org.tio.monitor.RateLimiterWrap;
import org.tio.server.intf.ServerAioHandler;
/**
*
* @author wchao
*
*/
public class ImSessionContext extends SessionContext
{
public class ImSessionContext {
/**
* 消息请求频率控制器
*/
@ -19,21 +17,19 @@ public class ImSessionContext extends SessionContext
protected Client client = null;
protected String token = null;
/**
* 通道所属协议处理器;
*/
private ServerAioHandler protocolHandler;
protected ImChannelContext imChannelContext;
protected String id;
/**
*
*
* @author: wchao
* @author: WChao
* 2017年2月21日 上午10:27:54
*
*/
public ImSessionContext()
{
public ImSessionContext(){}
public ImSessionContext(ImChannelContext imChannelContext){
this.imChannelContext = imChannelContext;
}
/**
* @return the client
@ -81,12 +77,19 @@ public class ImSessionContext extends SessionContext
this.requestRateLimiter = requestRateLimiter;
}
public ServerAioHandler getProtocolHandler() {
return protocolHandler;
public ImChannelContext getImChannelContext() {
return imChannelContext;
}
public ImSessionContext setProtocolHandler(ServerAioHandler protocolHandler) {
this.protocolHandler = protocolHandler;
return this;
public void setImChannelContext(ImChannelContext imChannelContext) {
this.imChannelContext = imChannelContext;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}

View File

@ -39,7 +39,7 @@ public enum ImStatus implements Status{
private String text;
private ImStatus(int status, String description, String text) {
ImStatus(int status, String description, String text) {
this.status = status;
this.description = description;
this.text = text;

View File

@ -0,0 +1,200 @@
package org.jim.common;
import org.jim.common.config.ImConfig;
import org.jim.common.exception.ImException;
import org.jim.common.listener.ImUserListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Tio;
import org.tio.core.ChannelContext;
import org.tio.core.ChannelContextFilter;
import org.tio.core.TioConfig;
import org.tio.utils.lock.SetWithLock;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
/**
* 版本: [1.0]
* 功能说明:
* @author : WChao 创建时间: 2017年9月22日 上午9:07:18
*/
public class Jim {
public static ImConfig imConfig = ImConfig.Global.get();
private static Logger log = LoggerFactory.getLogger(Jim.class);
/**
* 功能描述[发送到群组(所有不同协议端)]
* @authorWChao 创建时间: 2017年9月21日 下午3:26:57
* @param group
* @param packet
*/
public static void sendToGroup(String group, ImPacket packet){
Tio.sendToGroup(imConfig.getTioConfig(), group, packet);
}
/**
* 发送到指定通道;
* @param channelContext
* @param imPacket
*/
public static boolean send(ImChannelContext channelContext, ImPacket imPacket){
if(channelContext == null){
return false;
}
return Tio.send(channelContext.getTioChannelContext(),imPacket);
}
/**
* 阻塞发送确认把packet发送到对端后再返回
* @param channelContext
* @param packet
* @return
*/
public static boolean bSend(ImChannelContext channelContext , ImPacket packet){
if(channelContext == null){
return false;
}
return Tio.bSend(channelContext.getTioChannelContext(), packet);
}
/**
* 发送到指定用户;
* @param userId
* @param packet
*/
public static void sendToUser(String userId,ImPacket packet){
Tio.sendToUser(imConfig.getTioConfig(), userId, packet);
}
/**
* 发送到指定ip对应的集合
* @param ip
* @param packet
*/
public static void sendToIp( String ip, ImPacket packet) {
sendToIp(ip, packet, null);
}
public static void sendToIp(String ip, ImPacket packet, ChannelContextFilter channelContextFilter) {
Tio.sendToIp(imConfig.getTioConfig(), ip, packet, channelContextFilter);
}
public static void sendToSet(SetWithLock<ChannelContext> setWithLock, ImPacket packet, ChannelContextFilter channelContextFilter, boolean isBlock){
Tio.sendToSet(imConfig.getTioConfig(),setWithLock,packet,channelContextFilter);
}
/**
* 绑定用户
* @param imChannelContext
* @param userId
*/
public static void bindUser(ImChannelContext imChannelContext,String userId){
Tio.bindUser(imChannelContext.getTioChannelContext(), userId);
ImUserListener imUserListener = imConfig.getImUserListener();
if(imUserListener != null){
try {
imUserListener.onAfterBind(imChannelContext, userId);
}catch (ImException e){
log.error(e.toString(),e);
}
}
}
/**
* 解除userId的绑定一般用于多地登录踢掉前面登录的场景
* @param userId
* @author: WChao
*/
public static void unbindUser(String userId){
TioConfig tioConfig = imConfig.getTioConfig();
Tio.unbindUser(tioConfig, userId);
ImUserListener imUserListener = imConfig.getImUserListener();
if(imUserListener == null){
return;
}
SetWithLock<ChannelContext> userChannels = Tio.getByUserid(tioConfig, userId);
Set<ChannelContext> channelContexts = userChannels.getObj();
if(!channelContexts.isEmpty()){
ReadLock readLock = userChannels.getLock().readLock();
try{
readLock.lock();
for (ChannelContext channelContext : channelContexts){
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(ImConst.Key.IM_CHANNEL_CONTEXT_KEY);
imUserListener.onAfterUnbind(imChannelContext, userId);
}
} catch (ImException e) {
log.error(e.toString(), e);
}finally {
readLock.unlock();
}
}
}
/**
* 绑定群组
* @param imChannelContext
* @param group
*/
public static void bindGroup(ImChannelContext imChannelContext, String group){
Tio.bindGroup(imChannelContext.getTioChannelContext(), group);
}
/**
* 与指定组解除绑定关系
* @param groupId
* @param imChannelContext
* @author WChao
*/
public static void unbindGroup(String groupId, ImChannelContext imChannelContext){
Tio.unbindGroup(groupId, imChannelContext.getTioChannelContext());
}
/**
* 与所有组解除解绑关系
* @param imChannelContext
* @author WChao
*/
public static void unbindGroup(ImChannelContext imChannelContext){
Tio.unbindGroup(imChannelContext.getTioChannelContext());
}
/**
* 将制定用户从指定群组解除绑定
* @param userId
* @param group
*/
public static void unbindGroup(String userId,String group){
Tio.unbindGroup(imConfig.getTioConfig(),userId,group);
}
/**
* 移除用户, 和close方法一样只不过不再进行重连等维护性的操作
* @param userId
* @param remark
*/
public static void remove(String userId,String remark){
SetWithLock<ChannelContext> userChannelContexts = Tio.getByUserid(imConfig.getTioConfig(), userId);
Set<ChannelContext> channels = userChannelContexts.getObj();
if(channels.isEmpty()){
return;
}
ReadLock readLock = userChannelContexts.getLock().readLock();
try{
readLock.lock();
for(ChannelContext channelContext : channels){
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(ImConst.Key.IM_CHANNEL_CONTEXT_KEY);
remove(imChannelContext, remark);
}
}finally{
readLock.unlock();
}
}
/**
* 移除指定channel, 和close方法一样只不过不再进行重连等维护性的操作
* @param imChannelContext
* @param remark
*/
public static void remove(ImChannelContext imChannelContext, String remark){
Tio.remove(imChannelContext.getTioChannelContext(), remark);
}
/**
* 关闭连接
* @param imChannelContext
* @param remark
*/
public static void close(ImChannelContext imChannelContext, String remark){
Tio.close(imChannelContext.getTioChannelContext(), remark);
}
}

View File

@ -1,60 +0,0 @@
package org.jim.common;
public interface Protocol {
/**
* 心跳字节
*/
public static final byte HEARTBEAT_BYTE = -128;
/**
* 握手字节
*/
public static final byte HANDSHAKE_BYTE = -127;
/**
* 协议版本号
*/
public final static byte VERSION = 0x01;
public final static String WEBSOCKET = "ws";
public final static String HTTP = "http";
public final static String TCP = "tcp";
public static final String COOKIE_NAME_FOR_SESSION = "jim-s";
/**
* 消息体最多为多少
*/
public static final int MAX_LENGTH_OF_BODY = (int) (1024 * 1024 * 2.1); //只支持多少M数据
/**
* 消息头最少为多少个字节
*/
public static final int LEAST_HEADER_LENGHT = 4;//1+1+2 + (2+4)
/**
* 加密标识位mask1为加密否则不加密
*/
public static final byte FIRST_BYTE_MASK_ENCRYPT = -128;
/**
* 压缩标识位mask1为压缩否则不压缩
*/
public static final byte FIRST_BYTE_MASK_COMPRESS = 0B01000000;
/**
* 是否有同步序列号标识位mask如果有同步序列号则消息头会带有同步序列号否则不带
*/
public static final byte FIRST_BYTE_MASK_HAS_SYNSEQ = 0B00100000;
/**
* 是否是用4字节来表示消息体的长度
*/
public static final byte FIRST_BYTE_MASK_4_BYTE_LENGTH = 0B00010000;
/**
* 版本号mask
*/
public static final byte FIRST_BYTE_MASK_VERSION = 0B00001111;
}

View File

@ -1,21 +0,0 @@
/**
*
*/
package org.jim.common;
/**
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年8月3日 上午9:15:37
*/
public class SessionContext {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}

View File

@ -10,7 +10,7 @@ package org.jim.common;
*/
public interface Status {
public int getCode();
int getCode();
public String getMsg();
String getMsg();
}

View File

@ -8,7 +8,7 @@ import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import org.jim.common.ImAio;
import org.jim.common.Jim;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
@ -87,7 +87,7 @@ public class RedisConfigurationFactory {
url = standardClassloader.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
}
if (url == null) {
url = ImAio.class.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
url = Jim.class.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
}
if (url != null) {
LOG.debug("Configuring redis from jim.properties found in the classpath: " + url);

View File

@ -4,7 +4,6 @@
package org.jim.common.cluster;
import org.jim.common.ImPacket;
import org.tio.core.GroupContext;
/**
*
@ -12,8 +11,8 @@ import org.tio.core.GroupContext;
*
*/
public interface ICluster {
public void clusterToUser(GroupContext groupContext, String userid,ImPacket packet);
public void clusterToGroup(GroupContext groupContext, String group,ImPacket packet);
public void clusterToIp(GroupContext groupContext, String ip,ImPacket packet);
public void clusterToChannelId(GroupContext groupContext, String channelId,ImPacket packet);
public void clusterToUser(String userId,ImPacket packet);
public void clusterToGroup(String group,ImPacket packet);
public void clusterToIp(String ip,ImPacket packet);
public void clusterToChannelId(String channelId,ImPacket packet);
}

View File

@ -1,7 +1,5 @@
package org.jim.common.cluster;
import org.tio.core.GroupContext;
/**
*
* @author WChao
@ -30,7 +28,7 @@ public abstract class ImClusterConfig {
*/
private boolean cluster4all = true;
protected GroupContext groupContext = null;
//protected GroupContext groupContext = null;
public abstract void send(ImClusterVo imClusterVo);
public abstract void sendAsyn(ImClusterVo imClusterVo);

View File

@ -6,7 +6,6 @@ package org.jim.common.cluster.redis;
import org.jim.common.ImPacket;
import org.jim.common.cluster.ImCluster;
import org.jim.common.cluster.ImClusterVo;
import org.tio.core.GroupContext;
/**
* @author WChao
*
@ -18,7 +17,7 @@ public class RedisCluster extends ImCluster{
}
@Override
public void clusterToUser(GroupContext groupContext, String userid,ImPacket packet) {
public void clusterToUser( String userid,ImPacket packet) {
if (clusterConfig.isCluster4user()) {
ImClusterVo imClusterVo = new ImClusterVo(packet);
imClusterVo.setUserid(userid);
@ -27,7 +26,7 @@ public class RedisCluster extends ImCluster{
}
@Override
public void clusterToGroup(GroupContext groupContext, String group,ImPacket packet) {
public void clusterToGroup(String group,ImPacket packet) {
if(clusterConfig.isCluster4group()){
ImClusterVo imClusterVo = new ImClusterVo(packet);
imClusterVo.setGroup(group);
@ -36,7 +35,7 @@ public class RedisCluster extends ImCluster{
}
@Override
public void clusterToIp(GroupContext groupContext, String ip,ImPacket packet) {
public void clusterToIp(String ip,ImPacket packet) {
if(clusterConfig.isCluster4ip()){
ImClusterVo imClusterVo = new ImClusterVo(packet);
imClusterVo.setIp(ip);
@ -45,7 +44,7 @@ public class RedisCluster extends ImCluster{
}
@Override
public void clusterToChannelId(GroupContext groupContext, String channelId,ImPacket packet) {
public void clusterToChannelId(String channelId,ImPacket packet) {
if(clusterConfig.isCluster4channelId()){
ImClusterVo imClusterVo = new ImClusterVo(packet);
imClusterVo.setChannelId(channelId);

View File

@ -7,7 +7,8 @@ import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImAio;
import org.jim.common.ImConst;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.cluster.ImClusterConfig;
import org.jim.common.cluster.ImClusterVo;
@ -16,8 +17,7 @@ import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Aio;
import org.tio.core.GroupContext;
import org.tio.core.Tio;
import org.tio.utils.json.Json;
/**
@ -25,12 +25,10 @@ import org.tio.utils.json.Json;
* @author WChao
*
*/
public class RedisClusterConfig extends ImClusterConfig {
public class RedisClusterConfig extends ImClusterConfig implements ImConst {
private static Logger log = LoggerFactory.getLogger(RedisClusterConfig.class);
public static final String IM_CLUSTER_TOPIC = "JIM_CLUSTER";
private String topicSuffix;
private String topic;
@ -48,19 +46,15 @@ public class RedisClusterConfig extends ImClusterConfig {
* J-IM内置的集群是用redis的topic来实现的所以不同groupContext就要有一个不同的topicSuffix
* @param topicSuffix 不同类型的groupContext就要有一个不同的topicSuffix
* @param redisson
* @param groupContext
* @return
* @author: WChao
*/
public static RedisClusterConfig newInstance(String topicSuffix, RedissonClient redisson, GroupContext groupContext) {
public static RedisClusterConfig newInstance(String topicSuffix, RedissonClient redisson) {
if (redisson == null) {
throw new RuntimeException(RedissonClient.class.getSimpleName() + "不允许为空");
}
if (groupContext == null) {
throw new RuntimeException("GroupContext不允许为空");
}
RedisClusterConfig me = new RedisClusterConfig(topicSuffix, redisson, groupContext);
RedisClusterConfig me = new RedisClusterConfig(topicSuffix, redisson);
me.rtopic = redisson.getTopic(me.topic);
me.rtopic.addListener(new MessageListener<ImClusterVo>() {
@Override
@ -86,53 +80,40 @@ public class RedisClusterConfig extends ImClusterConfig {
//发送给所有
boolean isToAll = imClusterVo.isToAll();
if (isToAll) {
// for (GroupContext groupContext : me.groupContext) {
Aio.sendToAll(groupContext, packet);
// }
//return;
Tio.sendToAll(null, packet);
}
//发送给指定组
String group = imClusterVo.getGroup();
if (StringUtils.isNotBlank(group)) {
ImAio.sendToGroup(group, packet);
//return;
Jim.sendToGroup(group, packet);
}
//发送给指定用户
String userid = imClusterVo.getUserid();
if (StringUtils.isNotBlank(userid)) {
// for (GroupContext groupContext : me.groupContext) {
ImAio.sendToUser(userid, packet);
// }
//return;
Jim.sendToUser(userid, packet);
}
//发送给指定token
String token = imClusterVo.getToken();
if (StringUtils.isNotBlank(token)) {
// for (GroupContext groupContext : me.groupContext) {
Aio.sendToToken(me.groupContext, token, packet);
// }
//return;
//Tio.sendToToken(me.groupContext, token, packet);
}
//发送给指定ip
String ip = imClusterVo.getIp();
if (StringUtils.isNotBlank(ip)) {
// for (GroupContext groupContext : me.groupContext) {
ImAio.sendToIp(me.groupContext, ip, packet);
// }
//return;
//Jim.sendToIp(me.groupContext, ip, packet);
}
}
});
return me;
}
private RedisClusterConfig(String topicSuffix, RedissonClient redisson, GroupContext groupContext) {
private RedisClusterConfig(String topicSuffix, RedissonClient redisson) {
this.setTopicSuffix(topicSuffix);
this.setRedisson(redisson);
this.groupContext = groupContext;
//this.groupContext = groupContext;
}
public String getTopicSuffix() {
return topicSuffix;
@ -140,8 +121,7 @@ public class RedisClusterConfig extends ImClusterConfig {
public void setTopicSuffix(String topicSuffix) {
this.topicSuffix = topicSuffix;
this.topic = topicSuffix + IM_CLUSTER_TOPIC;
this.topic = topicSuffix + Topic.JIM_CLUSTER_TOPIC;
}
public String getTopic() {

View File

@ -1,172 +0,0 @@
/**
*
*/
package org.jim.common.config;
import org.jim.common.cluster.ImCluster;
import org.jim.common.message.MessageHelper;
import org.tio.core.GroupContext;
import org.tio.core.intf.GroupListener;
import org.tio.core.ssl.SslConfig;
/**
* @author WChao
* 2018/08/26
*/
public class Config {
/**
* IP地址
*/
protected String bindIp = null;
/**
* 监听端口
*/
protected Integer bindPort = 80;
/**
* 心跳包发送时长heartbeatTimeout/2
*/
protected long heartbeatTimeout = 0;
/**
* 全局群组上下文;
*/
protected GroupContext groupContext;
/**
* 群组监听器;
*/
protected GroupListener imGroupListener;
/**
* 用户消息持久化助手;
*/
protected MessageHelper messageHelper;
/**
* 是否开启持久化;
*/
protected String isStore = "off";
/**
* 是否开启集群;
*/
protected String isCluster = "off";
/**
* 是否开启SSL加密
*/
protected String isSSL = "off";
/**
* SSL配置
*/
protected SslConfig sslConfig;
/**
* 集群配置
* 如果此值不为null就表示要集群
*/
protected ImCluster cluster;
/**
* 默认的接收数据的buffer size
*/
protected long readBufferSize = 1024 * 2;
public String getBindIp() {
return bindIp;
}
public void setBindIp(String bindIp) {
this.bindIp = bindIp;
}
public Integer getBindPort() {
return bindPort;
}
public void setBindPort(Integer bindPort) {
this.bindPort = bindPort;
}
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
public void setHeartbeatTimeout(long heartbeatTimeout) {
this.heartbeatTimeout = heartbeatTimeout;
}
public GroupContext getGroupContext() {
return groupContext;
}
public void setGroupContext(GroupContext groupContext) {
this.groupContext = groupContext;
}
public GroupListener getImGroupListener() {
return imGroupListener;
}
public void setImGroupListener(GroupListener imGroupListener) {
this.imGroupListener = imGroupListener;
}
public MessageHelper getMessageHelper() {
return messageHelper;
}
public void setMessageHelper(MessageHelper messageHelper) {
this.messageHelper = messageHelper;
}
public String getIsStore() {
return isStore;
}
public void setIsStore(String isStore) {
this.isStore = isStore;
}
public String getIsCluster() {
return isCluster;
}
public void setIsCluster(String isCluster) {
this.isCluster = isCluster;
}
public String getIsSSL() {
return isSSL;
}
public void setIsSSL(String isSSL) {
this.isSSL = isSSL;
}
public SslConfig getSslConfig() {
return sslConfig;
}
public void setSslConfig(SslConfig sslConfig) {
this.sslConfig = sslConfig;
}
public ImCluster getCluster() {
return cluster;
}
public void setCluster(ImCluster cluster) {
this.cluster = cluster;
}
public long getReadBufferSize() {
return readBufferSize;
}
public void setReadBufferSize(long readBufferSize) {
this.readBufferSize = readBufferSize;
}
public interface Builder {
/**
* 配置构建接口
* @return
* @throws Exception
*/
Config build() throws Exception;
}
}

View File

@ -1,33 +0,0 @@
/**
*
*/
package org.jim.common.config;
import org.jim.common.http.HttpConfig;
import org.jim.common.ws.WsServerConfig;
/**
* @author WChao
*
*/
public class DefaultImConfigBuilder extends ImConfigBuilder {
/* (non-Javadoc)
* @see org.jim.common.config.ImConfigBuilder#configHttp(org.jim.common.http.HttpConfig)
*/
@Override
public ImConfigBuilder configHttp(HttpConfig httpConfig) {
// TODO Auto-generated method stub
return this;
}
/* (non-Javadoc)
* @see org.jim.common.config.ImConfigBuilder#configWs(org.jim.common.ws.WsServerConfig)
*/
@Override
public ImConfigBuilder configWs(WsServerConfig wsServerConfig) {
// TODO Auto-generated method stub
return this;
}
}

View File

@ -0,0 +1,278 @@
/**
*
*/
package org.jim.common.config;
import org.jim.common.ImConst;
import org.jim.common.ImHandler;
import org.jim.common.listener.ImGroupListener;
import org.jim.common.listener.ImGroupListenerAdapter;
import org.jim.common.listener.ImListener;
import org.jim.common.listener.ImUserListener;
import org.tio.core.TioConfig;
import org.tio.utils.Threads;
import org.tio.utils.prop.MapWithLockPropSupport;
import org.tio.utils.thread.pool.DefaultThreadFactory;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author WChao
*
*/
public abstract class ImConfig extends MapWithLockPropSupport implements ImConst{
/**
* IP地址
*/
protected String bindIp;
/**
* 监听端口
*/
protected Integer bindPort = 80;
/**
* 默认的接收数据的buffer size
*/
protected int readBufferSize = 1024 * 2;
/**
* 配置名称
*/
protected String name = "j-im";
/**
* tio相关配置信息
*/
protected TioConfig tioConfig;
/**
* 心跳包发送时长heartbeatTimeout/2
*/
protected long heartbeatTimeout = 0;
protected SynThreadPoolExecutor jimExecutor;
/**
* 群组绑定监听器
*/
protected ImGroupListener imGroupListener;
/**
* 用户绑定监听器
*/
protected ImUserListener imUserListener;
private static int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
public ImConfig(){
this(null);
}
public ImConfig(SynThreadPoolExecutor jimExecutor){
this.jimExecutor = jimExecutor;
if (this.jimExecutor == null) {
LinkedBlockingQueue<Runnable> timQueue = new LinkedBlockingQueue<>();
this.jimExecutor = new SynThreadPoolExecutor(CORE_POOL_SIZE, CORE_POOL_SIZE, Threads.KEEP_ALIVE_TIME, timQueue,
DefaultThreadFactory.getInstance(ImConst.JIM, Thread.NORM_PRIORITY), ImConst.JIM);
this.jimExecutor.prestartAllCoreThreads();
}
}
public static class Const{
public static final String ON = "on";
public static final String OFF = "off";
}
/**
* 获取ImHandler对象
* @return
* @author: WChao
*/
public abstract ImHandler getImHandler();
/**
* 获取ImListener对象
* @return
* @author: WChao
*/
public abstract ImListener getImListener();
public static class Global{
private static ImConfig global;
public static <C extends ImConfig> C get(){
return (C) global;
}
public static <C extends ImConfig> C set(C c){
global = (ImConfig) c;
return (C) global;
}
}
public abstract static class Builder<T extends ImConfig, B extends ImConfig.Builder<T,B>> {
/**
* IP地址
*/
protected String bindIp;
/**
* 监听端口
*/
protected Integer bindPort = 80;
/**
* 配置名称
*/
protected String name = "j-im";
/**
* 默认的接收数据的buffer size
*/
protected int readBufferSize = 1024 * 2;
/**
* 心跳包发送时长heartbeatTimeout/2
*/
protected long heartbeatTimeout = 0;
/**
* tio相关配置信息
*/
protected TioConfig tioConfig;
/**
* 群组绑定监听器
*/
protected ImGroupListener imGroupListener;
/**
* 用户绑定监听器
*/
protected ImUserListener imUserListener;
private B theBuilder = this.getThis();
/**
* 供子类获取自身builder抽象类;
* @return
*/
protected abstract B getThis();
public B bindIp(String bindIp){
this.bindIp = bindIp;
return theBuilder;
}
public B bindPort(Integer bindPort){
this.bindPort = bindPort;
return theBuilder;
}
public B name(String name){
this.name = name;
return theBuilder;
}
public B heartbeatTimeout(long heartbeatTimeout){
this.heartbeatTimeout = heartbeatTimeout;
return theBuilder;
}
public B readBufferSize(int readBufferSize){
this.readBufferSize = readBufferSize;
return theBuilder;
}
public B tioConfig(TioConfig tioConfig){
this.tioConfig = tioConfig;
return theBuilder;
}
public B groupListener(ImGroupListener imGroupListener){
this.imGroupListener = imGroupListener;
return theBuilder;
}
public B userListener(ImUserListener imUserListener){
this.imUserListener = imUserListener;
return theBuilder;
}
/**
* 配置构建接口
* @return
* @throws Exception
*/
public abstract T build();
}
public String getBindIp() {
return bindIp;
}
public void setBindIp(String bindIp) {
this.bindIp = bindIp;
}
public Integer getBindPort() {
return bindPort;
}
public void setBindPort(Integer bindPort) {
this.bindPort = bindPort;
}
public int getReadBufferSize() {
return readBufferSize;
}
public void setReadBufferSize(int readBufferSize) {
this.readBufferSize = readBufferSize;
tioConfig.setReadBufferSize(readBufferSize);
}
public TioConfig getTioConfig() {
return tioConfig;
}
public void setTioConfig(TioConfig tioConfig) {
this.tioConfig = tioConfig;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
public void setHeartbeatTimeout(long heartbeatTimeout) {
this.heartbeatTimeout = heartbeatTimeout;
tioConfig.setHeartbeatTimeout(heartbeatTimeout);
}
public SynThreadPoolExecutor getJimExecutor() {
return jimExecutor;
}
public ImGroupListener getImGroupListener() {
return imGroupListener;
}
public void setImGroupListener(ImGroupListener imGroupListener) {
this.imGroupListener = imGroupListener;
if(imGroupListener != null) {
this.tioConfig.setGroupListener(new ImGroupListenerAdapter(this.imGroupListener));
}
}
public ImUserListener getImUserListener() {
return imUserListener;
}
public void setImUserListener(ImUserListener imUserListener) {
this.imUserListener = imUserListener;
}
}

View File

@ -1,96 +0,0 @@
/**
*
*/
package org.jim.common.config;
import org.jim.common.ImConfig;
import org.jim.common.cluster.ImCluster;
import org.jim.common.http.HttpConfig;
import org.jim.common.message.MessageHelper;
import org.jim.common.ws.WsServerConfig;
import org.tio.core.GroupContext;
import org.tio.core.intf.GroupListener;
import org.tio.core.ssl.SslConfig;
/**
* @author WChao
* 2018/08/26
*/
public abstract class ImConfigBuilder implements Config.Builder {
protected ImConfig conf;
public ImConfigBuilder() {
this.conf = new ImConfig();
}
public abstract ImConfigBuilder configHttp(HttpConfig httpConfig);
public abstract ImConfigBuilder configWs(WsServerConfig wsServerConfig);
public ImConfigBuilder setBindIp(String bindIp) {
this.conf.bindIp = bindIp;
return this;
}
public ImConfigBuilder setBindPort(Integer bindPort) {
this.conf.bindPort = bindPort;
return this;
}
public ImConfigBuilder setHeartbeatTimeout(long heartbeatTimeout) {
this.conf.heartbeatTimeout = heartbeatTimeout;
return this;
}
public ImConfigBuilder setGroupContext(GroupContext groupContext) {
this.conf.groupContext = groupContext;
return this;
}
public ImConfigBuilder setImGroupListener(GroupListener imGroupListener) {
this.conf.imGroupListener = imGroupListener;
return this;
}
public ImConfigBuilder setMessageHelper(MessageHelper messageHelper) {
this.conf.messageHelper = messageHelper;
return this;
}
public ImConfigBuilder setIsStore(String isStore) {
this.conf.isStore = isStore;
return this;
}
public ImConfigBuilder setIsCluster(String isCluster) {
this.conf.isCluster = isCluster;
return this;
}
public ImConfigBuilder setIsSSL(String isSSL) {
this.conf.isSSL = isSSL;
return this;
}
public ImConfigBuilder setSslConfig(SslConfig sslConfig) {
this.conf.sslConfig = sslConfig;
return this;
}
public ImConfigBuilder setCluster(ImCluster cluster) {
this.conf.cluster = cluster;
return this;
}
public ImConfigBuilder setReadBufferSize(long readBufferSize) {
this.conf.readBufferSize = readBufferSize;
return this;
}
@Override
public ImConfig build() {
this.configHttp(conf.getHttpConfig());
this.configWs(conf.getWsServerConfig());
return conf;
}
}

View File

@ -1,52 +0,0 @@
/**
*
*/
package org.jim.common.config;
import org.jim.common.ImConfig;
import org.jim.common.http.HttpConfig;
import org.jim.common.utils.PropUtil;
import org.jim.common.ws.WsServerConfig;
/**
* @author WChao
* 2018/08/26
*/
public class PropertyImConfigBuilder extends ImConfigBuilder {
public PropertyImConfigBuilder(String file) {
PropUtil.use(file);
}
@Override
public ImConfigBuilder configHttp(HttpConfig httpConfig) {
//html/css/js等的根目录支持classpath:也支持绝对路径
String pageRoot = PropUtil.get("jim.http.page");
//j-im mvc需要扫描的根目录包
String[] scanPackages = PropUtil.get("jim.http.scan.packages").split(",");
httpConfig.setBindPort((PropUtil.getInt("jim.port")));
//设置web访问路径;
httpConfig.setPageRoot(pageRoot);
//不缓存资源;
httpConfig.setMaxLiveTimeOfStaticRes(PropUtil.getInt("jim.http.max.live.time"));
//设置j-im mvc扫描目录;
httpConfig.setScanPackages(scanPackages);
return this;
}
@Override
public ImConfigBuilder configWs(WsServerConfig wsServerConfig) {
return this;
}
@Override
public ImConfig build() {
super.build();
this.setBindIp(PropUtil.get("jim.bind.ip"));
this.setBindPort(PropUtil.getInt("jim.port"));
this.setHeartbeatTimeout(PropUtil.getLong("jim.heartbeat.timeout"));
this.setIsStore(PropUtil.get("jim.store"));
this.setIsCluster(PropUtil.get("jim.cluster"));
return conf;
}
}

View File

@ -0,0 +1,22 @@
package org.jim.common.exception;
import org.tio.core.exception.LengthOverflowException;
/**
* @ClassName ImDecodeException
* @Description TODO
* @Author WChao
* @Date 2020/1/11 6:02
* @Version 1.0
**/
public class ImDecodeException extends ImException {
public ImDecodeException(Throwable e) {
super(e);
}
public ImDecodeException(String message) {
super(message);
}
}

View File

@ -12,7 +12,7 @@ public class ImException extends Exception{
/**
* @Author WChao
* @Description //TODO
* @param []
* @param
* @return
**/
public ImException() {
@ -21,7 +21,7 @@ public class ImException extends Exception{
/**
* @Author WChao
* @Description //TODO
* @param [message]
* @param message
* @return
**/
public ImException(String message) {
@ -32,7 +32,7 @@ public class ImException extends Exception{
/**
* @Author WChao
* @Description //TODO
* @param [message, cause]
* @param message, cause
* @return
**/
public ImException(String message, Throwable cause) {
@ -43,7 +43,7 @@ public class ImException extends Exception{
/**
* @Author WChao
* @Description //TODO
* @param [message, cause, enableSuppression, writableStackTrace]
* @param message, cause, enableSuppression, writableStackTrace
* @return
**/
public ImException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
@ -54,7 +54,7 @@ public class ImException extends Exception{
/**
* @Author WChao
* @Description //TODO
* @param [cause]
* @param cause
* @return
**/
public ImException(Throwable cause) {

View File

@ -9,6 +9,7 @@ import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jim.common.ImConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -17,7 +18,7 @@ import org.slf4j.LoggerFactory;
* @author wchao
* 2017年5月29日 上午7:45:58
*/
public class Cookie {
public class Cookie implements ImConst {
private static Logger log = LoggerFactory.getLogger(Cookie.class);
/**
@ -50,7 +51,7 @@ public class Cookie {
default:
cookie.setName(cookieMapItem.getKey());
try {
cookie.setValue(URLDecoder.decode(cookieMapItem.getValue(), HttpConst.CHARSET_NAME));
cookie.setValue(URLDecoder.decode(cookieMapItem.getValue(), Http.CHARSET_NAME));
} catch (UnsupportedEncodingException e) {
log.error(e.toString(), e);
}

View File

@ -1,12 +0,0 @@
package org.jim.common.http;
/**
* @author wchao
* 2017年8月18日 下午5:43:54
*/
public interface GroupContextKey {
/**
* 存放HttpConfig
*/
String HTTP_SERVER_CONFIG = "TIO_HTTP_SERVER_CONFIG";
}

View File

@ -1,6 +1,6 @@
package org.jim.common.http;
import org.jim.common.config.Config;
import org.jim.common.ImConst;
import org.jim.common.http.handler.IHttpRequestHandler;
import org.jim.common.http.listener.IHttpServerListener;
import org.jim.common.session.id.ISessionIdGenerator;
@ -10,19 +10,24 @@ import org.tio.utils.cache.ICache;
* @author wchao
* 2017年8月15日 下午1:21:14
*/
public class HttpConfig extends Config{
// private static Logger log = LoggerFactory.getLogger(HttpConfig.class);
public class HttpConfig implements ImConst {
/**
* IP地址
*/
protected String bindIp;
/**
* 监听端口
*/
protected Integer bindPort = 80;
/**
* 存放HttpSession对象的cacheName
*/
public static final String SESSION_CACHE_NAME = "tio-h-s";
public static final String SESSION_CACHE_NAME = "jim-h-s";
/**
* 存放sessionId的cookie name
*/
public static final String SESSION_COOKIE_NAME = "TwIxO";
public static final String SESSION_COOKIE_NAME = "jimIxO";
/**
* session默认的超时时间单位
@ -32,7 +37,7 @@ public class HttpConfig extends Config{
/**
* 默认的静态资源缓存时间单位
*/
public static final int MAX_LIVETIME_OF_STATICRES = 60 * 10;
public static final int MAX_LIVE_TIME_OF_STATICS = 60 * 10;
/**
* 文件上传时boundary值的最大长度
@ -49,17 +54,9 @@ public class HttpConfig extends Config{
*/
public static final int MAX_LENGTH_OF_MULTI_BODY = 1024 * 1024 * 20;
/**
* @param args
* @author wchao
*/
public static void main(String[] args) {
private String serverInfo = Http.SERVER_INFO;
}
private String serverInfo = HttpConst.SERVER_INFO;
private String charset = HttpConst.CHARSET_NAME;
private String charset = Http.CHARSET_NAME;
private ICache sessionStore = null;
@ -72,14 +69,14 @@ public class HttpConfig extends Config{
/**
* session超时时间单位
*/
private long sessionTimeout = DEFAULT_SESSION_TIMEOUT;
private Long sessionTimeout = DEFAULT_SESSION_TIMEOUT;
private String sessionCookieName = SESSION_COOKIE_NAME;
/**
* 静态资源缓存时间如果小于等于0则不缓存单位
*/
private int maxLiveTimeOfStaticRes = MAX_LIVETIME_OF_STATICRES;
private Integer maxLiveTimeOfStaticRes = MAX_LIVE_TIME_OF_STATICS;
private String page404 = "/404.html";
@ -97,36 +94,138 @@ public class HttpConfig extends Config{
* 2绝对路径/page
* //FileUtil.getAbsolutePath("page");//"/page";
*/
private String pageRoot = null;
private String pageRoot = "page";
/**
* mvc扫描包路径;
*/
private String[] scanPackages = null;
public HttpConfig(IHttpRequestHandler httpRequestHandler, IHttpServerListener httpServerListener){
setHttpRequestHandler(httpRequestHandler);
setHttpServerListener(httpServerListener);
}
public HttpConfig() {}
public static HttpConfig.Builder newBuilder(){
return new HttpConfig.Builder();
}
public static class Builder{
private String charset = Http.CHARSET_NAME;
private ICache sessionStore;
private Long sessionTimeout = DEFAULT_SESSION_TIMEOUT;
/**
*
* @author wchao
* 静态资源缓存时间如果小于等于0则不缓存单位
*/
public HttpConfig(Integer bindPort, Long sessionTimeout) {
this.bindPort = bindPort;
if (sessionTimeout != null) {
private Integer maxLiveTimeOfStaticRes = MAX_LIVE_TIME_OF_STATICS;
private String page404 = "/404.html";
private String page500 = "/500.html";
private ISessionIdGenerator sessionIdGenerator;
private IHttpRequestHandler httpRequestHandler;
private IHttpServerListener httpServerListener;
private String pageRoot = "page";
private String[] scanPackages = null;
public Builder charset(String charset){
this.charset = charset;
return this;
}
public Builder sessionStore(ICache sessionStore){
this.sessionStore = sessionStore;
return this;
}
public Builder sessionTimeout(Long sessionTimeout){
this.sessionTimeout = sessionTimeout;
return this;
}
public Builder maxLiveTimeOfStaticRes(int maxLiveTimeOfStaticRes){
this.maxLiveTimeOfStaticRes = maxLiveTimeOfStaticRes;
return this;
}
public Builder page404(String page404){
this.page404 = page404;
return this;
}
public Builder page500(String page500){
this.page500 = page500;
return this;
}
public Builder sessionIdGenerator(ISessionIdGenerator sessionIdGenerator){
this.sessionIdGenerator = sessionIdGenerator;
return this;
}
public Builder httpRequestHandler(IHttpRequestHandler httpRequestHandler){
this.httpRequestHandler = httpRequestHandler;
return this;
}
public Builder httpServerListener(IHttpServerListener httpServerListener){
this.httpServerListener = httpServerListener;
return this;
}
public Builder pageRoot(String pageRoot){
this.pageRoot = pageRoot;
return this;
}
public Builder scanPackages(String[] scanPackages){
this.scanPackages = scanPackages;
return this;
}
public HttpConfig build(){
HttpConfig httpConfig = new HttpConfig(this.httpRequestHandler, this.httpServerListener);
httpConfig.setCharset(this.charset);
httpConfig.setSessionStore(this.sessionStore);
httpConfig.setSessionTimeout(this.sessionTimeout);
httpConfig.setMaxLiveTimeOfStaticRes(maxLiveTimeOfStaticRes);
httpConfig.setPage404(page404);
httpConfig.setPage500(page500);
httpConfig.setSessionIdGenerator(sessionIdGenerator);
httpConfig.setPageRoot(pageRoot);
httpConfig.setScanPackages(scanPackages);
return httpConfig;
}
}
/**
* @return the charset
*/
public String getCharset() {
return charset;
}
/**
* @return the maxLiveTimeOfStaticRes
*/
public void setCharset(String charset) {
this.charset = charset;
}
public ICache getSessionStore() {
return sessionStore;
}
public void setSessionStore(ICache sessionStore) {
this.sessionStore = sessionStore;
}
public Long getSessionTimeout() {
return sessionTimeout;
}
public int getMaxLiveTimeOfStaticRes() {
return maxLiveTimeOfStaticRes;
}
@ -135,127 +234,34 @@ public class HttpConfig extends Config{
return page404;
}
public String getPage500() {
return page500;
}
/**
* @return the pageRoot
*/
public String getPageRoot() {
return pageRoot;
}
/**
* @return the serverInfo
*/
public String getServerInfo() {
return serverInfo;
}
/**
* @return the sessionCacheName
*/
public String getSessionCacheName() {
return sessionCacheName;
}
public String getSessionCookieName() {
return sessionCookieName;
}
public ISessionIdGenerator getSessionIdGenerator() {
return sessionIdGenerator;
}
public ICache getSessionStore() {
return sessionStore;
}
public long getSessionTimeout() {
return sessionTimeout;
}
/**
* @param charset the charset to set
*/
public void setCharset(String charset) {
this.charset = charset;
}
/**
* @param maxLiveTimeOfStaticRes the maxLiveTimeOfStaticRes to set
*/
public void setMaxLiveTimeOfStaticRes(int maxLiveTimeOfStaticRes) {
this.maxLiveTimeOfStaticRes = maxLiveTimeOfStaticRes;
}
public void setPage404(String page404) {
this.page404 = page404;
}
public String getPage500() {
return page500;
}
public void setPage500(String page500) {
this.page500 = page500;
}
/**
*
* @param pageRoot
* @author wchao
*/
public void setPageRoot(String pageRoot) {
this.pageRoot = pageRoot;//FileUtil.getAbsolutePath(root);//"/page";;
}
/**
* @param serverInfo the serverInfo to set
*/
public void setServerInfo(String serverInfo) {
this.serverInfo = serverInfo;
}
/**
* @param sessionCacheName the sessionCacheName to set
*/
public void setSessionCacheName(String sessionCacheName) {
this.sessionCacheName = sessionCacheName;
}
public void setSessionCookieName(String sessionCookieName) {
this.sessionCookieName = sessionCookieName;
public ISessionIdGenerator getSessionIdGenerator() {
return sessionIdGenerator;
}
public void setSessionIdGenerator(ISessionIdGenerator sessionIdGenerator) {
this.sessionIdGenerator = sessionIdGenerator;
}
public void setSessionStore(ICache sessionStore) {
this.sessionStore = sessionStore;
// this.httpSessionManager = HttpSessionManager.getInstance(sessionStore);
}
/**
* @return the httpRequestHandler
*/
public IHttpRequestHandler getHttpRequestHandler() {
return httpRequestHandler;
}
/**
* @param httpRequestHandler the httpRequestHandler to set
*/
public void setHttpRequestHandler(IHttpRequestHandler httpRequestHandler) {
this.httpRequestHandler = httpRequestHandler;
}
public String[] getScanPackages() {
return scanPackages;
}
public void setScanPackages(String[] scanPackages) {
this.scanPackages = scanPackages;
}
public IHttpServerListener getHttpServerListener() {
return httpServerListener;
}
@ -264,4 +270,55 @@ public class HttpConfig extends Config{
this.httpServerListener = httpServerListener;
}
public String getPageRoot() {
return pageRoot;
}
public void setPageRoot(String pageRoot) {
this.pageRoot = pageRoot;
}
public String[] getScanPackages() {
return scanPackages;
}
public void setScanPackages(String[] scanPackages) {
this.scanPackages = scanPackages;
}
public String getServerInfo() {
return serverInfo;
}
public String getSessionCacheName() {
return sessionCacheName;
}
public String getSessionCookieName() {
return sessionCookieName;
}
public String getBindIp() {
return bindIp;
}
public void setBindIp(String bindIp) {
this.bindIp = bindIp;
}
public Integer getBindPort() {
return bindPort;
}
public void setBindPort(Integer bindPort) {
this.bindPort = bindPort;
}
public void setSessionTimeout(Long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public void setMaxLiveTimeOfStaticRes(Integer maxLiveTimeOfStaticRes) {
this.maxLiveTimeOfStaticRes = maxLiveTimeOfStaticRes;
}
}

View File

@ -7,182 +7,5 @@ package org.jim.common.http;
*/
public interface HttpConst {
/**
* 请求体的格式
* @author wchao
* 2017年6月28日 上午10:03:12
*/
public enum RequestBodyFormat {
URLENCODED, MULTIPART, TEXT
}
/**
* Accept-Language : zh-CN,zh;q=0.8
Sec-WebSocket-Version : 13
Sec-WebSocket-Extensions : permessage-deflate; client_max_window_bits
Upgrade : websocket
Host : t-io.org:9321
Accept-Encoding : gzip, deflate, sdch
User-Agent : Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Origin : http://www.t-io.org:9292
Sec-WebSocket-Key : kmCL2C7q9vtNSMyHpft7lw==
Connection : Upgrade
Cache-Control : no-cache
Pragma : no-cache
*
* @author wchao
* 2017年5月27日 下午2:11:57
*/
public interface RequestHeaderKey {
String Cookie = "Cookie".toLowerCase();//Cookie: $Version=1; Skin=new;
String Origin = "Origin".toLowerCase(); //http://127.0.0.1
String Sec_WebSocket_Key = "Sec-WebSocket-Key".toLowerCase(); //2GFwqJ1Z37glm62YKKLUeA==
String Cache_Control = "Cache-Control".toLowerCase(); //no-cache
String Connection = "Connection".toLowerCase(); //Upgrade, keep-alive
String User_Agent = "User-Agent".toLowerCase(); //Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3088.3 Safari/537.36
String Sec_WebSocket_Version = "Sec-WebSocket-Version".toLowerCase(); //13
String Host = "Host".toLowerCase(); //127.0.0.1:9321
String Pragma = "Pragma".toLowerCase(); //no-cache
String Accept_Encoding = "Accept-Encoding".toLowerCase(); //gzip, deflate, br
String Accept_Language = "Accept-Language".toLowerCase(); //zh-CN,zh;q=0.8,en;q=0.6
String Upgrade = "Upgrade".toLowerCase(); //websocket
String Sec_WebSocket_Extensions = "Sec-WebSocket-Extensions".toLowerCase(); //permessage-deflate; client_max_window_bits
String Content_Length = "Content-Length".toLowerCase(); //65
String Content_Type = "Content-Type".toLowerCase();// : application/x-www-form-urlencodedapplication/x-www-form-urlencoded; charset=UTF-8multipart/form-data; boundary=----WebKitFormBoundaryuwYcfA2AIgxqIxA0
String If_Modified_Since = "If-Modified-Since".toLowerCase(); //与Last-Modified配合
/**
* 值为XMLHttpRequest则为Ajax
*/
String X_Requested_With = "X-Requested-With".toLowerCase();//XMLHttpRequest
}
// Content-Type: text/html;charset:utf-8;
/**
*
* @author wchao
* 2017年6月27日 下午8:23:58
*/
public interface RequestHeaderValue {
public interface Connection {
String keep_alive = "keep-alive".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
String close = "close".toLowerCase();
}
//application/x-www-form-urlencodedmultipart/form-datatext/plain
public interface Content_Type {
/**
* 普通文本一般会是json或是xml
*/
String text_plain = "text/plain".toLowerCase();
/**
* 文件上传
*/
String multipart_form_data = "multipart/form-data".toLowerCase();
/**
* 普通的key-value
*/
String application_x_www_form_urlencoded = "application/x-www-form-urlencoded".toLowerCase();
}
}
public interface ResponseHeaderKey {
//Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
String Set_Cookie = "Set-Cookie".toLowerCase(); //Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
String Content_Length = "Content-Length".toLowerCase(); //65
String Connection = "Connection".toLowerCase(); //Upgrade, keep-alive
String Keep_Alive = "Keep-Alive".toLowerCase(); //Keep-Alive:timeout=20
String Sec_WebSocket_Accept = "Sec-WebSocket-Accept".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
/**
* Content-Disposition: attachment;filename=FileName.txt
* 文件下载
*/
String Content_disposition = "Content-disposition".toLowerCase();
/**
* 文档的编码Encode方法只有在解码之后才可以得到Content-Type头指定的内容类型
* 利用gzip压缩文档能够显著地减少HTML文档的下载时间
* Java的GZIPOutputStream可以很方便地进行gzip压缩但只有Unix上的Netscape和Windows上的IE 4IE 5才支持它
* 因此Servlet应该通过查看Accept-Encoding头即request.getHeader("Accept-Encoding")检查浏览器是否支持gzip
* 为支持gzip的浏览器返回经gzip压缩的HTML页面为其他浏览器返回普通页面
*/
String Content_Encoding = "Content-Encoding".toLowerCase();
/**
* 表示后面的文档属于什么MIME类型Servlet默认为text/plain但通常需要显式地指定为text/html
* 由于经常要设置Content-Type因此HttpServletResponse提供了一个专用的方法setContentType
*/
String Content_Type = "Content-Type".toLowerCase();
/**
* 当前的GMT时间你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦
*/
String Date = "Date".toLowerCase();
/**
* 应该在什么时候认为文档已经过期从而不再缓存它
*/
String Expires = "Expires".toLowerCase();
/**
* 文档的最后改动时间客户可以通过If-Modified-Since请求头提供一个日期该请求将被视为一个条件GET
* 只有改动时间迟于指定时间的文档才会返回否则返回一个304Not Modified状态Last-Modified也可用setDateHeader方法来设置
*/
String Last_Modified = "Last-Modified".toLowerCase();
/**
* 表示客户应当到哪里去提取文档Location通常不是直接设置的而是通过HttpServletResponse的sendRedirect方法该方法同时设置状态代码为302
*/
String Location = "Location".toLowerCase();
/**
* 表示浏览器应该在多少时间之后刷新文档以秒计除了刷新当前文档之外你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面
注意这种功能通常是通过设置HTML页面HEAD区的META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path"实现这是因为自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要但是对于Servlet来说直接设置Refresh头更加方便
注意Refresh的意义是"N秒之后刷新本页面或访问指定页面"而不是"每隔N秒刷新本页面或访问指定页面"因此连续刷新要求每次都发送一个Refresh头而发送204状态代码则可以阻止浏览器继续刷新不管是使用Refresh头还是META HTTP-EQUIV="Refresh" ...
注意Refresh头不属于HTTP 1.1正式规范的一部分而是一个扩展但Netscape和IE都支持它
*/
String Refresh = "Refresh".toLowerCase();
/**
* 服务器名字Servlet一般不设置这个值而是由Web服务器自己设置
*/
String Server = "Server".toLowerCase();
/**
*
*/
String Access_Control_Allow_Origin = "Access-Control-Allow-Origin".toLowerCase(); //value: *
/**
*
*/
String Access_Control_Allow_Headers = "Access-Control-Allow-Headers".toLowerCase(); //value: x-requested-with,content-type
/**
* 是否是从缓存中获取的数据tio-httpserver特有的头部信息
*/
String tio_from_cache = "tio-from-cache";
}
/**
*
* @author wchao
* 2017年6月27日 下午8:24:02
*/
public interface ResponseHeaderValue {
public interface Connection {
String keep_alive = "keep-alive".toLowerCase();
String Upgrade = "Upgrade".toLowerCase();
String close = "close".toLowerCase();
}
}
/**
*
*/
String SERVER_INFO = "tio-httpserver/0.0.1";
/**
* 默认规定连接到本服务器的客户端统一用utf-8
*/
String CHARSET_NAME = "utf-8";
}

View File

@ -3,26 +3,26 @@
*/
package org.jim.common.http;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.http.session.HttpSession;
import org.jim.common.packets.Command;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.tio.core.ChannelContext;
import org.jim.common.protocol.IProtocolConverter;
/**
* HTTP协议消息转化包
* @author WChao
*
*/
public class HttpConvertPacket implements IConvertProtocolPacket {
public class HttpConvertPacket implements IProtocolConverter {
/**
* 转HTTP协议响应包;
*/
@Override
public ImPacket RespPacket(byte[] body, Command command,ChannelContext channelContext) {
Object sessionContext = channelContext.getAttribute();
public ImPacket RespPacket(byte[] body, Command command, ImChannelContext channelContext) {
ImSessionContext sessionContext = channelContext.getSessionContext();
if(sessionContext instanceof HttpSession){
HttpRequest request = (HttpRequest)channelContext.getAttribute(ImConst.HTTP_REQUEST);
HttpResponse response = new HttpResponse(request,request.getHttpConfig());
@ -34,7 +34,7 @@ public class HttpConvertPacket implements IConvertProtocolPacket {
}
@Override
public ImPacket ReqPacket(byte[] body, Command command,ChannelContext channelContext) {
public ImPacket ReqPacket(byte[] body, Command command, ImChannelContext channelContext) {
return null;
}

View File

@ -8,10 +8,10 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImChannelContext;
import org.jim.common.exception.ImDecodeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.exception.LengthOverflowException;
import org.tio.core.utils.ByteBufferUtils;
import org.jim.common.utils.HttpParseUtils;
@ -83,31 +83,20 @@ public class HttpMultiBodyDecoder {
* @author wchao
* 2017年7月27日 上午10:18:01
*/
public static interface MultiBodyHeaderKey {
public interface MultiBodyHeaderKey {
String Content_Disposition = "Content-Disposition".toLowerCase();
String Content_Type = "Content-Type".toLowerCase();
}
public static enum Step {
public enum Step {
BOUNDARY, HEADER, BODY, END
}
private static Logger log = LoggerFactory.getLogger(HttpMultiBodyDecoder.class);
// public static int processReadIndex(ByteBuffer buffer)
// {
// int newReaderIndex = buffer.readerIndex();
// if (newReaderIndex < buffer.capacity())
// {
// buffer.readerIndex(newReaderIndex + 1);
// return 1;
// }
// return 0;
// }
public static void decode(HttpRequest request, RequestLine firstLine, byte[] bodyBytes, String initboundary, ChannelContext channelContext) throws AioDecodeException {
if (StringUtils.isBlank(initboundary)) {
throw new AioDecodeException("boundary is null");
public static void decode(HttpRequest request, RequestLine firstLine, byte[] bodyBytes, String initBoundary, ImChannelContext channelContext) throws ImDecodeException {
if (StringUtils.isBlank(initBoundary)) {
throw new ImDecodeException("boundary is null");
}
long start = SystemTimer.currentTimeMillis();
@ -115,25 +104,21 @@ public class HttpMultiBodyDecoder {
ByteBuffer buffer = ByteBuffer.wrap(bodyBytes);
buffer.position(0);
String boundary = "--" + initboundary;
String boundary = "--" + initBoundary;
String endBoundary = boundary + "--";
// int boundaryLength = boundary.getBytes().length;
Step step = Step.BOUNDARY;
// int bufferLength = buffer.capacity();
try {
label1: while (true) {
if (step == Step.BOUNDARY) {
String line = ByteBufferUtils.readLine(buffer, request.getCharset(), HttpConfig.MAX_LENGTH_OF_BOUNDARY);
// int offset = HttpMultiBodyDecoder.processReadIndex(buffer);
if (boundary.equals(line)) {
step = Step.HEADER;
} else if (endBoundary.equals(line)) // 结束了
// 结束了
} else if (endBoundary.equals(line))
{
// int ss = buffer.readerIndex() + 2 - offset;
break;
} else {
throw new AioDecodeException("line need:" + boundary + ", but is: " + line + "");
throw new ImDecodeException("line need:" + boundary + ", but is: " + line + "");
}
}
@ -164,7 +149,7 @@ public class HttpMultiBodyDecoder {
}
} catch (LengthOverflowException loe) {
throw new AioDecodeException(loe);
throw new ImDecodeException(loe);
} catch (UnsupportedEncodingException e) {
log.error(channelContext.toString(), e);
} finally {
@ -175,37 +160,6 @@ public class HttpMultiBodyDecoder {
}
/**
* 返回值不包括最后的\r\n
* @param buffer
* @param charset
* @return
* @throws UnsupportedEncodingException
*/
// public static String getLine(ByteBuffer buffer, String charset) throws UnsupportedEncodingException {
// char lastByte = 0; // 上一个字节
// int initPosition = buffer.position();
//
// while (buffer.hasRemaining()) {
// char b = (char) buffer.get();
//
// if (b == '\n') {
// if (lastByte == '\r') {
// int startIndex = initPosition;
// int endIndex = buffer.position() - 2;
// int length = endIndex - startIndex;
// byte[] dst = new byte[length];
//
// System.arraycopy(buffer.array(), startIndex, dst, 0, length);
// String line = new String(dst, charset);
// return line;
// }
// }
// lastByte = b;
// }
// return null;
// }
/**
* @param args
* @throws UnsupportedEncodingException
@ -234,7 +188,7 @@ public class HttpMultiBodyDecoder {
* @throws LengthOverflowException
* @author wchao
*/
public static Step parseBody(Header header, HttpRequest request, ByteBuffer buffer, String boundary, String endBoundary, ChannelContext channelContext)
public static Step parseBody(Header header, HttpRequest request, ByteBuffer buffer, String boundary, String endBoundary, ImChannelContext channelContext)
throws UnsupportedEncodingException, LengthOverflowException {
int initPosition = buffer.position();
@ -249,10 +203,11 @@ public class HttpMultiBodyDecoder {
byte[] dst = new byte[length];
System.arraycopy(buffer.array(), startIndex, dst, 0, length);
//该字段类型是file
String filename = header.getFilename();
if (filename != null)//该字段类型是file
if (filename != null)
{
if (!"".equals(filename)) { //
if (!"".equals(filename)) {
UploadFile uploadFile = new UploadFile();
uploadFile.setName(filename);
uploadFile.setData(dst);
@ -285,24 +240,22 @@ public class HttpMultiBodyDecoder {
* @param header
* @author wchao
*/
public static void parseHeader(List<String> lines, Header header, ChannelContext channelContext) throws AioDecodeException {
public static void parseHeader(List<String> lines, Header header, ImChannelContext channelContext) throws ImDecodeException {
if (lines == null || lines.size() == 0) {
throw new AioDecodeException("multipart_form_data 格式不对,没有头部信息");
throw new ImDecodeException("multipart_form_data 格式不对,没有头部信息");
}
try {
for (String line : lines) {
String[] keyvalue = StringUtils.split(line, ":");
String key = StringUtils.lowerCase(StringUtils.trim(keyvalue[0]));//
String value = StringUtils.trim(keyvalue[1]);
String[] keyValue = StringUtils.split(line, ":");
String key = StringUtils.lowerCase(StringUtils.trim(keyValue[0]));
String value = StringUtils.trim(keyValue[1]);
header.map.put(key, value);
}
String contentDisposition = header.map.get(MultiBodyHeaderKey.Content_Disposition);
String name = HttpParseUtils.getPerprotyEqualValue(header.map, MultiBodyHeaderKey.Content_Disposition, "name");
String filename = HttpParseUtils.getPerprotyEqualValue(header.map, MultiBodyHeaderKey.Content_Disposition, "filename");
String contentType = header.map.get(MultiBodyHeaderKey.Content_Type);//.HttpParseUtils.getPerprotyEqualValue(header.map, MultiBodyHeaderKey.Content_Type, "filename");
String contentType = header.map.get(MultiBodyHeaderKey.Content_Type);
header.setContentDisposition(contentDisposition);
header.setName(name);
header.setFilename(filename);
@ -310,29 +263,8 @@ public class HttpMultiBodyDecoder {
} catch (Exception e) {
log.error(channelContext.toString(), e);
throw new AioDecodeException(e.toString());
throw new ImDecodeException(e.toString());
}
// for (int i = 0; i < lines.size(); i++) {
// String line = lines.get(i);
// if (i == 0) {
// String[] mapStrings = StringUtils.split(line, ";");
// String s = mapStrings[0];//
//
// String[] namekeyvalue = StringUtils.split(mapStrings[1], "=");
// header.setName(namekeyvalue[1].substring(1, namekeyvalue[1].length() - 1));
//
// if (mapStrings.length == 3) {
// String[] finenamekeyvalue = StringUtils.split(mapStrings[2], "=");
// String filename = finenamekeyvalue[1].substring(1, finenamekeyvalue[1].length() - 1);
// header.setFilename(FilenameUtils.getName(filename));
// }
// } else if (i == 1) {
// String[] map = StringUtils.split(line, ":");
// String contentType = map[1].trim();//
// header.setContentType(contentType);
// }
// }
}
/**

View File

@ -4,62 +4,58 @@
package org.jim.common.http;
import java.nio.ByteBuffer;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.Protocol;
import org.jim.common.exception.ImException;
import org.jim.common.http.session.HttpSession;
import org.jim.common.protocol.AbProtocol;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.jim.common.protocol.AbstractProtocol;
import org.jim.common.protocol.IProtocolConverter;
import org.jim.common.utils.ImUtils;
import org.tio.core.ChannelContext;
/**
*
* Http协议校验器
* @author WChao
*
*/
public class HttpProtocol extends AbProtocol {
public class HttpProtocol extends AbstractProtocol {
@Override
public String name() {
return Protocol.HTTP;
}
public HttpProtocol(IProtocolConverter protocolConverter){
super(protocolConverter);
}
@Override
public boolean isProtocolByBuffer(ByteBuffer buffer,ChannelContext channelContext) throws Throwable {
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
if(imSessionContext != null && imSessionContext instanceof HttpSession) {
protected void init(ImChannelContext imChannelContext) {
imChannelContext.setSessionContext(new HttpSession(imChannelContext));
ImUtils.setClient(imChannelContext);
}
@Override
public boolean validateProtocol(ImSessionContext imSessionContext) throws ImException {
if(imSessionContext instanceof HttpSession) {
return true;
}
if(buffer != null){
HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext,false);
if(request.getHeaders().get(HttpConst.RequestHeaderKey.Sec_WebSocket_Key) == null)
return false;
}
@Override
public boolean validateProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException {
HttpRequest request = HttpRequestDecoder.decode(buffer, imChannelContext,false);
if(request.getHeaders().get(Http.RequestHeaderKey.Sec_WebSocket_Key) == null)
{
channelContext.setAttribute(new HttpSession());
ImUtils.setClient(channelContext);
return true;
}
}
return false;
}
@Override
public IConvertProtocolPacket converter() {
return new HttpConvertPacket();
}
@Override
public boolean isProtocol(ImPacket imPacket,ChannelContext channelContext) throws Throwable {
if(imPacket == null) {
return false;
}
public boolean validateProtocol(ImPacket imPacket) throws ImException {
if(imPacket instanceof HttpPacket){
Object sessionContext = channelContext.getAttribute();
if(sessionContext == null){
channelContext.setAttribute(new HttpSession());
}
return true;
}
return false;

View File

@ -7,12 +7,13 @@ import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
import org.jim.common.http.HttpConst.RequestBodyFormat;
import org.jim.common.http.session.HttpSession;
import org.tio.utils.hutool.ArrayUtil;
import cn.hutool.core.util.ArrayUtil;
/**
*
* @author wchao
@ -20,7 +21,7 @@ import cn.hutool.core.util.ArrayUtil;
*/
public class HttpRequest extends HttpPacket {
// private static Logger log = LoggerFactory.getLogger(HttpRequest.class);
private static Logger log = LoggerFactory.getLogger(HttpRequest.class);
private static final long serialVersionUID = -3849253977016967211L;
@ -43,14 +44,12 @@ public class HttpRequest extends HttpPacket {
private Map<String, Cookie> cookieMap = null;
private int contentLength;
private String bodyString;
private RequestBodyFormat bodyFormat;
private String charset = HttpConst.CHARSET_NAME;
private Http.RequestBodyFormat bodyFormat;
private String charset = Http.CHARSET_NAME;
private Boolean isAjax = null;
private Boolean isSupportGzip = null;
private HttpSession httpSession;
private Node remote = null;
private ChannelContext channelContext;
private Node remote;
private HttpConfig httpConfig;
/**
@ -84,7 +83,7 @@ public class HttpRequest extends HttpPacket {
/**
* @return the bodyFormat
*/
public RequestBodyFormat getBodyFormat() {
public Http.RequestBodyFormat getBodyFormat() {
return bodyFormat;
}
@ -95,13 +94,6 @@ public class HttpRequest extends HttpPacket {
return bodyString;
}
/**
* @return the channelContext
*/
public ChannelContext getChannelContext() {
return channelContext;
}
/**
* @return the charset
*/
@ -156,7 +148,7 @@ public class HttpRequest extends HttpPacket {
*/
public Boolean getIsAjax() {
if (isAjax == null) {
String X_Requested_With = this.getHeader(HttpConst.RequestHeaderKey.X_Requested_With);
String X_Requested_With = this.getHeader(Http.RequestHeaderKey.X_Requested_With);
if (X_Requested_With != null && "XMLHttpRequest".equalsIgnoreCase(X_Requested_With)) {
isAjax = true;
} else {
@ -172,7 +164,7 @@ public class HttpRequest extends HttpPacket {
*/
public Boolean getIsSupportGzip() {
if (isSupportGzip == null) {
String Accept_Encoding = getHeader(HttpConst.RequestHeaderKey.Accept_Encoding);
String Accept_Encoding = getHeader(Http.RequestHeaderKey.Accept_Encoding);
if (StringUtils.isNoneBlank(Accept_Encoding)) {
String[] ss = StringUtils.split(Accept_Encoding, ",");
if (ArrayUtil.contains(ss, "gzip")) {
@ -219,7 +211,7 @@ public class HttpRequest extends HttpPacket {
}
public void parseCookie() {
String cookieLine = headers.get(HttpConst.RequestHeaderKey.Cookie);
String cookieLine = headers.get(Http.RequestHeaderKey.Cookie);
if (StringUtils.isNotBlank(cookieLine)) {
cookies = new ArrayList<>();
cookieMap = new HashMap<>();
@ -233,7 +225,7 @@ public class HttpRequest extends HttpPacket {
Cookie cookie = Cookie.buildCookie(cookieOneMap);
cookies.add(cookie);
cookieMap.put(cookie.getName(), cookie);
//log.error("{}, 收到cookie:{}", channelContext, cookie.toString());
log.info("{}, 收到cookie:{}", imChannelContext, cookie.toString());
}
}
}
@ -241,7 +233,7 @@ public class HttpRequest extends HttpPacket {
/**
* @param bodyFormat the bodyFormat to set
*/
public void setBodyFormat(RequestBodyFormat bodyFormat) {
public void setBodyFormat(Http.RequestBodyFormat bodyFormat) {
this.bodyFormat = bodyFormat;
}
@ -252,13 +244,6 @@ public class HttpRequest extends HttpPacket {
this.bodyString = bodyString;
}
/**
* @param channelContext the channelContext to set
*/
public void setChannelContext(ChannelContext channelContext) {
this.channelContext = channelContext;
}
/**
* @param charset the charset to set
*/
@ -267,7 +252,7 @@ public class HttpRequest extends HttpPacket {
}
/**
* @param bodyLength the bodyLength to set
* @param contentLength the bodyLength to set
*/
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
@ -290,7 +275,6 @@ public class HttpRequest extends HttpPacket {
/**
* 设置好header后会把cookie等头部信息也设置好
* @param headers the headers to set
* @param channelContext
*/
@Override
public void setHeaders(Map<String, String> headers) {

View File

@ -7,24 +7,23 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.http.HttpConst.RequestBodyFormat;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.exception.ImDecodeException;
import org.jim.common.utils.HttpParseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.exception.LengthOverflowException;
import org.tio.core.utils.ByteBufferUtils;
import cn.hutool.core.util.StrUtil;
import org.tio.utils.hutool.StrUtil;
/**
*
* @author WChao
*
*/
public class HttpRequestDecoder {
public static enum Step {
public class HttpRequestDecoder implements ImConst {
public enum Step {
firstLine, header, body
}
@ -40,7 +39,7 @@ public class HttpRequestDecoder {
*/
public static final int MAX_LENGTH_OF_HEADER_LINE = 2048;
public static HttpRequest decode(ByteBuffer buffer, ChannelContext channelContext,boolean isBody) throws AioDecodeException {
public static HttpRequest decode(ByteBuffer buffer, ImChannelContext channelContext, boolean isBody) throws ImDecodeException {
int initPosition = buffer.position();
int readableLength = buffer.limit() - initPosition;
// int count = 0;
@ -48,7 +47,7 @@ public class HttpRequestDecoder {
// StringBuilder currLine = new StringBuilder();
Map<String, String> headers = new HashMap<>();
int contentLength = 0;
byte[] bodyBytes = null;
byte[] bodyBytes;
StringBuilder headerSb = new StringBuilder(512);
RequestLine firstLine = null;
@ -57,12 +56,12 @@ public class HttpRequestDecoder {
try {
line = ByteBufferUtils.readLine(buffer, null, MAX_LENGTH_OF_HEADER_LINE);
} catch (LengthOverflowException e) {
throw new AioDecodeException(e);
throw new ImDecodeException(e);
}
int newPosition = buffer.position();
if (newPosition - initPosition > MAX_LENGTH_OF_HEADER) {
throw new AioDecodeException("max http header length " + MAX_LENGTH_OF_HEADER);
throw new ImDecodeException("max http header length " + MAX_LENGTH_OF_HEADER);
}
if (line == null) {
@ -72,7 +71,7 @@ public class HttpRequestDecoder {
headerSb.append(line).append("\r\n");
//头部解析完成了
if ("".equals(line) && isBody) {
String contentLengthStr = headers.get(HttpConst.RequestHeaderKey.Content_Length);
String contentLengthStr = headers.get(Http.RequestHeaderKey.Content_Length);
if (StringUtils.isBlank(contentLengthStr)) {
contentLength = 0;
} else {
@ -109,13 +108,13 @@ public class HttpRequestDecoder {
return null;
}
if (!headers.containsKey(HttpConst.RequestHeaderKey.Host)) {
throw new AioDecodeException("there is no host header");
if (!headers.containsKey(Http.RequestHeaderKey.Host)) {
throw new ImDecodeException("there is no host header");
}
HttpRequest httpRequest = new HttpRequest(channelContext.getClientNode());
httpRequest.setChannelContext(channelContext);
httpRequest.setHttpConfig((HttpConfig) channelContext.getGroupContext().getAttribute(GroupContextKey.HTTP_SERVER_CONFIG));
httpRequest.setImChannelContext(channelContext);
httpRequest.setHttpConfig((HttpConfig) channelContext.getAttribute(Key.HTTP_SERVER_CONFIG));
httpRequest.setHeaderString(headerSb.toString());
httpRequest.setRequestLine(firstLine);
httpRequest.setHeaders(headers);
@ -136,7 +135,7 @@ public class HttpRequestDecoder {
}
public static void decodeParams(Map<String, Object[]> params, String paramsStr, String charset, ChannelContext channelContext) {
public static void decodeParams(Map<String, Object[]> params, String paramsStr, String charset, ImChannelContext channelContext) {
if (StrUtil.isBlank(paramsStr)) {
return;
}
@ -175,18 +174,18 @@ public class HttpRequestDecoder {
* @param firstLine
* @param bodyBytes
* @param channelContext
* @throws AioDecodeException
* @throws ImDecodeException
* @author WChao
*/
private static void parseBody(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, ChannelContext channelContext) throws AioDecodeException {
private static void parseBody(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, ImChannelContext channelContext) throws ImDecodeException {
parseBodyFormat(httpRequest, httpRequest.getHeaders());
RequestBodyFormat bodyFormat = httpRequest.getBodyFormat();
Http.RequestBodyFormat bodyFormat = httpRequest.getBodyFormat();
httpRequest.setBody(bodyBytes);
if (bodyFormat == RequestBodyFormat.MULTIPART) {
if (bodyFormat == Http.RequestBodyFormat.MULTIPART) {
if (log.isInfoEnabled()) {
String bodyString = null;
String bodyString;
if (bodyBytes != null && bodyBytes.length > 0) {
if (log.isDebugEnabled()) {
try {
@ -200,7 +199,7 @@ public class HttpRequestDecoder {
}
//multipart/form-data; boundary=----WebKitFormBoundaryuwYcfA2AIgxqIxA0
String initBoundary = HttpParseUtils.getPerprotyEqualValue(httpRequest.getHeaders(), HttpConst.RequestHeaderKey.Content_Type, "boundary");
String initBoundary = HttpParseUtils.getPerprotyEqualValue(httpRequest.getHeaders(), Http.RequestHeaderKey.Content_Type, "boundary");
log.debug("{}, initBoundary:{}", channelContext, initBoundary);
HttpMultiBodyDecoder.decode(httpRequest, firstLine, bodyBytes, initBoundary, channelContext);
} else {
@ -217,7 +216,7 @@ public class HttpRequestDecoder {
}
}
if (bodyFormat == RequestBodyFormat.URLENCODED) {
if (bodyFormat == Http.RequestBodyFormat.URLENCODED) {
parseUrlencoded(httpRequest, firstLine, bodyBytes, bodyString, channelContext);
}
}
@ -231,19 +230,19 @@ public class HttpRequestDecoder {
* @author WChao
*/
public static void parseBodyFormat(HttpRequest httpRequest, Map<String, String> headers) {
String Content_Type = StringUtils.lowerCase(headers.get(HttpConst.RequestHeaderKey.Content_Type));
RequestBodyFormat bodyFormat = null;
if (StringUtils.contains(Content_Type, HttpConst.RequestHeaderValue.Content_Type.application_x_www_form_urlencoded)) {
bodyFormat = RequestBodyFormat.URLENCODED;
} else if (StringUtils.contains(Content_Type, HttpConst.RequestHeaderValue.Content_Type.multipart_form_data)) {
bodyFormat = RequestBodyFormat.MULTIPART;
String Content_Type = StringUtils.lowerCase(headers.get(Http.RequestHeaderKey.Content_Type));
Http.RequestBodyFormat bodyFormat;
if (StringUtils.contains(Content_Type, Http.RequestHeaderValue.Content_Type.application_x_www_form_urlencoded)) {
bodyFormat = Http.RequestBodyFormat.URLENCODED;
} else if (StringUtils.contains(Content_Type, Http.RequestHeaderValue.Content_Type.multipart_form_data)) {
bodyFormat = Http.RequestBodyFormat.MULTIPART;
} else {
bodyFormat = RequestBodyFormat.TEXT;
bodyFormat = Http.RequestBodyFormat.TEXT;
}
httpRequest.setBodyFormat(bodyFormat);
if (StringUtils.isNotBlank(Content_Type)) {
String charset = HttpParseUtils.getPerprotyEqualValue(headers, HttpConst.RequestHeaderKey.Content_Type, "charset");
String charset = HttpParseUtils.getPerprotyEqualValue(headers, Http.RequestHeaderKey.Content_Type, "charset");
if (StringUtils.isNotBlank(charset)) {
httpRequest.setCharset(charset);
}
@ -284,7 +283,7 @@ public class HttpRequestDecoder {
* 2017年2月23日 下午1:37:51
*
*/
public static RequestLine parseRequestLine(String line, ChannelContext channelContext) throws AioDecodeException {
public static RequestLine parseRequestLine(String line, ImChannelContext channelContext) throws ImDecodeException {
try {
int index1 = line.indexOf(' ');
String _method = StringUtils.upperCase(line.substring(0, index1));
@ -322,7 +321,7 @@ public class HttpRequestDecoder {
return requestLine;
} catch (Throwable e) {
log.error(channelContext.toString(), e);
throw new AioDecodeException(e);
throw new ImDecodeException(e);
}
}
@ -331,7 +330,7 @@ public class HttpRequestDecoder {
* 形如 Content-Type : application/x-www-form-urlencoded; charset=UTF-8
* @author WChao
*/
private static void parseUrlencoded(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, String bodyString, ChannelContext channelContext) {
private static void parseUrlencoded(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, String bodyString, ImChannelContext channelContext) {
if (StringUtils.isNotBlank(bodyString)) {
decodeParams(httpRequest.getParams(), bodyString, httpRequest.getCharset(), channelContext);
}
@ -343,7 +342,7 @@ public class HttpRequestDecoder {
* @param firstLine
* @param channelContext
*/
private static void parseQueryString(HttpRequest httpRequest, RequestLine firstLine, ChannelContext channelContext) {
private static void parseQueryString(HttpRequest httpRequest, RequestLine firstLine, ImChannelContext channelContext) {
String paramStr = firstLine.getQuery();
if (StringUtils.isNotBlank(paramStr)) {
decodeParams(httpRequest.getParams(), paramStr, httpRequest.getCharset(), channelContext);

View File

@ -2,12 +2,11 @@ package org.jim.common.http;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.utils.hutool.ZipUtil;
import cn.hutool.core.util.ZipUtil;
/**
*
* @author WChao
@ -36,12 +35,11 @@ public class HttpResponse extends HttpPacket {
*/
private boolean isStaticRes = false;
private HttpRequest request = null;
private HttpRequest request;
private volatile List<Cookie> cookies = null;
// private int contentLength;
// private byte[] bodyBytes;
private String charset = HttpConst.CHARSET_NAME;
private String charset = Http.CHARSET_NAME;
/**
* 已经编码好的byte[]
@ -57,29 +55,29 @@ public class HttpResponse extends HttpPacket {
public HttpResponse(HttpRequest request, HttpConfig httpConfig) {
this.request = request;
String Connection = StringUtils.lowerCase(request.getHeader(HttpConst.RequestHeaderKey.Connection));
String Connection = StringUtils.lowerCase(request.getHeader(Http.RequestHeaderKey.Connection));
RequestLine requestLine = request.getRequestLine();
String version = requestLine.getVersion();
if ("1.0".equals(version)) {
if (StringUtils.equals(Connection, HttpConst.RequestHeaderValue.Connection.keep_alive)) {
addHeader(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.keep_alive);
addHeader(HttpConst.ResponseHeaderKey.Keep_Alive, "timeout=10, max=20");
if (StringUtils.equals(Connection, Http.RequestHeaderValue.Connection.keep_alive)) {
addHeader(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.keep_alive);
addHeader(Http.ResponseHeaderKey.Keep_Alive, "timeout=10, max=20");
} else {
addHeader(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.close);
addHeader(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.close);
}
} else {
if (StringUtils.equals(Connection, HttpConst.RequestHeaderValue.Connection.close)) {
addHeader(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.close);
if (StringUtils.equals(Connection, Http.RequestHeaderValue.Connection.close)) {
addHeader(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.close);
} else {
addHeader(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.keep_alive);
addHeader(HttpConst.ResponseHeaderKey.Keep_Alive, "timeout=10, max=20");
addHeader(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.keep_alive);
addHeader(Http.ResponseHeaderKey.Keep_Alive, "timeout=10, max=20");
}
}
//暂时先设置为短连接...防止服务器一直不释放资源;
addHeader(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.close);
addHeader(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.close);
if (httpConfig != null) {
addHeader(HttpConst.ResponseHeaderKey.Server, httpConfig.getServerInfo());
addHeader(Http.ResponseHeaderKey.Server, httpConfig.getServerInfo());
}
}
@ -137,11 +135,11 @@ public class HttpResponse extends HttpPacket {
byte[] bs2 = ZipUtil.gzip(bs);
if (bs2.length < bs.length) {
this.body = bs2;
this.addHeader(HttpConst.ResponseHeaderKey.Content_Encoding, "gzip");
this.addHeader(Http.ResponseHeaderKey.Content_Encoding, "gzip");
}
}
} else {
log.info("{} 竟然不支持gzip, {}", request.getChannelContext(), request.getHeader(HttpConst.RequestHeaderKey.User_Agent));
log.info("{} 竟然不支持gzip, {}", request.getImChannelContext(), request.getHeader(Http.RequestHeaderKey.User_Agent));
}
}

View File

@ -6,18 +6,18 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
/**
*
* @author wchao
* 2017年8月4日 上午9:41:12
*/
public class HttpResponseEncoder {
public static enum Step {
public class HttpResponseEncoder implements ImConst {
public enum Step {
firstLine, header, body
}
@ -28,13 +28,12 @@ public class HttpResponseEncoder {
/**
*
* @param httpResponse
* @param groupContext
* @param channelContext
* @param skipCookie true: 忽略掉cookie部分的编码
* @return
* @author wchao
* @author WChao
*/
public static ByteBuffer encode(HttpResponse httpResponse, GroupContext groupContext, ChannelContext channelContext, boolean skipCookie) {
public static ByteBuffer encode(HttpResponse httpResponse, ImChannelContext channelContext, boolean skipCookie) {
byte[] encodedBytes = httpResponse.getEncodedBytes();
if (encodedBytes != null) {
ByteBuffer ret = ByteBuffer.wrap(encodedBytes);
@ -56,7 +55,7 @@ public class HttpResponseEncoder {
Map<String, String> headers = httpResponse.getHeaders();
if (headers != null && headers.size() > 0) {
headers.put(HttpConst.ResponseHeaderKey.Content_Length, bodyLength + "");
headers.put(Http.ResponseHeaderKey.Content_Length, bodyLength + "");
Set<Entry<String, String>> set = headers.entrySet();
for (Entry<String, String> entry : set) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
@ -68,7 +67,7 @@ public class HttpResponseEncoder {
List<Cookie> cookies = httpResponse.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
sb.append(HttpConst.ResponseHeaderKey.Set_Cookie).append(": ");
sb.append(Http.ResponseHeaderKey.Set_Cookie).append(": ");
sb.append(cookie.toString());
sb.append("\r\n");
if (log.isInfoEnabled()) {

View File

@ -1,5 +1,6 @@
package org.jim.common.http.handler;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpRequest;
import org.jim.common.http.HttpResponse;
import org.jim.common.http.RequestLine;
@ -15,16 +16,15 @@ public interface IHttpRequestHandler {
* @param packet
* @param requestLine
* @return
* @throws Exception
* @throws ImException
* @author wchao
*/
public HttpResponse handler(HttpRequest packet, RequestLine requestLine) throws Exception;
public HttpResponse handler(HttpRequest packet, RequestLine requestLine) throws ImException;
/**
*
* @param request
* @param requestLine
* @param channelContext
* @return
* @author wchao
*/

View File

@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jim.common.ImChannelContext;
import org.jim.common.ImSessionContext;
import org.jim.common.http.HttpConfig;
@ -18,15 +19,16 @@ public class HttpSession extends ImSessionContext implements java.io.Serializabl
private Map<String, Serializable> data = new ConcurrentHashMap<>();
private String id = null;
public HttpSession() {
public HttpSession(String id){
this(id, null);
}
/**
* @author wchao
*/
public HttpSession(String id) {
public HttpSession(ImChannelContext imChannelContext){
this(null, imChannelContext);
}
public HttpSession(String id, ImChannelContext imChannelContext){
super(imChannelContext);
this.id = id;
}

View File

@ -4,7 +4,7 @@
package org.jim.common.listener;
import org.jim.common.ImConst;
import org.jim.common.ImConfig;
import org.jim.common.config.ImConfig;
/**
* @author WChao
* 2018/08/26

View File

@ -1,6 +1,7 @@
package org.jim.common.listener;
import org.tio.core.ChannelContext;
import org.jim.common.ImChannelContext;
import org.jim.common.exception.ImException;
/**
* IM绑定用户及群组监听器;
@ -10,39 +11,39 @@ import org.tio.core.ChannelContext;
public interface ImBindListener {
/**
* 绑定群组后回调该方法
* @param channelContext
* @param imChannelContext
* @param group
* @throws Exception
*/
void onAfterGroupBind(ChannelContext channelContext, String group) throws Exception;
void onAfterGroupBind(ImChannelContext imChannelContext, String group) throws ImException;
/**
* 解绑群组后回调该方法
* @param channelContext
* @param imChannelContext
* @param group
* @throws Exception
*/
void onAfterGroupUnbind(ChannelContext channelContext, String group) throws Exception;
void onAfterGroupUnbind(ImChannelContext imChannelContext, String group) throws ImException;
/**
* 绑定用户后回调该方法
* @param channelContext
* @param imChannelContext
* @param userId
* @throws Exception
*/
void onAfterUserBind(ChannelContext channelContext, String userId) throws Exception;
void onAfterUserBind(ImChannelContext imChannelContext, String userId) throws ImException;
/**
* 解绑用户后回调该方法
* @param channelContext
* @param imChannelContext
* @param userId
* @throws Exception
*/
void onAfterUserUnbind(ChannelContext channelContext, String userId) throws Exception;
void onAfterUserUnbind(ImChannelContext imChannelContext, String userId) throws Exception;
/**
* 更新用户终端协议类型及在线状态;
* @param channelContext
* @param imChannelContext
* @param terminal(wstcphttpandroidios等)
* @param status(onlineoffline)
*/
void initUserTerminal(ChannelContext channelContext , String terminal , String status);
void initUserTerminal(ImChannelContext imChannelContext , String terminal , String status);
}

View File

@ -0,0 +1,32 @@
package org.jim.common.listener;
import org.jim.common.ImChannelContext;
import org.jim.common.exception.ImException;
import org.tio.core.ChannelContext;
/**
* @ClassName ImGroupListener
* @Description TODO
* @Author WChao
* @Date 2020/1/12 14:17
* @Version 1.0
**/
public interface ImGroupListener {
/**
* 绑定群组后回调该方法
* @param imChannelContext
* @param group
* @throws ImException
* @author WChao
*/
void onAfterBind(ImChannelContext imChannelContext, String group) throws ImException;
/**
* 解绑群组后回调该方法
* @param imChannelContext
* @param group
* @throws ImException
* @author WChao
*/
void onAfterUnbind(ImChannelContext imChannelContext, String group) throws ImException;
}

View File

@ -0,0 +1,34 @@
package org.jim.common.listener;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.tio.core.ChannelContext;
import org.tio.core.intf.GroupListener;
/**
* @ClassName ImGroupListenerAdapter
* @Description TODO
* @Author WChao
* @Date 2020/1/12 14:19
* @Version 1.0
**/
public class ImGroupListenerAdapter implements GroupListener, ImConst {
private ImGroupListener imGroupListener;
public ImGroupListenerAdapter(ImGroupListener imGroupListener){
this.imGroupListener = imGroupListener;
}
@Override
public void onAfterBind(ChannelContext channelContext, String group) throws Exception {
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY);
imGroupListener.onAfterBind(imChannelContext, group);
}
@Override
public void onAfterUnbind(ChannelContext channelContext, String group) throws Exception {
ImChannelContext imChannelContext = (ImChannelContext)channelContext.get(Key.IM_CHANNEL_CONTEXT_KEY);
imGroupListener.onAfterBind(imChannelContext, group);
}
}

View File

@ -0,0 +1,75 @@
package org.jim.common.listener;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.tio.core.ChannelContext;
import org.tio.core.intf.Packet;
/**
* @ClassName ImListener
* @Description TODO
* @Author WChao
* @Date 2020/1/4 11:09
* @Version 1.0
**/
public interface ImListener {
/**
* 建链后触发本方法建链不一定成功需要关注参数isConnected
* @param imChannelContext
* @param isConnected 是否连接成功,true:表示连接成功false:表示连接失败
* @param isReconnect 是否是重连, true: 表示这是重新连接false: 表示这是第一次连接
* @throws Exception
* @author: WChao
*/
void onAfterConnected(ImChannelContext imChannelContext, boolean isConnected, boolean isReconnect) throws Exception;
/**
* 原方法名onAfterDecoded
* 解码成功后触发本方法
* @param imChannelContext
* @param packet
* @param packetSize
* @throws Exception
* @author: WChao
*/
void onAfterDecoded(ImChannelContext imChannelContext, ImPacket packet, int packetSize) throws Exception;
/**
* 接收到TCP层传过来的数据后
* @param imChannelContext
* @param receivedBytes 本次接收了多少字节
* @throws Exception
*/
void onAfterReceivedBytes(ImChannelContext imChannelContext, int receivedBytes) throws Exception;
/**
* 消息包发送之后触发本方法
* @param imChannelContext
* @param packet
* @param isSentSuccess true:发送成功false:发送失败
* @throws Exception
* @author WChao
*/
void onAfterSent(ImChannelContext imChannelContext, ImPacket packet, boolean isSentSuccess) throws Exception;
/**
* 处理一个消息包后
* @param imChannelContext
* @param packet
* @param cost 本次处理消息耗时单位毫秒
* @throws Exception
*/
void onAfterHandled(ImChannelContext imChannelContext, ImPacket packet, long cost) throws Exception;
/**
* 连接关闭前触发本方法
* @param imChannelContext
* @param throwable the throwable 有可能为空
* @param remark the remark 有可能为空
* @param isRemove
* @author WChao
* @throws Exception
*/
void onBeforeClose(ImChannelContext imChannelContext, Throwable throwable, String remark, boolean isRemove) throws Exception;
}

View File

@ -0,0 +1,31 @@
package org.jim.common.listener;
import org.jim.common.ImChannelContext;
import org.jim.common.exception.ImException;
/**
* @ClassName ImUserListener
* @Description TODO
* @Author WChao
* @Date 2020/1/12 14:24
* @Version 1.0
**/
public interface ImUserListener {
/**
* 绑定用户后回调该方法
* @param imChannelContext
* @param userId
* @throws Exception
* @author WChao
*/
void onAfterBind(ImChannelContext imChannelContext, String userId) throws ImException;
/**
* 解绑用户后回调该方法
* @param imChannelContext
* @param userId
* @throws Exception
* @author WChao
*/
void onAfterUnbind(ImChannelContext imChannelContext, String userId) throws ImException;
}

View File

@ -4,7 +4,7 @@
package org.jim.common.message;
import org.jim.common.ImConst;
import org.jim.common.ImConfig;
import org.jim.common.config.ImConfig;
/**
* @author HP

View File

@ -3,17 +3,23 @@
*/
package org.jim.common.packets;
import org.jim.common.ImStatus;
import org.jim.common.Status;
/**
* 版本: [1.0]
* 功能说明: 进入群组通知消息体
* 作者: WChao 创建时间: 2017年7月26日 下午5:14:04
*/
public class JoinGroupNotifyRespBody extends Message{
public class JoinGroupNotifyRespBody extends RespBody{
private static final long serialVersionUID = 3828976681110713803L;
private User user;
private String group;
public JoinGroupNotifyRespBody(Command command, Status status){
super(command,status);
}
public User getUser() {
return user;
}

View File

@ -3,6 +3,8 @@
*/
package org.jim.common.packets;
import org.jim.common.Status;
/**
* 版本: [1.0]
* 功能说明: 加入群组响应
@ -13,6 +15,10 @@ public class JoinGroupRespBody extends RespBody {
private static final long serialVersionUID = 6635620192752369689L;
public JoinGroupResult result;
public String group;
public JoinGroupRespBody(Command command , Status status){
super(command, status);
}
public JoinGroupResult getResult() {
return result;
}

View File

@ -15,16 +15,23 @@ import org.jim.common.utils.JsonKit;
public class RespBody implements Serializable{
private static final long serialVersionUID = 1L;
/**
* 响应状态码;
*/
private Integer code;
/**
* 响应状态信息提示;
*/
private String msg;
/**
* 响应cmd命令码;
*/
private Command command;
/**
* 响应数据;
*/
private Object data;
private Integer code;//响应状态码;
private String msg;//响应状态信息提示;
private Command command;//响应cmd命令码;
private Object data;//响应数据;
public RespBody(){}
public RespBody(Command command){
this.command = command;
}

View File

@ -11,7 +11,7 @@ import java.util.List;
/**
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年7月26日 下午3:13:47
* @author : WChao 创建时间: 2017年7月26日 下午3:13:47
*/
public class User implements Serializable{

View File

@ -1,43 +0,0 @@
/**
*
*/
package org.jim.common.protocol;
import java.nio.ByteBuffer;
import org.tio.core.ChannelContext;
/**
* @author WChao
* @date 2018-09-05 23:52:00
*/
public abstract class AbProtocol implements IProtocol {
/**
* 协议包转化器;
*/
private IConvertProtocolPacket converter;
public AbProtocol(){
this.converter = converter();
}
/**
* 根据buffer判断是否属于指定协议
* @param buffer
* @param channelContext
* @return
* @throws Throwable
*/
public abstract boolean isProtocolByBuffer(ByteBuffer buffer,ChannelContext channelContext) throws Throwable;
public boolean isProtocol(ByteBuffer buffer,ChannelContext channelContext) throws Throwable {
ByteBuffer copyByteBuffer = null;
if(buffer != null && channelContext.getAttribute() == null){
copyByteBuffer = ByteBuffer.wrap(buffer.array());
}
return isProtocolByBuffer(copyByteBuffer, channelContext);
}
public IConvertProtocolPacket getConverter() {
return converter;
}
}

View File

@ -0,0 +1,90 @@
/**
*
*/
package org.jim.common.protocol;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.exception.ImException;
/**
* @author WChao
* @date 2018-09-05 23:52:00
*/
public abstract class AbstractProtocol implements IProtocol,ImConst {
/**
* 协议包转化器;
*/
protected IProtocolConverter converter;
public AbstractProtocol(IProtocolConverter converter){
this.converter = converter;
}
/**
* 协议初始化
* @param imChannelContext
*/
protected abstract void init(ImChannelContext imChannelContext);
/**
* 根据buffer判断是否属于指定协议
* @param buffer
* @param imChannelContext
* @return
* @throws ImException
*/
protected abstract boolean validateProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException;
/**
* 根据SessionContext判断协议
* @param imSessionContext
* @return
* @throws ImException
*/
protected abstract boolean validateProtocol(ImSessionContext imSessionContext) throws ImException;
/**
* 根据imPacket判断是否属于指定协议
* @param imPacket
* @return
* @throws ImException
*/
protected abstract boolean validateProtocol(ImPacket imPacket) throws ImException;
@Override
public boolean isProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException {
ImSessionContext imSessionContext = imChannelContext.getSessionContext();
if(Objects.isNull(imSessionContext) && Objects.isNull(buffer)){
return false;
}else if(Objects.isNull(imSessionContext) && Objects.nonNull(buffer)){
boolean isProtocol = validateProtocol(ByteBuffer.wrap(buffer.array()), imChannelContext);
if(isProtocol){
init(imChannelContext);
}
return isProtocol;
}else{
return validateProtocol(imSessionContext);
}
}
@Override
public boolean isProtocol(ImPacket imPacket, ImChannelContext imChannelContext) throws ImException {
ImSessionContext sessionContext = imChannelContext.getSessionContext();
if(Objects.isNull(imPacket)){
return false;
}
boolean isProtocol = validateProtocol(imPacket);
if(isProtocol && Objects.isNull(sessionContext)) {
init(imChannelContext);
}
return isProtocol;
}
public IProtocolConverter getConverter() {
return converter;
}
}

View File

@ -3,8 +3,11 @@
*/
package org.jim.common.protocol;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.tio.core.ChannelContext;
import org.jim.common.exception.ImException;
import java.nio.ByteBuffer;
/**
* 判断协议接口
@ -16,20 +19,23 @@ public interface IProtocol {
* 协议名称
* @return :httpwstcp等
*/
public String name();
String name();
/**
* 判断是否属于指定协议
* 根据buffer判断是否属于指定协议
* @param buffer
* @param imChannelContext
* @return
* @throws ImException
*/
boolean isProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException;
/**
* 根据imPacket判断是否属于指定协议
* @param imPacket
* @param channelContext
* @param imChannelContext
* @return
* @throws Throwable
* @throws ImException
*/
public boolean isProtocol(ImPacket imPacket,ChannelContext channelContext)throws Throwable;
/**
* 获取该协议包转化器
* @return
*/
public IConvertProtocolPacket converter();
boolean isProtocol(ImPacket imPacket, ImChannelContext imChannelContext)throws ImException;
}

View File

@ -3,30 +3,29 @@
*/
package org.jim.common.protocol;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.packets.Command;
import org.tio.core.ChannelContext;
/**
* 转换不同协议消息包;
* @author WChao
*
*/
public interface IConvertProtocolPacket {
public interface IProtocolConverter {
/**
* 转化请求包
* @param body
* @param command
* @param channelContext
* @param imChannelContext
* @return
*/
public ImPacket ReqPacket(byte[] body,Command command, ChannelContext channelContext);
ImPacket ReqPacket(byte[] body,Command command, ImChannelContext imChannelContext);
/**
* 转化响应包
* @param body
* @param command
* @param channelContext
* @param imChannelContext
* @return
*/
public ImPacket RespPacket(byte[] body,Command command, ChannelContext channelContext);
ImPacket RespPacket(byte[] body,Command command, ImChannelContext imChannelContext);
}

View File

@ -5,9 +5,10 @@ import org.slf4j.LoggerFactory;
import org.jim.common.http.HttpConfig;
import org.jim.common.session.id.ISessionIdGenerator;
import cn.hutool.core.util.RandomUtil;
import java.util.UUID;
/**
* @author wchao
* @author WChao
* 2017年8月15日 上午10:53:39
*/
public class UUIDSessionIdGenerator implements ISessionIdGenerator {
@ -39,6 +40,6 @@ public class UUIDSessionIdGenerator implements ISessionIdGenerator {
*/
@Override
public String sessionId(HttpConfig httpConfig) {
return RandomUtil.randomUUID().replace("-", "");
return UUID.randomUUID().toString().replace("-", "");
}
}

View File

@ -3,27 +3,28 @@
*/
package org.jim.common.tcp;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.packets.Command;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.tio.core.ChannelContext;
import org.jim.common.protocol.IProtocolConverter;
/**
* TCP协议消息转化包
* @author WChao
*
*/
public class TcpConvertPacket implements IConvertProtocolPacket {
public class TcpConvertPacket implements IProtocolConverter {
/**
* 转TCP协议响应包;
*/
@Override
public ImPacket RespPacket(byte[] body, Command command,ChannelContext channelContext) {
Object sessionContext = channelContext.getAttribute();
if(sessionContext instanceof TcpSessionContext){//转TCP协议响应包;
public ImPacket RespPacket(byte[] body, Command command, ImChannelContext imChannelContext) {
ImSessionContext sessionContext = imChannelContext.getSessionContext();
if(sessionContext instanceof TcpSessionContext){
TcpPacket tcpPacket = new TcpPacket(command,body);
TcpServerEncoder.encode(tcpPacket, channelContext.getGroupContext(), channelContext);
TcpServerEncoder.encode(tcpPacket, imChannelContext.getImConfig(), imChannelContext);
tcpPacket.setCommand(command);
return tcpPacket;
}
@ -33,11 +34,11 @@ public class TcpConvertPacket implements IConvertProtocolPacket {
* 转TCP协议请求包;
*/
@Override
public ImPacket ReqPacket(byte[] body, Command command,ChannelContext channelContext) {
Object sessionContext = channelContext.getAttribute();
if(sessionContext instanceof TcpSessionContext){//转TCP协议请求包;
public ImPacket ReqPacket(byte[] body, Command command, ImChannelContext channelContext) {
Object sessionContext = channelContext.getSessionContext();
if(sessionContext instanceof TcpSessionContext){
TcpPacket tcpPacket = new TcpPacket(command,body);
TcpServerEncoder.encode(tcpPacket, channelContext.getGroupContext(), channelContext);
TcpServerEncoder.encode(tcpPacket, channelContext.getImConfig(), channelContext);
tcpPacket.setCommand(command);
return tcpPacket;
}

View File

@ -5,20 +5,24 @@ package org.jim.common.tcp;
import java.nio.ByteBuffer;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.Protocol;
import org.jim.common.protocol.AbProtocol;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.jim.common.exception.ImException;
import org.jim.common.protocol.AbstractProtocol;
import org.jim.common.protocol.IProtocolConverter;
import org.jim.common.utils.ImUtils;
import org.tio.core.ChannelContext;
/**
* Tcp协议判断器
* @author WChao
*
*/
public class TcpProtocol extends AbProtocol {
public class TcpProtocol extends AbstractProtocol {
public TcpProtocol(IProtocolConverter converter){
super(converter);
}
@Override
public String name() {
@ -26,39 +30,31 @@ public class TcpProtocol extends AbProtocol {
}
@Override
public boolean isProtocolByBuffer(ByteBuffer buffer,ChannelContext channelContext) throws Throwable {
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
if(imSessionContext != null && imSessionContext instanceof TcpSessionContext) {
return true;
protected void init(ImChannelContext imChannelContext) {
imChannelContext.setSessionContext(new TcpSessionContext(imChannelContext));
ImUtils.setClient(imChannelContext);
}
if(buffer != null){
//获取第一个字节协议版本号;
byte version = buffer.get();
//TCP协议;
if(version == Protocol.VERSION){
channelContext.setAttribute(new TcpSessionContext());
ImUtils.setClient(channelContext);
@Override
public boolean validateProtocol(ImSessionContext imSessionContext) throws ImException {
if(imSessionContext instanceof TcpSessionContext){
return true;
}
}
return false;
}
@Override
public IConvertProtocolPacket converter() {
return new TcpConvertPacket();
public boolean validateProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException {
//获取第一个字节协议版本号,TCP协议;
if(buffer.get() == Protocol.VERSION){
return true;
}
return false;
}
@Override
public boolean isProtocol(ImPacket imPacket,ChannelContext channelContext) throws Throwable {
if(imPacket == null) {
return false;
}
public boolean validateProtocol(ImPacket imPacket) throws ImException {
if(imPacket instanceof TcpPacket){
Object sessionContext = channelContext.getAttribute();
if(sessionContext == null){
channelContext.setAttribute(new TcpSessionContext());
}
return true;
}
return false;

View File

@ -6,11 +6,11 @@ package org.jim.common.tcp;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.jim.common.Protocol;
import org.tio.core.ChannelContext;
import org.tio.core.exception.AioDecodeException;
import org.jim.common.exception.ImDecodeException;
import org.jim.common.packets.Command;
/**
@ -18,11 +18,11 @@ import org.jim.common.packets.Command;
* 功能说明:
* @author : WChao 创建时间: 2017年8月21日 下午3:08:04
*/
public class TcpServerDecoder {
public class TcpServerDecoder implements ImConst {
private static Logger logger = Logger.getLogger(TcpServerDecoder.class);
public static TcpPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException{
public static TcpPacket decode(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImDecodeException {
//校验协议头
if(!isHeaderLength(buffer)) {
return null;
@ -30,7 +30,7 @@ public class TcpServerDecoder {
//获取第一个字节协议版本号;
byte version = buffer.get();
if(version != Protocol.VERSION){
throw new AioDecodeException(ImStatus.C10013.getText());
throw new ImDecodeException(ImStatus.C10013.getText());
}
//标志位
byte maskByte = buffer.get();
@ -42,13 +42,13 @@ public class TcpServerDecoder {
//cmd命令码
byte cmdByte = buffer.get();
if(Command.forNumber(cmdByte) == null){
throw new AioDecodeException(ImStatus.C10014.getText());
throw new ImDecodeException(ImStatus.C10014.getText());
}
int bodyLen = buffer.getInt();
//数据不正确则抛出AioDecodeException异常
//数据不正确则抛出ImDecodeException异常
if (bodyLen < 0)
{
throw new AioDecodeException("bodyLength [" + bodyLen + "] is not right, remote:" + channelContext.getClientNode());
throw new ImDecodeException("bodyLength [" + bodyLen + "] is not right, remote:" + imChannelContext.getClientNode());
}
int readableLength = buffer.limit() - buffer.position();
int validateBodyLen = readableLength - bodyLen;
@ -72,7 +72,7 @@ public class TcpServerDecoder {
if(synSeq > 0){
tcpPacket.setSynSeq(synSeq);
try {
channelContext.getGroupContext().getAioHandler().handler(tcpPacket, channelContext);
//channelContext.getGroupContext().getAioHandler().handler(tcpPacket, channelContext);
} catch (Exception e) {
logger.error("同步发送解码调用handler异常!"+e);
}
@ -83,9 +83,9 @@ public class TcpServerDecoder {
* 判断是否符合协议头长度
* @param buffer
* @return
* @throws AioDecodeException
* @throws ImDecodeException
*/
private static boolean isHeaderLength(ByteBuffer buffer) throws AioDecodeException{
private static boolean isHeaderLength(ByteBuffer buffer) throws ImDecodeException{
int readableLength = buffer.limit() - buffer.position();
if(readableLength == 0) {
return false;

View File

@ -6,18 +6,18 @@ package org.jim.common.tcp;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.Protocol;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.jim.common.config.ImConfig;
/**
* 版本: [1.0]
* 功能说明:
* @author : WChao 创建时间: 2017年8月21日 下午4:00:31
*/
public class TcpServerEncoder {
public class TcpServerEncoder implements ImConst {
public static ByteBuffer encode(TcpPacket tcpPacket, GroupContext groupContext, ChannelContext channelContext){
public static ByteBuffer encode(TcpPacket tcpPacket, ImConfig imConfig, ImChannelContext imChannelContext){
int bodyLen = 0;
byte[] body = tcpPacket.getBody();
if (body != null)
@ -53,7 +53,7 @@ public class TcpServerEncoder {
allLen += 1+4+bodyLen;
ByteBuffer buffer = ByteBuffer.allocate(allLen);
//设置字节序
ByteOrder byteOrder = groupContext == null ? ByteOrder.BIG_ENDIAN : groupContext.getByteOrder();
ByteOrder byteOrder = imConfig == null ? ByteOrder.BIG_ENDIAN : imConfig.getTioConfig().getByteOrder();
buffer.order(byteOrder);
buffer.put(tcpPacket.getVersion());
buffer.put(tcpPacket.getMask());

View File

@ -3,12 +3,17 @@
*/
package org.jim.common.tcp;
import org.jim.common.ImChannelContext;
import org.jim.common.ImSessionContext;
/**
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年9月6日 下午5:03:15
* @author : WChao 创建时间: 2017年9月6日 下午5:03:15
*/
public class TcpSessionContext extends ImSessionContext {
public TcpSessionContext(ImChannelContext imChannelContext){
super(imChannelContext);
}
}

View File

@ -5,12 +5,9 @@ package org.jim.common.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.jim.common.ImConst;
import org.jim.common.ImAio;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.ImStatus;
import org.jim.common.config.Config;
import org.jim.common.*;
import org.jim.common.config.ImConfig;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpConst;
import org.jim.common.packets.ChatBody;
import org.jim.common.packets.Command;
@ -18,7 +15,7 @@ import org.jim.common.packets.RespBody;
import org.jim.common.packets.User;
import org.jim.common.session.id.impl.UUIDSessionIdGenerator;
import org.tio.core.ChannelContext;
import org.tio.utils.lock.SetWithLock;
/**
* IM聊天命令工具类
* @date 2018-09-05 23:29:30
@ -32,19 +29,19 @@ public class ChatKit {
/**
* 转换为聊天消息结构;
* @param body
* @param channelContext
* @param imChannelContext
* @return
*/
public static ChatBody toChatBody(byte[] body,ChannelContext channelContext){
public static ChatBody toChatBody(byte[] body,ImChannelContext imChannelContext){
ChatBody chatReqBody = parseChatBody(body);
if(chatReqBody != null){
if(StringUtils.isEmpty(chatReqBody.getFrom())){
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
ImSessionContext imSessionContext = imChannelContext.getSessionContext();
User user = imSessionContext.getClient().getUser();
if(user != null){
chatReqBody.setFrom(user.getNick());
}else{
chatReqBody.setFrom(channelContext.getId());
chatReqBody.setFrom(imChannelContext.getId());
}
}
}
@ -62,7 +59,7 @@ public class ChatKit {
}
ChatBody chatReqBody = null;
try{
String text = new String(body,HttpConst.CHARSET_NAME);
String text = new String(body,ImConst.CHARSET);
chatReqBody = JsonKit.toBean(text,ChatBody.class);
if(chatReqBody != null){
if(chatReqBody.getCreateTime() == null) {
@ -89,67 +86,28 @@ public class ChatKit {
return null;
}
try {
return parseChatBody(bodyStr.getBytes(HttpConst.CHARSET_NAME));
return parseChatBody(bodyStr.getBytes(ImConst.CHARSET));
} catch (Exception e) {
log.error(e);
}
return null;
}
/**
* 聊天数据格式不正确响应包
* @param channelContext
* @return imPacket
* @throws Exception
*/
public static ImPacket dataInCorrectRespPacket(ChannelContext channelContext) throws Exception{
RespBody chatDataInCorrectRespPacket = new RespBody(Command.COMMAND_CHAT_RESP,ImStatus.C10002);
ImPacket respPacket = ImKit.ConvertRespPacket(chatDataInCorrectRespPacket, channelContext);
respPacket.setStatus(ImStatus.C10002);
return respPacket;
}
/**
* 聊天发送成功响应包
* @param channelContext
* @return imPacket
* @throws Exception
*/
public static ImPacket sendSuccessRespPacket(ChannelContext channelContext) throws Exception{
RespBody chatDataInCorrectRespPacket = new RespBody(Command.COMMAND_CHAT_RESP,ImStatus.C10000);
ImPacket respPacket = ImKit.ConvertRespPacket(chatDataInCorrectRespPacket, channelContext);
respPacket.setStatus(ImStatus.C10000);
return respPacket;
}
/**
* 聊天用户不在线响应包
* @param channelContext
* @return
* @throws Exception
*/
public static ImPacket offlineRespPacket(ChannelContext channelContext) throws Exception{
RespBody chatDataInCorrectRespPacket = new RespBody(Command.COMMAND_CHAT_RESP,ImStatus.C10001);
ImPacket respPacket = ImKit.ConvertRespPacket(chatDataInCorrectRespPacket, channelContext);
respPacket.setStatus(ImStatus.C10001);
return respPacket;
}
/**
* 判断用户是否在线
* @param userId
* @param imConfig
* @return
*/
public static boolean isOnline(String userId ,Config imConfig){
boolean isStore = ImConst.ON.equals(imConfig.getIsStore());
public static boolean isOnline(String userId , ImConfig imConfig){
/* boolean isStore = ImConst.ON.equals(imConfig.getIsStore());
if(isStore){
return imConfig.getMessageHelper().isOnline(userId);
}
SetWithLock<ChannelContext> toChannelContexts = ImAio.getChannelContextsByUserId(userId);
SetWithLock<ChannelContext> toChannelContexts = Jim.getChannelContextsByUserId(userId);
if(toChannelContexts != null && toChannelContexts.size() > 0){
return true;
}
}*/
return false;
}

View File

@ -4,23 +4,23 @@
package org.jim.common.utils;
import cn.hutool.core.bean.BeanUtil;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.jim.common.http.HttpConst;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpProtocol;
import org.jim.common.packets.Command;
import org.jim.common.packets.Group;
import org.jim.common.packets.RespBody;
import org.jim.common.packets.User;
import org.jim.common.protocol.AbProtocol;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.jim.common.protocol.AbstractProtocol;
import org.jim.common.protocol.IProtocolConverter;
import org.jim.common.protocol.IProtocol;
import org.jim.common.tcp.TcpProtocol;
import org.jim.common.ws.WsProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -36,108 +36,7 @@ import java.util.Map.Entry;
public class ImKit {
private static Logger logger = LoggerFactory.getLogger(ImKit.class);
private static Map<String,AbProtocol> protocols = new HashMap<String,AbProtocol>();
static{
WsProtocol wsProtocol = new WsProtocol();
TcpProtocol tcpProtocol = new TcpProtocol();
HttpProtocol httpProtocol = new HttpProtocol();
protocols.put(wsProtocol.name(),wsProtocol);
protocols.put(tcpProtocol.name(),tcpProtocol);
protocols.put(httpProtocol.name(),httpProtocol);
}
/**
* 功能描述[转换不同协议响应包]
* @authorWChao 创建时间: 2017年9月21日 下午3:21:54
* @param respBody
* @param channelContext
* @return
*
*/
public static ImPacket ConvertRespPacket(RespBody respBody, ChannelContext channelContext){
ImPacket respPacket = null;
if(respBody == null) {
return respPacket;
}
byte[] body;
try {
body = respBody.toString().getBytes(HttpConst.CHARSET_NAME);
respPacket = ConvertRespPacket(body,respBody.getCommand(), channelContext);
} catch (Exception e) {
logger.error(e.getMessage());
}
return respPacket;
}
/**
* 功能描述[转换不同协议响应包]
* @authorWChao 创建时间: 2017年9月21日 下午3:21:54
* @param body
* @param channelContext
* @return
*
*/
public static ImPacket ConvertRespPacket(byte[] body,Command command, ChannelContext channelContext){
ImPacket respPacket = null;
IConvertProtocolPacket converter = (IConvertProtocolPacket)channelContext.getAttribute(ImConst.CONVERTER);
if(converter != null){
return converter.RespPacket(body, command, channelContext);
}
for(Entry<String,AbProtocol> entry : protocols.entrySet()){
AbProtocol protocol = entry.getValue();
IConvertProtocolPacket converterObj = protocol.getConverter();
respPacket = converterObj.RespPacket(body, command, channelContext);
if(respPacket != null){
channelContext.setAttribute(ImConst.CONVERTER, converterObj);
return respPacket;
}
}
return respPacket;
}
public static ImPacket ConvertRespPacket(ImPacket imPacket,Command command, ChannelContext channelContext){
ImPacket respPacket = ConvertRespPacket(imPacket.getBody(), command, channelContext);
if(respPacket == null){
for(Entry<String,AbProtocol> entry : protocols.entrySet()){
AbProtocol protocol = entry.getValue();
try{
boolean isProtocol = protocol.isProtocol(imPacket,channelContext);
if(isProtocol){
IConvertProtocolPacket converterObj = protocol.getConverter();
respPacket = converterObj.RespPacket(imPacket.getBody(), command, channelContext);
if(respPacket != null){
channelContext.setAttribute(ImConst.CONVERTER, converterObj);
return respPacket;
}
}
}catch(Throwable e){
logger.error(e.toString());
}
}
}
return respPacket;
}
/**
* 获取所属终端协议;
* @param byteBuffer
* @param channelContext
*/
public static IProtocol protocol(ByteBuffer byteBuffer , ChannelContext channelContext){
for(Entry<String,AbProtocol> entry : protocols.entrySet()){
AbProtocol protocol = entry.getValue();
try {
boolean isProtocol = protocol.isProtocol(byteBuffer, channelContext);
if(isProtocol){
return protocol;
}
} catch (Throwable e) {
logger.error(e.toString(),e);
}
}
return null;
}
/**
* 格式化状态码消息响应体;
@ -145,15 +44,7 @@ public class ImKit {
* @return
*/
public static byte[] toImStatusBody(ImStatus status){
return JsonKit.toJsonBytes(new RespBody().setCode(status.getCode()).setMsg(status.getDescription()+" "+status.getText()));
}
/**
* 获取所有协议判断器目前内置(HttpProtocolWebSocketProtocolHttpProtocol)
* @return
*/
public static Map<String, AbProtocol> getProtocols() {
return protocols;
return JsonKit.toJsonBytes(new RespBody(status).setMsg(status.getDescription()+" "+status.getText()));
}
/**

View File

@ -3,6 +3,7 @@ package org.jim.common.utils;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImChannelContext;
import org.jim.common.ImSessionContext;
import org.tio.core.ChannelContext;
import org.jim.common.packets.Client;
@ -16,10 +17,10 @@ public class ImUtils {
* 设置Client对象到ImSessionContext中
* @param channelContext
* @return
* @author: wchao
* @author: WChao
*/
public static Client setClient(ChannelContext channelContext) {
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
public static Client setClient(ImChannelContext channelContext) {
ImSessionContext imSessionContext = channelContext.getSessionContext();
Client client = imSessionContext.getClient();
if (client == null) {
client = new Client();
@ -28,7 +29,6 @@ public class ImUtils {
client.setPort(channelContext.getClientNode().getPort());
imSessionContext.setClient(client);
}
return client;
}

View File

@ -1,5 +1,6 @@
package org.jim.common.ws;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.tio.core.ChannelContext;
import org.jim.common.ws.WsRequestPacket;
@ -13,43 +14,43 @@ public interface IWsMsgHandler
/**
*
* @param packet
* @param channelContext
* @param imChannelContext
* @return
*
* @author: WChao
* 2016年11月18日 下午1:08:45
*
*/
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception;
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws Exception;
/**
* @param websocketPacket
* @param wsPacket
* @param text
* @param channelContext
* @param imChannelContext
* @return 可以是WsResponsePacketbyte[]ByteBufferString或null如果是null框架不会回消息
* @throws Exception
* @author: WChao
*/
Object onText(WsRequestPacket wsPacket, String text, ChannelContext channelContext) throws Exception;
Object onText(WsRequestPacket wsPacket, String text, ImChannelContext imChannelContext) throws Exception;
/**
*
* @param websocketPacket
* @param webSocketPacket
* @param bytes
* @param channelContext
* @return 可以是WsResponsePacketbyte[]ByteBufferString或null如果是null框架不会回消息
* @throws Exception
* @author: WChao
*/
Object onClose(WsRequestPacket websocketPacket, byte[] bytes, ChannelContext channelContext) throws Exception;
Object onClose(WsRequestPacket webSocketPacket, byte[] bytes, ImChannelContext channelContext) throws Exception;
/**
*
* @param websocketPacket
* @param webSocketPacket
* @param bytes
* @param channelContext
* @param imChannelContext
* @return 可以是WsResponsePacketbyte[]ByteBufferString或null如果是null框架不会回消息
* @throws Exception
* @author: WChao
*/
Object onBytes(WsRequestPacket websocketPacket, byte[] bytes, ChannelContext channelContext) throws Exception;
Object onBytes(WsRequestPacket webSocketPacket, byte[] bytes, ImChannelContext imChannelContext) throws Exception;
}

View File

@ -0,0 +1,71 @@
package org.jim.common.ws;
import org.jim.common.ImConst;
import org.jim.common.config.ImConfig;
import org.jim.common.http.HttpConst;
/**
*
* 版本: [1.0]
* 功能说明:
* @author : WChao 创建时间: 2017年9月6日 上午11:11:26
*/
public class WsConfig implements ImConst {
private String charset = Http.CHARSET_NAME;
private IWsMsgHandler wsMsgHandler;
public WsConfig(){};
public WsConfig(IWsMsgHandler wsMsgHandler){
this.wsMsgHandler = wsMsgHandler;
}
public static WsConfig.Builder newBuilder(){
return new WsConfig.Builder();
}
/**
* @return the charset
*/
public String getCharset() {
return charset;
}
/**
* @param charset the charset to set
*/
public void setCharset(String charset) {
this.charset = charset;
}
public IWsMsgHandler getWsMsgHandler() {
return wsMsgHandler;
}
public void setWsMsgHandler(IWsMsgHandler wsMsgHandler) {
this.wsMsgHandler = wsMsgHandler;
}
public static class Builder{
private IWsMsgHandler wsMsgHandler;
private String charset;
public Builder wsMsgHandler(IWsMsgHandler wsMsgHandler){
this.wsMsgHandler = wsMsgHandler;
return this;
}
public Builder charset(String charset){
this.charset = charset;
return this;
}
public WsConfig build(){
WsConfig wsConfig = new WsConfig(wsMsgHandler);
wsConfig.setCharset(charset);
return wsConfig;
}
}
}

View File

@ -3,24 +3,24 @@
*/
package org.jim.common.ws;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.packets.Command;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.tio.core.ChannelContext;
import org.jim.common.protocol.IProtocolConverter;
/**
* Ws协议消息转化包
* @author WChao
*
*/
public class WsConvertPacket implements IConvertProtocolPacket {
public class WsConvertPacket implements IProtocolConverter {
/**
* WebSocket响应包;
*/
@Override
public ImPacket RespPacket(byte[] body, Command command,ChannelContext channelContext) {
Object sessionContext = channelContext.getAttribute();
public ImPacket RespPacket(byte[] body, Command command, ImChannelContext channelContext) {
ImSessionContext sessionContext = channelContext.getSessionContext();
//转ws协议响应包;
if(sessionContext instanceof WsSessionContext){
WsResponsePacket wsResponsePacket = new WsResponsePacket();
@ -33,7 +33,7 @@ public class WsConvertPacket implements IConvertProtocolPacket {
}
@Override
public ImPacket ReqPacket(byte[] body, Command command,ChannelContext channelContext) {
public ImPacket ReqPacket(byte[] body, Command command, ImChannelContext channelContext) {
return null;
}

View File

@ -5,65 +5,62 @@ package org.jim.common.ws;
import java.nio.ByteBuffer;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.Protocol;
import org.jim.common.http.HttpConst;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpRequest;
import org.jim.common.http.HttpRequestDecoder;
import org.jim.common.protocol.AbProtocol;
import org.jim.common.protocol.IConvertProtocolPacket;
import org.jim.common.protocol.AbstractProtocol;
import org.jim.common.protocol.IProtocolConverter;
import org.jim.common.utils.ImUtils;
import org.tio.core.ChannelContext;
/**
* WebSocket协议判断器
* @author WChao
*
*/
public class WsProtocol extends AbProtocol {
public class WsProtocol extends AbstractProtocol {
@Override
public String name() {
return Protocol.WEBSOCKET;
return Protocol.WEB_SOCKET;
}
public WsProtocol(IProtocolConverter converter){
super(converter);
}
@Override
public boolean isProtocolByBuffer(ByteBuffer buffer,ChannelContext channelContext) throws Throwable {
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
if(imSessionContext != null && imSessionContext instanceof WsSessionContext) {
protected void init(ImChannelContext imChannelContext) {
imChannelContext.setSessionContext(new WsSessionContext(imChannelContext));
ImUtils.setClient(imChannelContext);
}
@Override
protected boolean validateProtocol(ImSessionContext imSessionContext) throws ImException {
if(imSessionContext instanceof WsSessionContext) {
return true;
}
return false;
}
@Override
protected boolean validateProtocol(ByteBuffer buffer, ImChannelContext imChannelContext) throws ImException {
//第一次连接;
if(buffer != null){
HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext,false);
if(request.getHeaders().get(HttpConst.RequestHeaderKey.Sec_WebSocket_Key) != null)
HttpRequest request = HttpRequestDecoder.decode(buffer, imChannelContext,false);
if(request.getHeaders().get(Http.RequestHeaderKey.Sec_WebSocket_Key) != null)
{
channelContext.setAttribute(new WsSessionContext());
ImUtils.setClient(channelContext);
return true;
}
}
return false;
}
@Override
public IConvertProtocolPacket converter() {
return new WsConvertPacket();
}
@Override
public boolean isProtocol(ImPacket imPacket,ChannelContext channelContext) throws Throwable {
if(imPacket == null) {
return false;
}
public boolean validateProtocol(ImPacket imPacket) throws ImException {
if(imPacket instanceof WsPacket){
Object sessionContext = channelContext.getAttribute();
if(sessionContext == null){
channelContext.setAttribute(new WsSessionContext());
}
return true;
}
return false;
}
}

View File

@ -1,42 +0,0 @@
package org.jim.common.ws;
import org.jim.common.config.Config;
import org.jim.common.http.HttpConst;
/**
*
* 版本: [1.0]
* 功能说明:
* @author : WChao 创建时间: 2017年9月6日 上午11:11:26
*/
public class WsServerConfig extends Config{
private String charset = HttpConst.CHARSET_NAME;
private IWsMsgHandler wsMsgHandler;
public WsServerConfig(){};
public WsServerConfig(Integer bindPort) {
this.bindPort = bindPort;
}
/**
* @return the charset
*/
public String getCharset() {
return charset;
}
/**
* @param charset the charset to set
*/
public void setCharset(String charset) {
this.charset = charset;
}
public IWsMsgHandler getWsMsgHandler() {
return wsMsgHandler;
}
public void setWsMsgHandler(IWsMsgHandler wsMsgHandler) {
this.wsMsgHandler = wsMsgHandler;
}
}

View File

@ -8,12 +8,12 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.exception.ImDecodeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.utils.ByteBufferUtils;
import org.jim.common.http.HttpConst;
import org.jim.common.http.HttpRequest;
import org.jim.common.http.HttpResponse;
import org.jim.common.http.HttpResponseStatus;
@ -24,15 +24,15 @@ import org.jim.common.utils.SHA1Util;
* @author wchao
* 2017年7月30日 上午10:10:50
*/
public class WsServerDecoder {
public static enum Step {
public class WsServerDecoder implements ImConst{
public enum Step {
header, remain_header, data,
}
private static Logger log = LoggerFactory.getLogger(WsServerDecoder.class);
public static WsRequestPacket decode(ByteBuffer buf, ChannelContext channelContext) throws AioDecodeException {
WsSessionContext imSessionContext = (WsSessionContext) channelContext.getAttribute();
public static WsRequestPacket decode(ByteBuffer buf, ImChannelContext imChannelContext) throws ImDecodeException {
WsSessionContext imSessionContext = (WsSessionContext) imChannelContext.getSessionContext();
List<byte[]> lastParts = imSessionContext.getLastParts();
//第一阶段解析
@ -70,7 +70,7 @@ public class WsServerDecoder {
// Client data must be masked
if (!hasMask) { //第9为为mask,必须为1
//throw new AioDecodeException("websocket client data must be masked");
//throw new ImDecodeException("websocket client data must be masked");
} else {
headLength += 4;
}
@ -83,7 +83,7 @@ public class WsServerDecoder {
return null;
}
payloadLength = ByteBufferUtils.readUB2WithBigEdian(buf);
log.info("{} payloadLengthFlag: 126payloadLength {}", channelContext, payloadLength);
log.info("{} payloadLengthFlag: 126payloadLength {}", imChannelContext, payloadLength);
} else if (payloadLength == 127) { //127读8个字节,后8个字节为payloadLength
headLength += 8;
@ -92,11 +92,11 @@ public class WsServerDecoder {
}
payloadLength = (int) buf.getLong();
log.info("{} payloadLengthFlag: 127payloadLength {}", channelContext, payloadLength);
log.info("{} payloadLengthFlag: 127payloadLength {}", imChannelContext, payloadLength);
}
if (payloadLength < 0 || payloadLength > WsPacket.MAX_BODY_LENGTH) {
throw new AioDecodeException("body length(" + payloadLength + ") is not right");
throw new ImDecodeException("body length(" + payloadLength + ") is not right");
}
if (readableLength < headLength + payloadLength) {
@ -173,14 +173,14 @@ public class WsServerDecoder {
* 本方法改编自baseio: https://git.oschina.net/generallycloud/baseio<br>
* 感谢开源作者的付出
* @param request
* @param channelContext
* @param imChannelContext
* @return
* @author wchao
*/
public static HttpResponse updateWebSocketProtocol(HttpRequest request, ChannelContext channelContext) {
public static HttpResponse updateWebSocketProtocol(HttpRequest request, ImChannelContext imChannelContext) {
Map<String, String> headers = request.getHeaders();
String Sec_WebSocket_Key = headers.get(HttpConst.RequestHeaderKey.Sec_WebSocket_Key);
String Sec_WebSocket_Key = headers.get(Http.RequestHeaderKey.Sec_WebSocket_Key);
if (StringUtils.isNotBlank(Sec_WebSocket_Key)) {
String Sec_WebSocket_Key_Magic = Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -191,9 +191,9 @@ public class WsServerDecoder {
httpResponse.setStatus(HttpResponseStatus.C101);
Map<String, String> respHeaders = new HashMap<>();
respHeaders.put(HttpConst.ResponseHeaderKey.Connection, HttpConst.ResponseHeaderValue.Connection.Upgrade);
respHeaders.put(HttpConst.ResponseHeaderKey.Upgrade, "WebSocket");
respHeaders.put(HttpConst.ResponseHeaderKey.Sec_WebSocket_Accept, acceptKey);
respHeaders.put(Http.ResponseHeaderKey.Connection, Http.ResponseHeaderValue.Connection.Upgrade);
respHeaders.put(Http.ResponseHeaderKey.Upgrade, "WebSocket");
respHeaders.put(Http.ResponseHeaderKey.Sec_WebSocket_Accept, acceptKey);
httpResponse.setHeaders(respHeaders);
return httpResponse;
}

View File

@ -2,10 +2,11 @@ package org.jim.common.ws;
import java.nio.ByteBuffer;
import org.jim.common.ImChannelContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.TioConfig;
import org.tio.core.utils.ByteBufferUtils;
/**
@ -34,8 +35,9 @@ public class WsServerEncoder {
}
}
public static ByteBuffer encode(WsResponsePacket wsResponsePacket, GroupContext groupContext, ChannelContext channelContext) {
byte[] imBody = wsResponsePacket.getBody();//就是ws的body不包括ws的头
public static ByteBuffer encode(WsResponsePacket wsResponsePacket, ImChannelContext imChannelContext) {
//就是ws的body不包括ws的头
byte[] imBody = wsResponsePacket.getBody();
int wsBodyLength = 0;
if (imBody != null) {

View File

@ -2,6 +2,7 @@ package org.jim.common.ws;
import java.util.List;
import org.jim.common.ImChannelContext;
import org.jim.common.ImSessionContext;
import org.jim.common.http.HttpRequest;
import org.jim.common.http.HttpResponse;
@ -18,12 +19,12 @@ public class WsSessionContext extends ImSessionContext {
private boolean isHandshaked = false;
/**
* websocket 握手请求包
* webSocket 握手请求包
*/
private HttpRequest handshakeRequestPacket = null;
/**
* websocket 握手响应包
* webSocket 握手响应包
*/
private HttpResponse handshakeResponsePacket = null;
/**
@ -33,20 +34,21 @@ public class WsSessionContext extends ImSessionContext {
/**
* ws响应状态包;
*/
private WsResponsePacket wsResponsPacket = null;
private WsResponsePacket wsResponsePacket = null;
//websocket 协议用到的有时候数据包是分几个到的注意那个fin字段本im暂时不支持
/**
* webSocket 协议用到的有时候数据包是分几个到的注意那个fin字段本im暂时不支持
*/
private List<byte[]> lastParts = null;
/**
*
*
* @author wchao
* 2017年2月21日 上午10:27:54
*
*/
public WsSessionContext() {
public WsSessionContext(ImChannelContext imChannelContext) {
super(imChannelContext);
}
/**
@ -85,7 +87,7 @@ public class WsSessionContext extends ImSessionContext {
}
/**
* @param httpHandshakePacket the httpHandshakePacket to set
* @param handshakeRequestPacket the httpHandshakePacket to set
*/
public void setHandshakeRequestPacket(HttpRequest handshakeRequestPacket) {
this.handshakeRequestPacket = handshakeRequestPacket;
@ -113,11 +115,11 @@ public class WsSessionContext extends ImSessionContext {
this.wsRequestPacket = wsRequestPacket;
}
public WsResponsePacket getWsResponsPacket() {
return wsResponsPacket;
public WsResponsePacket getWsResponsePacket() {
return wsResponsePacket;
}
public void setWsResponsPacket(WsResponsePacket wsResponsPacket) {
this.wsResponsPacket = wsResponsPacket;
public void setWsResponsePacket(WsResponsePacket wsResponsePacket) {
this.wsResponsePacket = wsResponsePacket;
}
}

View File

@ -1,9 +1,9 @@
package org.jim.common.ws;
import org.tio.core.intf.TioUuid;
import org.tio.utils.hutool.Snowflake;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author WChao
@ -13,11 +13,11 @@ public class WsTioUuid implements TioUuid {
private Snowflake snowflake;
public WsTioUuid() {
snowflake = new Snowflake(RandomUtil.randomInt(1, 30), RandomUtil.randomInt(1, 30));
snowflake = new Snowflake(ThreadLocalRandom.current().nextInt(1, 30), ThreadLocalRandom.current().nextInt(1, 30));
}
public WsTioUuid(long workerId, long datacenterId) {
snowflake = new Snowflake(workerId, datacenterId);
public WsTioUuid(long workerId, long dataCenterId) {
snowflake = new Snowflake(workerId, dataCenterId);
}
/**

View File

@ -5,7 +5,7 @@
<artifactId>jim-parent</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<version>2.6.0.v20190114-RELEASE</version>
<version>3.0.0.v20200101-RELEASE</version>
<description>Jim-parent is the dependent parent project of J-IM communication establishment.</description>
<url>http://www.j-im.org/</url>
<licenses>
@ -34,9 +34,9 @@
<module>../jim-server-demo</module>
</modules>
<properties>
<jim.version>2.6.0.v20190114-RELEASE</jim.version>
<tio-core.version>2.4.0.v20180508-RELEASE</tio-core.version>
<tio-utils.version>2.4.0.v20180508-RELEASE</tio-utils.version>
<jim.version>3.0.0.v20200101-RELEASE</jim.version>
<tio-core.version>3.5.8.v20191228-RELEASE</tio-core.version>
<tio-utils.version>3.5.8.v20191228-RELEASE</tio-utils.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@ -62,6 +62,7 @@
<jedis.version>2.7.3</jedis.version>
<redisson.version>3.7.0</redisson.version>
<j2cache.version>2.3.21-release</j2cache.version>
<guava.version>23.0</guava.version>
</properties>
<repositories>
@ -250,7 +251,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>nl.basjes.parse.useragent</groupId>
@ -267,6 +268,11 @@
<artifactId>j2cache-core</artifactId>
<version>${j2cache.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 插件配置 -->

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.j-im</groupId>
<artifactId>jim-parent</artifactId>
<version>2.6.0.v20190114-RELEASE</version>
<version>3.0.0.v20200101-RELEASE</version>
<relativePath>../jim-parent/pom.xml</relativePath>
</parent>
<dependencies>

View File

@ -3,17 +3,16 @@
*/
package org.jim.server.demo;
import com.jfinal.kit.PropKit;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImConfig;
import org.jim.common.ImConst;
import org.jim.common.config.PropertyImConfigBuilder;
import org.jim.common.config.ImConfig;
import org.jim.common.packets.Command;
import org.jim.common.utils.PropUtil;
import org.jim.server.ImServerStarter;
import org.jim.server.command.CommandManager;
import org.jim.server.command.handler.HandshakeReqHandler;
import org.jim.server.command.handler.LoginReqHandler;
import org.jim.server.config.ImServerConfig;
import org.jim.server.config.PropertyImServerConfigBuilder;
import org.jim.server.demo.command.DemoWsHandshakeProcessor;
import org.jim.server.demo.listener.ImDemoGroupListener;
import org.jim.server.demo.service.LoginServiceProcessor;
@ -26,12 +25,12 @@ import org.tio.core.ssl.SslConfig;
public class ImServerDemoStart {
public static void main(String[] args)throws Exception{
ImConfig imConfig = new PropertyImConfigBuilder("jim.properties").build();
ImServerConfig imServerConfig = new PropertyImServerConfigBuilder("jim.properties").build();
//初始化SSL;(开启SSL之前,你要保证你有SSL证书哦...)
initSsl(imConfig);
initSsl(imServerConfig);
//设置群组监听器非必须根据需要自己选择性实现;
imConfig.setImGroupListener(new ImDemoGroupListener());
ImServerStarter imServerStarter = new ImServerStarter(imConfig);
imServerConfig.setImGroupListener(new ImDemoGroupListener());
ImServerStarter imServerStarter = new ImServerStarter(imServerConfig);
/*****************start 以下处理器根据业务需要自行添加与扩展每个Command都可以添加扩展,此处为demo中处理**********************************/
HandshakeReqHandler handshakeReqHandler = CommandManager.getCommand(Command.COMMAND_HANDSHAKE_REQ, HandshakeReqHandler.class);
//添加自定义握手处理器;
@ -45,19 +44,19 @@ public class ImServerDemoStart {
/**
* 开启SSL之前你要保证你有SSL证书哦
* @param imConfig
* @param imServerConfig
* @throws Exception
*/
private static void initSsl(ImConfig imConfig) throws Exception {
private static void initSsl(ImServerConfig imServerConfig) throws Exception {
//开启SSL
if(ImConst.ON.equals(imConfig.getIsSSL())){
if(ImConfig.Const.ON.equals(imServerConfig.getIsSSL())){
String keyStorePath = PropUtil.get("jim.key.store.path");
String keyStoreFile = keyStorePath;
String trustStoreFile = keyStorePath;
String keyStorePwd = PropUtil.get("jim.key.store.pwd");
if (StringUtils.isNotBlank(keyStoreFile) && StringUtils.isNotBlank(trustStoreFile)) {
SslConfig sslConfig = SslConfig.forServer(keyStoreFile, trustStoreFile, keyStorePwd);
imConfig.setSslConfig(sslConfig);
imServerConfig.setSslConfig(sslConfig);
}
}
}

View File

@ -3,9 +3,11 @@
*/
package org.jim.server.demo.command;
import org.jim.common.ImAio;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.http.HttpConst;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpRequest;
import org.jim.common.packets.Command;
import org.jim.common.packets.LoginReqBody;
@ -13,7 +15,6 @@ import org.jim.common.utils.JsonKit;
import org.jim.server.command.CommandManager;
import org.jim.server.command.handler.LoginReqHandler;
import org.jim.server.command.handler.processor.handshake.WsHandshakeProcessor;
import org.tio.core.ChannelContext;
/**
* @author WChao
*
@ -21,7 +22,7 @@ import org.tio.core.ChannelContext;
public class DemoWsHandshakeProcessor extends WsHandshakeProcessor {
@Override
public void onAfterHandshaked(ImPacket packet, ChannelContext channelContext) throws Exception {
public void onAfterHandshake(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
LoginReqHandler loginHandler = (LoginReqHandler)CommandManager.getCommand(Command.COMMAND_LOGIN_REQ);
HttpRequest request = (HttpRequest)packet;
String username = request.getParams().get("username") == null ? null : (String)request.getParams().get("username")[0];
@ -30,10 +31,14 @@ public class DemoWsHandshakeProcessor extends WsHandshakeProcessor {
LoginReqBody loginBody = new LoginReqBody(username,password,token);
byte[] loginBytes = JsonKit.toJsonBytes(loginBody);
request.setBody(loginBytes);
request.setBodyString(new String(loginBytes,HttpConst.CHARSET_NAME));
ImPacket loginRespPacket = loginHandler.handler(request, channelContext);
try{
request.setBodyString(new String(loginBytes, ImConst.CHARSET));
}catch (Exception e){
throw new ImException(e);
}
ImPacket loginRespPacket = loginHandler.handler(request, imChannelContext);
if(loginRespPacket != null){
ImAio.send(channelContext, loginRespPacket);
Jim.send(imChannelContext, loginRespPacket);
}
}
}

View File

@ -1,20 +1,28 @@
package org.jim.server.demo.listener;
import org.jim.common.ImAio;
import org.jim.common.ImChannelContext;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.exception.ImException;
import org.jim.common.listener.ImGroupListener;
import org.tio.core.ChannelContext;
import org.jim.common.packets.Client;
import org.jim.common.packets.Command;
import org.jim.common.packets.ExitGroupNotifyRespBody;
import org.jim.common.packets.RespBody;
import org.jim.common.packets.User;
import org.jim.server.listener.ImGroupListener;
/**
* @author WChao
* 2017年5月13日 下午10:38:36
*/
public class ImDemoGroupListener extends ImGroupListener{
public class ImDemoGroupListener implements ImGroupListener {
@Override
public void onAfterBind(ImChannelContext imChannelContext, String group) throws ImException {
System.out.println("===绑定成功:"+group);
}
/**
* @param channelContext
* @param group
@ -22,12 +30,11 @@ public class ImDemoGroupListener extends ImGroupListener{
* @author: WChao
*/
@Override
public void onAfterUnbind(ChannelContext channelContext, String group) throws Exception {
public void onAfterUnbind(ImChannelContext channelContext, String group) throws ImException {
//发退出房间通知 COMMAND_EXIT_GROUP_NOTIFY_RESP
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
ExitGroupNotifyRespBody exitGroupNotifyRespBody = new ExitGroupNotifyRespBody();
exitGroupNotifyRespBody.setGroup(group);
Client client = imSessionContext.getClient();
Client client = channelContext.getSessionContext().getClient();
if(client == null){
return;
}
@ -40,7 +47,7 @@ public class ImDemoGroupListener extends ImGroupListener{
RespBody respBody = new RespBody(Command.COMMAND_EXIT_GROUP_NOTIFY_RESP,exitGroupNotifyRespBody);
ImPacket imPacket = new ImPacket(Command.COMMAND_EXIT_GROUP_NOTIFY_RESP, respBody.toByte());
ImAio.sendToGroup(group, imPacket);
Jim.sendToGroup(group, imPacket);
}
}

View File

@ -5,10 +5,7 @@ package org.jim.server.demo.service;
import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.ImStatus;
import org.jim.common.*;
import org.jim.common.http.HttpConst;
import org.jim.common.packets.Command;
import org.jim.common.packets.Group;
@ -51,17 +48,17 @@ public class LoginServiceProcessor implements LoginCmdProcessor {
/**
* 根据用户名和密码获取用户
* @param loginname
* @param loginName
* @param password
* @return
* @author: WChao
*/
public User getUser(String loginname, String password) {
String text = loginname+password;
public User getUser(String loginName, String password) {
String text = loginName+password;
String key = ImConst.AUTH_KEY;
String token = Md5.sign(text, key, HttpConst.CHARSET_NAME);
String token = Md5.sign(text, key, CHARSET);
User user = getUser(token);
user.setId(loginname);
user.setId(loginName);
return user;
}
/**
@ -124,17 +121,17 @@ public class LoginServiceProcessor implements LoginCmdProcessor {
* 当登陆失败时设置user属性需要为空相反登陆成功user属性是必须非空的;
*/
@Override
public LoginRespBody doLogin(LoginReqBody loginReqBody , ChannelContext channelContext) {
String loginname = loginReqBody.getLoginname();
public LoginRespBody doLogin(LoginReqBody loginReqBody , ImChannelContext channelContext) {
String loginName = loginReqBody.getLoginname();
String password = loginReqBody.getPassword();
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
ImSessionContext imSessionContext = channelContext.getSessionContext();
String handshakeToken = imSessionContext.getToken();
User user;
LoginRespBody loginRespBody;
if (!StringUtils.isBlank(handshakeToken)) {
user = this.getUser(handshakeToken);
}else{
user = this.getUser(loginname, password);
user = this.getUser(loginName, password);
}
if(user == null){
loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10008);
@ -145,12 +142,13 @@ public class LoginServiceProcessor implements LoginCmdProcessor {
}
@Override
public void onSuccess(ChannelContext channelContext) {
public void onSuccess(ImChannelContext channelContext) {
logger.info("登录成功回调方法");
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
ImSessionContext imSessionContext = channelContext.getSessionContext();
User user = imSessionContext.getClient().getUser();
if(user.getGroups() != null){
for(Group group : user.getGroups()){//发送加入群组通知
//发送加入群组通知
for(Group group : user.getGroups()){
ImPacket groupPacket = new ImPacket(Command.COMMAND_JOIN_GROUP_REQ,JsonKit.toJsonBytes(group));
try {
JoinGroupReqHandler joinGroupReqHandler = CommandManager.getCommand(Command.COMMAND_JOIN_GROUP_REQ, JoinGroupReqHandler.class);
@ -163,7 +161,7 @@ public class LoginServiceProcessor implements LoginCmdProcessor {
}
@Override
public boolean isProtocol(ChannelContext channelContext) {
public boolean isProtocol(ImChannelContext channelContext) {
return true;
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.j-im</groupId>
<artifactId>jim-parent</artifactId>
<version>2.6.0.v20190114-RELEASE</version>
<version>3.0.0.v20200101-RELEASE</version>
<relativePath>../jim-parent/pom.xml</relativePath>
</parent>
<dependencies>

View File

@ -0,0 +1,45 @@
package org.jim.server;
import org.jim.common.ImChannelContext;
import org.jim.common.ImHandler;
import org.jim.common.config.ImConfig;
import org.jim.common.protocol.AbstractProtocol;
import org.jim.common.protocol.IProtocol;
import org.jim.server.handler.AbstractProtocolHandler;
import org.tio.core.ChannelContext;
import org.tio.utils.thread.pool.AbstractQueueRunnable;
/**
* @ClassName ImServerChannelContext
* @Description TODO
* @Author WChao
* @Date 2020/1/5 23:56
* @Version 1.0
**/
public class ImServerChannelContext extends ImChannelContext {
protected AbstractQueueRunnable msgQue;
protected AbstractProtocolHandler protocolHandler;
public ImServerChannelContext(ImConfig imConfig, ChannelContext tioChannelContext) {
super(imConfig, tioChannelContext);
}
public AbstractQueueRunnable getMsgQue() {
return msgQue;
}
public void setMsgQue(AbstractQueueRunnable msgQue) {
this.msgQue = msgQue;
}
public AbstractProtocolHandler getProtocolHandler() {
return protocolHandler;
}
public void setProtocolHandler(AbstractProtocolHandler protocolHandler) {
this.protocolHandler = protocolHandler;
}
}

View File

@ -3,31 +3,19 @@
*/
package org.jim.server;
import org.jim.common.ImConst;
import org.jim.common.ImAio;
import org.jim.common.ImConfig;
import org.jim.common.cache.redis.RedissonTemplate;
import org.jim.common.cluster.redis.RedisCluster;
import org.jim.common.cluster.redis.RedisClusterConfig;
import org.jim.server.command.CommandManager;
import org.jim.server.handler.ImServerAioHandler;
import org.jim.server.handler.ProtocolHandlerManager;
import org.jim.server.listener.ImServerAioListener;
import org.jim.common.config.ImConfig;
import org.jim.server.handler.ImServerHandler;
import org.jim.server.listener.ImServerListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.server.ServerGroupContext;
import org.tio.utils.Threads;
import org.tio.utils.thread.pool.DefaultThreadFactory;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
* @author WChao
*
*/
public class ImServerGroupContext extends ServerGroupContext{
public class ImServerGroupContext /*extends TioConfig */{
private Logger log = LoggerFactory.getLogger(ImServerGroupContext.class);
@ -39,8 +27,8 @@ public class ImServerGroupContext extends ServerGroupContext{
protected SynThreadPoolExecutor timExecutor = null;
public ImServerGroupContext(ImConfig imConfig , ImServerAioHandler imServerAioHandler,ImServerAioListener imServerAioListener) {
super(imServerAioHandler, imServerAioListener);
public ImServerGroupContext(ImConfig imConfig , ImServerHandler imServerAioHandler, ImServerListenerAdapter imServerAioListener) {
/*super(imServerAioHandler, imServerAioListener);
this.imConfig = imConfig;
this.setHeartbeatTimeout(imConfig.getHeartbeatTimeout());
//是否开启集群
@ -67,7 +55,7 @@ public class ImServerGroupContext extends ServerGroupContext{
imConfig.setGroupContext(this);
ProtocolHandlerManager.init(imConfig);
CommandManager.init(imConfig);
ImAio.imConfig = imConfig;
Jim.imConfig = imConfig;*/
}
public SynThreadPoolExecutor getTimExecutor() {

View File

@ -4,72 +4,60 @@
package org.jim.server;
import org.jim.common.ImConst;
import org.jim.common.ImConfig;
import org.jim.server.handler.ImServerAioHandler;
import org.jim.common.cache.redis.RedissonTemplate;
import org.jim.common.cluster.redis.RedisCluster;
import org.jim.common.cluster.redis.RedisClusterConfig;
import org.jim.common.config.ImConfig;
import org.jim.server.config.ImServerConfig;
import org.jim.server.handler.ProtocolManager;
import org.jim.server.helper.redis.RedisMessageHelper;
import org.jim.server.listener.ImGroupListener;
import org.jim.server.listener.ImServerAioListener;
import org.tio.core.intf.GroupListener;
import org.tio.core.ssl.SslConfig;
import org.tio.server.AioServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.server.ServerTioConfig;
import org.tio.server.TioServer;
import java.io.IOException;
/**
*
* J-IM服务端启动类
* @author WChao
*
*/
public class ImServerStarter {
private ImServerAioHandler imAioHandler = null;
private ImServerAioListener imAioListener = null;
private ImServerGroupContext imServerGroupContext = null;
private ImGroupListener imGroupListener = null;
private AioServer aioServer = null;
private ImConfig imConfig = null;
private static Logger log = LoggerFactory.getLogger(ImServerStarter.class);
private TioServer tioServer = null;
private ImServerConfig imServerConfig;
public ImServerStarter(ImConfig imConfig){
this(imConfig,null);
public ImServerStarter(ImServerConfig imServerConfig){
this.imServerConfig = imServerConfig;
init(imServerConfig);
}
public ImServerStarter(ImConfig imConfig,ImServerAioListener imAioListener){
this.imConfig = imConfig;
this.imAioListener = imAioListener;
init();
public void init(ImServerConfig imServerConfig){
ImConfig.Global.set(imServerConfig);
System.setProperty("tio.default.read.buffer.size", String.valueOf(imServerConfig.getReadBufferSize()));
if(imServerConfig.getMessageHelper() == null){
imServerConfig.setMessageHelper(new RedisMessageHelper());
}
public void init(){
System.setProperty("tio.default.read.buffer.size", String.valueOf(imConfig.getReadBufferSize()));
imAioHandler = new ImServerAioHandler(imConfig) ;
if(imAioListener == null){
imAioListener = new ImServerAioListener(imConfig);
}
GroupListener groupListener = imConfig.getImGroupListener();
if(groupListener == null){
imConfig.setImGroupListener(new ImGroupListener());
}
this.imGroupListener = (ImGroupListener)imConfig.getImGroupListener();
imServerGroupContext = new ImServerGroupContext(imConfig,imAioHandler, imAioListener);
imServerGroupContext.setGroupListener(imGroupListener);
if(imConfig.getMessageHelper() == null){
imConfig.setMessageHelper(new RedisMessageHelper(imConfig));
}
//开启SSL
if(ImConst.ON.equals(imConfig.getIsSSL())){
SslConfig sslConfig = imConfig.getSslConfig();
if(sslConfig != null) {
imServerGroupContext.setSslConfig(sslConfig);
if(ImConfig.Const.ON.equals(imServerConfig.getIsCluster())){
imServerConfig.setIsStore(ImConfig.Const.ON);
if(imServerConfig.getCluster() == null){
try{
imServerConfig.setCluster(new RedisCluster(RedisClusterConfig.newInstance(ImConst.Topic.REDIS_CLUSTER_TOPIC_SUFFIX, RedissonTemplate.me().getRedissonClient())));
}catch(Exception e){
log.error("连接集群配置出现异常,请检查!",e);
}
}
aioServer = new AioServer(imServerGroupContext);
}
ProtocolManager.init();
tioServer = new TioServer((ServerTioConfig)imServerConfig.getTioConfig());
}
public void start() throws IOException {
aioServer.start(this.imConfig.getBindIp(),this.imConfig.getBindPort());
tioServer.start(this.imServerConfig.getBindIp(), this.imServerConfig.getBindPort());
}
public void stop(){
aioServer.stop();
tioServer.stop();
}
}

View File

@ -3,24 +3,21 @@
*/
package org.jim.server.command;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.common.ImConfig;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.config.ImConfig;
import org.jim.server.command.handler.processor.CmdProcessor;
import org.tio.core.ChannelContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jim.server.config.ImServerConfig;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
/**
* 版本: [1.0]
* 功能说明:
* @author: WChao 创建时间: 2017年9月11日 下午2:07:44
*/
public abstract class AbstractCmdHandler implements CmdHandler {
public abstract class AbstractCmdHandler implements CmdHandler, ImConst {
/**
* 不同协议cmd处理命令如(wssocket自定义协议)握手心跳命令等.
*/
@ -28,14 +25,10 @@ public abstract class AbstractCmdHandler implements CmdHandler {
/**
* IM相关配置类
*/
protected ImConfig imConfig;
protected ImServerConfig imConfig = ImConfig.Global.get();
public AbstractCmdHandler() {};
public AbstractCmdHandler(ImConfig imConfig) {
this.imConfig = imConfig;
}
public AbstractCmdHandler addProcessor(CmdProcessor processor){
this.processors.put(processor.name(), processor);
return this;
@ -43,16 +36,16 @@ public abstract class AbstractCmdHandler implements CmdHandler {
/**
* 根据当前通道所属协议获取cmd业务处理器
* @param channelContext
* @param imChannelContext
* @return
*/
public <T> List<T> getProcessor(ChannelContext channelContext,Class<T> clazz){
public <T> List<T> getProcessor(ImChannelContext imChannelContext, Class<T> clazz){
List<T> processorList = null;
for(Entry<String,CmdProcessor> processorEntry : processors.entrySet()){
CmdProcessor processor = processorEntry.getValue();
if(processor.isProtocol(channelContext)){
if(processor.isProtocol(imChannelContext)){
if(CollectionUtils.isEmpty(processorList)){
processorList = Lists.newArrayList();
processorList = new ArrayList<>();
}
processorList.add((T)processor);
}
@ -71,7 +64,7 @@ public abstract class AbstractCmdHandler implements CmdHandler {
CmdProcessor processor = processorEntry.getValue();
if(name.equals(processor.name())){
if(CollectionUtils.isEmpty(processorList)){
processorList = Lists.newArrayList();
processorList = new ArrayList<>();
}
processorList.add((T)processor);
}
@ -89,7 +82,7 @@ public abstract class AbstractCmdHandler implements CmdHandler {
for(Entry<String,CmdProcessor> processorEntry : processors.entrySet()){
CmdProcessor processor = processorEntry.getValue();
if(CollectionUtils.isEmpty(processorList)){
processorList = Lists.newArrayList();
processorList = new ArrayList<>();
}
if(CollectionUtils.isEmpty(names)){
processorList.add((T)processor);
@ -106,12 +99,8 @@ public abstract class AbstractCmdHandler implements CmdHandler {
return processors.remove(name);
}
public ImConfig getImConfig() {
public ImServerConfig getImConfig() {
return imConfig;
}
public void setImConfig(ImConfig imConfig) {
this.imConfig = imConfig;
}
}

View File

@ -1,29 +1,31 @@
package org.jim.server.command;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.tio.core.ChannelContext;
import org.jim.common.exception.ImException;
import org.jim.common.packets.Command;
/**
*
* 版本: [1.0]
* 功能说明:
* 作者: WChao 创建时间: 2017年9月8日 下午4:29:38
* @author : WChao 创建时间: 2017年9月8日 下午4:29:38
*/
public interface CmdHandler
{
/**
* 功能描述[命令主键]
* 创建者WChao 创建时间: 2017年7月17日 下午2:31:51
* @authorWChao 创建时间: 2017年7月17日 下午2:31:51
* @return
*/
public Command command();
Command command();
/**
* @param packet
* @param channelContext
* 处理Cmd命令
* @param imPacket
* @param imChannelContext
* @return
* @throws Exception
* @throws ImException
* @date 2016年11月18日 下午1:08:45
*/
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception;
ImPacket handler(ImPacket imPacket, ImChannelContext imChannelContext) throws ImException;
}

View File

@ -3,9 +3,9 @@
*/
package org.jim.server.command;
import org.jim.common.ImConfig;
import org.jim.common.packets.Command;
import org.jim.server.command.handler.processor.CmdProcessor;
import org.jim.server.config.ImServerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,8 +25,6 @@ public class CommandManager{
private static Map<Integer, AbstractCmdHandler> handlerMap = new HashMap<>();
private static Logger LOG = LoggerFactory.getLogger(CommandManager.class);
private static ImConfig imConfig;
private CommandManager(){};
static{
@ -63,7 +61,6 @@ public class CommandManager{
}
if(handlerMap.get(cmd_number) == null)
{
imCommandHandler.setImConfig(imConfig);
return handlerMap.put(cmd_number,imCommandHandler);
}
return null;
@ -95,18 +92,4 @@ public class CommandManager{
}
return handlerMap.get(command.getNumber());
}
public static void init(ImConfig config) {
imConfig = config;
for(Entry<Integer,AbstractCmdHandler> entry : handlerMap.entrySet()){
try {
entry.getValue().setImConfig(imConfig);
} catch (Exception e) {
LOG.error(e.toString());
}
}
}
public static ImConfig getImConfig() {
return imConfig;
}
}

View File

@ -1,15 +1,18 @@
package org.jim.server.command.handler;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.tio.core.ChannelContext;
import org.jim.common.exception.ImException;
import org.jim.common.packets.AuthReqBody;
import org.jim.common.packets.Command;
import org.jim.common.packets.RespBody;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.handler.ProtocolManager;
/**
*
* 版本: [1.0]
@ -19,11 +22,11 @@ import org.jim.server.command.AbstractCmdHandler;
public class AuthReqHandler extends AbstractCmdHandler
{
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception
public ImPacket handler(ImPacket packet, ImChannelContext channelContext) throws ImException
{
if (packet.getBody() == null) {
RespBody respBody = new RespBody(Command.COMMAND_AUTH_RESP,ImStatus.C10010);
return ImKit.ConvertRespPacket(respBody, channelContext);
return ProtocolManager.Converter.respPacket(respBody, channelContext);
}
AuthReqBody authReqBody = JsonKit.toBean(packet.getBody(), AuthReqBody.class);
String token = authReqBody.getToken() == null ? "" : authReqBody.getToken();
@ -31,7 +34,7 @@ public class AuthReqHandler extends AbstractCmdHandler
authReqBody.setToken(data);
authReqBody.setCmd(null);
RespBody respBody = new RespBody(Command.COMMAND_AUTH_RESP,ImStatus.C10009).setData(authReqBody);
return ImKit.ConvertRespPacket(respBody, channelContext);
return ProtocolManager.Converter.respPacket(respBody, channelContext);
}
@Override

View File

@ -1,21 +1,18 @@
package org.jim.server.command.handler;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.common.ImAio;
import org.jim.common.ImConst;
import org.jim.common.ImChannelContext;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.exception.ImException;
import org.jim.common.packets.ChatBody;
import org.jim.common.packets.ChatType;
import org.jim.common.packets.Command;
import org.jim.common.packets.RespBody;
import org.jim.common.utils.ChatKit;
import org.jim.server.ImServerChannelContext;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.handler.processor.chat.ChatCmdProcessor;
import org.jim.server.command.handler.processor.chat.MsgQueueRunnable;
import org.tio.core.ChannelContext;
import java.util.List;
import org.jim.server.handler.ProtocolManager;
import org.tio.utils.thread.pool.AbstractQueueRunnable;
/**
* 版本: [1.0]
@ -25,26 +22,27 @@ import java.util.List;
public class ChatReqHandler extends AbstractCmdHandler {
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket handler(ImPacket packet, ImChannelContext channelContext) throws ImException {
ImServerChannelContext imServerChannelContext = (ImServerChannelContext)channelContext;
if (packet.getBody() == null) {
throw new Exception("body is null");
throw new ImException("body is null");
}
ChatBody chatBody = ChatKit.toChatBody(packet.getBody(), channelContext);
packet.setBody(chatBody.toByte());
//聊天数据格式不正确
if(chatBody == null || chatBody.getChatType() == null){
ImPacket respChatPacket = ChatKit.dataInCorrectRespPacket(channelContext);
ImPacket respChatPacket = ProtocolManager.Packet.dataInCorrect(channelContext);
return respChatPacket;
}
List<ChatCmdProcessor> chatProcessors = this.getProcessorNotEqualName(Sets.newHashSet(ImConst.BASE_ASYNC_CHAT_MESSAGE_PROCESSOR),ChatCmdProcessor.class);
/*List<ChatCmdProcessor> chatProcessors = this.getProcessorNotEqualName(new HashSet<>(ImConst.BASE_ASYNC_CHAT_MESSAGE_PROCESSOR),ChatCmdProcessor.class);
if(CollectionUtils.isNotEmpty(chatProcessors)){
chatProcessors.forEach(chatProcessor -> chatProcessor.handler(packet,channelContext));
}
}*/
//异步调用业务处理消息接口
if(ChatType.forNumber(chatBody.getChatType()) != null){
MsgQueueRunnable msgQueueRunnable = (MsgQueueRunnable)channelContext.getAttribute(ImConst.CHAT_QUEUE);
AbstractQueueRunnable msgQueueRunnable = imServerChannelContext.getMsgQue();
msgQueueRunnable.addMsg(packet);
msgQueueRunnable.getExecutor().execute(msgQueueRunnable);
msgQueueRunnable.executor.execute(msgQueueRunnable);
}
ImPacket chatPacket = new ImPacket(Command.COMMAND_CHAT_REQ,new RespBody(Command.COMMAND_CHAT_REQ,chatBody).toByte());
//设置同步序列号;
@ -53,19 +51,19 @@ public class ChatReqHandler extends AbstractCmdHandler {
if(ChatType.CHAT_TYPE_PRIVATE.getNumber() == chatBody.getChatType()){
String toId = chatBody.getTo();
if(ChatKit.isOnline(toId,imConfig)){
ImAio.sendToUser(toId, chatPacket);
Jim.sendToUser(toId, chatPacket);
//发送成功响应包
return ChatKit.sendSuccessRespPacket(channelContext);
return ProtocolManager.Packet.success(channelContext);
}else{
//用户不在线响应包
return ChatKit.offlineRespPacket(channelContext);
return ProtocolManager.Packet.offline(channelContext);
}
//群聊
}else if(ChatType.CHAT_TYPE_PUBLIC.getNumber() == chatBody.getChatType()){
String group_id = chatBody.getGroup_id();
ImAio.sendToGroup(group_id, chatPacket);
Jim.sendToGroup(group_id, chatPacket);
//发送成功响应包
return ChatKit.sendSuccessRespPacket(channelContext);
return ProtocolManager.Packet.success(channelContext);
}
return null;
}

View File

@ -1,15 +1,17 @@
package org.jim.server.command.handler;
import org.jim.common.ImAio;
import org.jim.common.ImChannelContext;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.jim.common.exception.ImException;
import org.jim.common.packets.CloseReqBody;
import org.jim.common.packets.Command;
import org.jim.common.packets.RespBody;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.AbstractCmdHandler;
import org.tio.core.ChannelContext;
import org.jim.server.handler.ProtocolManager;
/**
* 版本: [1.0]
@ -19,22 +21,23 @@ import org.tio.core.ChannelContext;
public class CloseReqHandler extends AbstractCmdHandler
{
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws ImException
{
CloseReqBody closeReqBody = null;
try{
closeReqBody = JsonKit.toBean(packet.getBody(),CloseReqBody.class);
}catch (Exception e) {
//关闭请求消息格式不正确
return ImKit.ConvertRespPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10020), channelContext);
return ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10020), imChannelContext);
}
Jim.bSend(imChannelContext, ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10021), imChannelContext));
if(closeReqBody == null || closeReqBody.getUserid() == null){
ImAio.remove(channelContext, "收到关闭请求");
Jim.remove(imChannelContext, "收到关闭请求");
}else{
String userId = closeReqBody.getUserid();
ImAio.remove(userId, "收到关闭请求!");
Jim.remove(userId, "收到关闭请求!");
}
return ImKit.ConvertRespPacket(new RespBody(Command.COMMAND_CLOSE_REQ, ImStatus.C10021), channelContext);
return null;
}
@Override

View File

@ -1,16 +1,15 @@
package org.jim.server.command.handler;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.common.ImAio;
import org.jim.common.ImChannelContext;
import org.jim.common.Jim;
import org.jim.common.ImPacket;
import org.jim.common.exception.ImException;
import org.jim.common.http.HttpRequest;
import org.jim.common.packets.Command;
import org.jim.common.ws.WsSessionContext;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.handler.processor.handshake.HandshakeCmdProcessor;
import org.tio.core.Aio;
import org.tio.core.ChannelContext;
import java.util.List;
/**
@ -21,22 +20,22 @@ import java.util.List;
public class HandshakeReqHandler extends AbstractCmdHandler {
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket handler(ImPacket packet, ImChannelContext channelContext) throws ImException {
List<HandshakeCmdProcessor> handshakeProcessors = this.getProcessor(channelContext,HandshakeCmdProcessor.class);
if(CollectionUtils.isEmpty(handshakeProcessors)){
Aio.remove(channelContext, "没有对应的握手协议处理器HandshakeProCmd...");
Jim.remove(channelContext, "没有对应的握手协议处理器HandshakeProCmd...");
return null;
}
HandshakeCmdProcessor handShakeProCmdHandler = handshakeProcessors.get(0);
ImPacket handShakePacket = handShakeProCmdHandler.handshake(packet, channelContext);
if (handShakePacket == null) {
Aio.remove(channelContext, "业务层不同意握手");
Jim.remove(channelContext, "业务层不同意握手");
return null;
}
ImAio.send(channelContext, handShakePacket);
WsSessionContext wsSessionContext = (WsSessionContext) channelContext.getAttribute();
Jim.send(channelContext, handShakePacket);
WsSessionContext wsSessionContext = (WsSessionContext) channelContext.getSessionContext();
HttpRequest request = wsSessionContext.getHandshakeRequestPacket();
handShakeProCmdHandler.onAfterHandshaked(request, channelContext);
handShakeProCmdHandler.onAfterHandshake(request, channelContext);
return null;
}

View File

@ -1,13 +1,13 @@
package org.jim.server.command.handler;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.Protocol;
import org.jim.common.exception.ImException;
import org.jim.common.packets.Command;
import org.jim.common.packets.HeartbeatBody;
import org.jim.common.packets.RespBody;
import org.jim.common.utils.ImKit;
import org.jim.server.command.AbstractCmdHandler;
import org.tio.core.ChannelContext;
import org.jim.server.handler.ProtocolManager;
/**
*
@ -15,10 +15,10 @@ import org.tio.core.ChannelContext;
public class HeartbeatReqHandler extends AbstractCmdHandler
{
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception
public ImPacket handler(ImPacket packet, ImChannelContext channelContext) throws ImException
{
RespBody heartbeatBody = new RespBody(Command.COMMAND_HEARTBEAT_REQ).setData(new HeartbeatBody(Protocol.HEARTBEAT_BYTE));
ImPacket heartbeatPacket = ImKit.ConvertRespPacket(heartbeatBody,channelContext);
ImPacket heartbeatPacket = ProtocolManager.Converter.respPacket(heartbeatBody,channelContext);
return heartbeatPacket;
}

View File

@ -2,15 +2,12 @@ package org.jim.server.command.handler;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImAio;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.ImStatus;
import org.jim.common.*;
import org.jim.common.exception.ImException;
import org.jim.server.command.handler.processor.group.GroupCmdProcessor;
import org.jim.server.handler.ProtocolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Aio;
import org.tio.core.ChannelContext;
import org.jim.common.packets.Command;
import org.jim.common.packets.Group;
import org.jim.common.packets.JoinGroupNotifyRespBody;
@ -18,10 +15,8 @@ import org.jim.common.packets.JoinGroupRespBody;
import org.jim.common.packets.JoinGroupResult;
import org.jim.common.packets.RespBody;
import org.jim.common.packets.User;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.AbstractCmdHandler;
import java.util.List;
/**
@ -35,20 +30,20 @@ public class JoinGroupReqHandler extends AbstractCmdHandler {
private static Logger log = LoggerFactory.getLogger(JoinGroupReqHandler.class);
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
//绑定群组;
ImPacket joinGroupRespPacket = bindGroup(packet, channelContext);
ImPacket joinGroupRespPacket = bindGroup(packet, imChannelContext);
//发送进房间通知;
joinGroupNotify(packet,channelContext);
joinGroupNotify(packet,imChannelContext);
return joinGroupRespPacket;
}
/**
* 发送进房间通知;
* @param packet
* @param channelContext
* @param imChannelContext
*/
public void joinGroupNotify(ImPacket packet,ChannelContext channelContext){
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
public void joinGroupNotify(ImPacket packet, ImChannelContext imChannelContext)throws ImException{
ImSessionContext imSessionContext = imChannelContext.getSessionContext();
User clientUser = imSessionContext.getClient().getUser();
User notifyUser = new User(clientUser.getId(),clientUser.getNick());
@ -56,55 +51,52 @@ public class JoinGroupReqHandler extends AbstractCmdHandler {
Group joinGroup = JsonKit.toBean(packet.getBody(),Group.class);
String groupId = joinGroup.getGroup_id();
//发进房间通知 COMMAND_JOIN_GROUP_NOTIFY_RESP
JoinGroupNotifyRespBody joinGroupNotifyRespBody = new JoinGroupNotifyRespBody().setGroup(groupId).setUser(notifyUser);
RespBody notifyRespBody = new RespBody(Command.COMMAND_JOIN_GROUP_NOTIFY_RESP,joinGroupNotifyRespBody);
ImPacket joinGroupNotifyRespPacket = new ImPacket(Command.COMMAND_JOIN_GROUP_NOTIFY_RESP,notifyRespBody.toByte());
ImAio.sendToGroup(groupId, joinGroupNotifyRespPacket);
JoinGroupNotifyRespBody joinGroupNotifyRespBody = new JoinGroupNotifyRespBody(Command.COMMAND_JOIN_GROUP_NOTIFY_RESP, ImStatus.C10011)
.setGroup(groupId)
.setUser(notifyUser);
Jim.sendToGroup(groupId, ProtocolManager.Converter.respPacket(joinGroupNotifyRespBody,imChannelContext));
}
/**
* 绑定群组
* @param packet
* @param channelContext
* @param imChannelContext
* @return
* @throws Exception
* @throws ImException
*/
public ImPacket bindGroup(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket bindGroup(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
if (packet.getBody() == null) {
throw new Exception("body is null");
throw new ImException("body is null");
}
Group joinGroup = JsonKit.toBean(packet.getBody(),Group.class);
String groupId = joinGroup.getGroup_id();
if (StringUtils.isBlank(groupId)) {
log.error("group is null,{}", channelContext);
Aio.close(channelContext, "group is null when join group");
log.error("group is null,{}", imChannelContext);
Jim.close(imChannelContext, "group is null when join group");
return null;
}
//实际绑定之前执行处理器动作
List<GroupCmdProcessor> groupCmdProcessors = this.getProcessor(channelContext, GroupCmdProcessor.class);
List<GroupCmdProcessor> groupCmdProcessors = this.getProcessor(imChannelContext, GroupCmdProcessor.class);
//先定义为操作成功
JoinGroupResult joinGroupResult = JoinGroupResult.JOIN_GROUP_RESULT_OK;
JoinGroupRespBody joinGroupRespBody = new JoinGroupRespBody();
JoinGroupRespBody joinGroupRespBody = new JoinGroupRespBody(Command.COMMAND_JOIN_GROUP_RESP,ImStatus.C10011);
//当有群组处理器时候才会去处理
if(CollectionUtils.isNotEmpty(groupCmdProcessors)){
GroupCmdProcessor groupCmdProcessor = groupCmdProcessors.get(0);
joinGroupRespBody = groupCmdProcessor.join(joinGroup, channelContext);
joinGroupRespBody = groupCmdProcessor.join(joinGroup, imChannelContext);
if (joinGroupRespBody == null || JoinGroupResult.JOIN_GROUP_RESULT_OK.getNumber() != joinGroupRespBody.getResult().getNumber()) {
RespBody joinRespBody = new RespBody(Command.COMMAND_JOIN_GROUP_RESP, ImStatus.C10012).setData(joinGroupRespBody);
ImPacket respPacket = ImKit.ConvertRespPacket(joinRespBody, channelContext);
ImPacket respPacket = ProtocolManager.Converter.respPacket(joinRespBody, imChannelContext);
return respPacket;
}
}
//处理完处理器内容后
ImAio.bindGroup(channelContext, groupId,imConfig.getMessageHelper().getBindListener());
Jim.bindGroup(imChannelContext, groupId);
//回一条消息告诉对方进群结果
joinGroupRespBody.setGroup(groupId);
joinGroupRespBody.setResult(joinGroupResult);
RespBody joinRespBody = new RespBody(Command.COMMAND_JOIN_GROUP_RESP,ImStatus.C10011).setData(joinGroupRespBody);
ImPacket respPacket = ImKit.ConvertRespPacket(joinRespBody, channelContext);
//先定义为操作成功
joinGroupRespBody.setResult(JoinGroupResult.JOIN_GROUP_RESULT_OK);
joinGroupRespBody.setData(joinGroupRespBody);
ImPacket respPacket = ProtocolManager.Converter.respPacket(joinGroupRespBody, imChannelContext);
return respPacket;
}
@Override

View File

@ -1,11 +1,9 @@
package org.jim.server.command.handler;
import org.apache.commons.collections4.CollectionUtils;
import org.jim.common.ImConst;
import org.jim.common.ImAio;
import org.jim.common.ImPacket;
import org.jim.common.ImSessionContext;
import org.jim.common.ImStatus;
import org.jim.common.*;
import org.jim.common.config.ImConfig;
import org.jim.common.exception.ImException;
import org.jim.common.message.MessageHelper;
import org.jim.common.packets.Command;
import org.jim.common.packets.Group;
@ -15,13 +13,15 @@ import org.jim.common.packets.User;
import org.jim.common.protocol.IProtocol;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.ImServerChannelContext;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.command.CommandManager;
import org.jim.server.command.handler.processor.login.LoginCmdProcessor;
import org.jim.server.handler.ProtocolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Aio;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import java.util.List;
@ -34,22 +34,23 @@ public class LoginReqHandler extends AbstractCmdHandler {
private static Logger log = LoggerFactory.getLogger(LoginReqHandler.class);
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
ImServerChannelContext imServerChannelContext = (ImServerChannelContext)imChannelContext;
if (packet.getBody() == null) {
Aio.remove(channelContext, "body is null");
Jim.remove(imChannelContext, "body is null");
return null;
}
List<LoginCmdProcessor> loginProcessors = this.getProcessor(channelContext, LoginCmdProcessor.class);
List<LoginCmdProcessor> loginProcessors = this.getProcessor(imChannelContext, LoginCmdProcessor.class);
if(CollectionUtils.isEmpty(loginProcessors)){
log.info("登录失败,没有登录命令业务处理器!");
Aio.remove(channelContext, "no login serviceHandler processor!");
Jim.remove(imChannelContext, "no login serviceHandler processor!");
return null;
}
LoginCmdProcessor loginServiceHandler = loginProcessors.get(0);
ImSessionContext imSessionContext = (ImSessionContext)channelContext.getAttribute();
ImSessionContext imSessionContext = imChannelContext.getSessionContext();
LoginReqBody loginReqBody = JsonKit.toBean(packet.getBody(),LoginReqBody.class);
LoginRespBody loginRespBody = loginServiceHandler.doLogin(loginReqBody,channelContext);
LoginRespBody loginRespBody = loginServiceHandler.doLogin(loginReqBody, imChannelContext);
if (loginRespBody == null || loginRespBody.getUser() == null) {
log.info("登录失败, loginName:{}, password:{}", loginReqBody.getLoginname(), loginReqBody.getPassword());
if(loginRespBody == null){
@ -57,32 +58,30 @@ public class LoginReqHandler extends AbstractCmdHandler {
}
loginRespBody.clear();
ImPacket loginRespPacket = new ImPacket(Command.COMMAND_LOGIN_RESP, loginRespBody.toByte());
ImAio.bSend(channelContext,loginRespPacket);
ImAio.remove(channelContext, "loginName and token is incorrect");
Jim.bSend(imChannelContext, loginRespPacket);
Jim.remove(imChannelContext, "loginName and token is incorrect");
return null;
}
User user = loginRespBody.getUser();
String userId = user.getId();
IProtocol protocol = ImKit.protocol(null, channelContext);
IProtocol protocol = imServerChannelContext.getProtocolHandler().getProtocol();
String terminal = protocol == null ? "" : protocol.name();
user.setTerminal(terminal);
imSessionContext.getClient().setUser(user);
ImAio.bindUser(channelContext,userId,imConfig.getMessageHelper().getBindListener());
Jim.bindUser(imServerChannelContext, user.getId());
//初始化绑定或者解绑群组;
bindUnbindGroup(channelContext, user);
loginServiceHandler.onSuccess(channelContext);
bindUnbindGroup(imChannelContext, user);
loginServiceHandler.onSuccess(imChannelContext);
loginRespBody.clear();
ImPacket loginRespPacket = new ImPacket(Command.COMMAND_LOGIN_RESP, loginRespBody.toByte());
return loginRespPacket;
return ProtocolManager.Converter.respPacket(loginRespBody, imChannelContext);
}
/**
* 初始化绑定或者解绑群组;
*/
public void bindUnbindGroup(ChannelContext channelContext , User user)throws Exception{
public void bindUnbindGroup(ImChannelContext imChannelContext , User user)throws ImException{
String userId = user.getId();
List<Group> groups = user.getGroups();
if( groups != null){
boolean isStore = ImConst.ON.equals(imConfig.getIsStore());
boolean isStore = ImConfig.Const.ON.equals(imConfig.getIsStore());
MessageHelper messageHelper = null;
List<String> groupIds = null;
if(isStore){
@ -97,14 +96,14 @@ public class LoginReqHandler extends AbstractCmdHandler {
ImPacket groupPacket = new ImPacket(Command.COMMAND_JOIN_GROUP_REQ,JsonKit.toJsonBytes(group));
try {
JoinGroupReqHandler joinGroupReqHandler = CommandManager.getCommand(Command.COMMAND_JOIN_GROUP_REQ, JoinGroupReqHandler.class);
joinGroupReqHandler.bindGroup(groupPacket, channelContext);
joinGroupReqHandler.bindGroup(groupPacket, imChannelContext);
} catch (Exception e) {
log.error(e.toString(),e);
}
}
if(isStore && groupIds != null){
for(String groupId : groupIds){
messageHelper.getBindListener().onAfterGroupUnbind(channelContext, groupId);
messageHelper.getBindListener().onAfterGroupUnbind(imChannelContext, groupId);
}
}
}

View File

@ -1,10 +1,11 @@
package org.jim.server.command.handler;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImConst;
import org.jim.common.ImChannelContext;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.tio.core.ChannelContext;
import org.jim.common.exception.ImException;
import org.jim.server.config.ImServerConfig;
import org.jim.common.message.MessageHelper;
import org.jim.common.packets.Command;
import org.jim.common.packets.RespBody;
@ -13,6 +14,7 @@ import org.jim.common.packets.MessageReqBody;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.AbstractCmdHandler;
import org.jim.server.handler.ProtocolManager;
/**
* 获取聊天消息命令处理器
@ -28,14 +30,14 @@ public class MessageReqHandler extends AbstractCmdHandler {
}
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
RespBody resPacket = null;
MessageReqBody messageReqBody = null;
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
RespBody resPacket;
MessageReqBody messageReqBody;
try{
messageReqBody = JsonKit.toBean(packet.getBody(),MessageReqBody.class);
}catch (Exception e) {
//用户消息格式不正确
return getMessageFailedPacket(channelContext);
return getMessageFailedPacket(imChannelContext);
}
UserMessageData messageData = null;
MessageHelper messageHelper = imConfig.getMessageHelper();
@ -56,8 +58,8 @@ public class MessageReqHandler extends AbstractCmdHandler {
//消息类型;
int type = messageReqBody.getType();
//如果用户ID为空或者type格式不正确获取消息失败;
if(StringUtils.isEmpty(userId) || (0 != type && 1 != type) || !ImConst.ON.equals(imConfig.getIsStore())){
return getMessageFailedPacket(channelContext);
if(StringUtils.isEmpty(userId) || (0 != type && 1 != type) || !ImServerConfig.Const.ON.equals(imConfig.getIsStore())){
return getMessageFailedPacket(imChannelContext);
}
if(type == 0){
resPacket = new RespBody(Command.COMMAND_GET_MESSAGE_RESP,ImStatus.C10016);
@ -78,7 +80,7 @@ public class MessageReqHandler extends AbstractCmdHandler {
if(0 == type){
messageData = messageHelper.getFriendsOfflineMessage(userId);
}else{
return getMessageFailedPacket(channelContext);
return getMessageFailedPacket(imChannelContext);
}
}else{
//获取与指定用户离线消息;
@ -90,15 +92,15 @@ public class MessageReqHandler extends AbstractCmdHandler {
}
}
resPacket.setData(messageData);
return ImKit.ConvertRespPacket(resPacket, channelContext);
return ProtocolManager.Converter.respPacket(resPacket, imChannelContext);
}
/**
* 获取用户消息失败响应包;
* @param channelContext
* @param imChannelContext
* @return
*/
public ImPacket getMessageFailedPacket(ChannelContext channelContext){
public ImPacket getMessageFailedPacket(ImChannelContext imChannelContext) throws ImException{
RespBody resPacket = new RespBody(Command.COMMAND_GET_MESSAGE_RESP,ImStatus.C10015);
return ImKit.ConvertRespPacket(resPacket, channelContext);
return ProtocolManager.Converter.respPacket(resPacket, imChannelContext);
}
}

View File

@ -4,10 +4,9 @@
package org.jim.server.command.handler;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImConst;
import org.jim.common.ImAio;
import org.jim.common.ImPacket;
import org.jim.common.ImStatus;
import org.jim.common.*;
import org.jim.common.config.ImConfig;
import org.jim.common.exception.ImException;
import org.jim.common.message.MessageHelper;
import org.jim.common.packets.Command;
import org.jim.common.packets.Group;
@ -17,7 +16,7 @@ import org.jim.common.packets.UserReqBody;
import org.jim.common.utils.ImKit;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.AbstractCmdHandler;
import org.tio.core.ChannelContext;
import org.jim.server.handler.ProtocolManager;
import java.util.ArrayList;
import java.util.List;
@ -34,14 +33,13 @@ public class UserReqHandler extends AbstractCmdHandler {
}
@Override
public ImPacket handler(ImPacket packet, ChannelContext channelContext) throws Exception {
public ImPacket handler(ImPacket packet, ImChannelContext imChannelContext) throws ImException {
UserReqBody userReqBody = JsonKit.toBean(packet.getBody(),UserReqBody.class);
User user = null;
RespBody resPacket = null;
String userId = userReqBody.getUserid();
if(StringUtils.isEmpty(userId)) {
return ImKit.ConvertRespPacket(new RespBody(Command.COMMAND_GET_USER_RESP, ImStatus.C10004), channelContext);
return ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_GET_USER_RESP, ImStatus.C10004), imChannelContext);
}
//(0:所有在线用户,1:所有离线用户,2:所有用户[在线+离线]);
Integer type = userReqBody.getType() == null ? 2 : userReqBody.getType();
@ -59,34 +57,34 @@ public class UserReqHandler extends AbstractCmdHandler {
}
}
if(user == null) {
return ImKit.ConvertRespPacket(new RespBody(Command.COMMAND_GET_USER_RESP, ImStatus.C10004), channelContext);
return ProtocolManager.Converter.respPacket(new RespBody(Command.COMMAND_GET_USER_RESP, ImStatus.C10004), imChannelContext);
}
resPacket.setData(user);
return ImKit.ConvertRespPacket(resPacket, channelContext);
return ProtocolManager.Converter.respPacket(resPacket, imChannelContext);
}
/**
* 根据用户id获取用户在线及离线用户;
* @param userid
* @param userId
* @param type(0:所有在线用户,1:所有离线用户,2:所有用户[在线+离线])
* @return
*/
public User getUserInfo(String userid , Integer type){
public User getUserInfo(String userId , Integer type){
User user = null;
//是否开启持久化;
boolean isStore = ImConst.ON.equals(imConfig.getIsStore());
boolean isStore = ImConfig.Const.ON.equals(imConfig.getIsStore());
//消息持久化助手;
MessageHelper messageHelper = imConfig.getMessageHelper();
if(isStore){
user = messageHelper.getUserByType(userid, 2);
user = messageHelper.getUserByType(userId, 2);
if(user == null) {
return null;
}
user.setFriends(messageHelper.getAllFriendUsers(userid, type));
user.setGroups(messageHelper.getAllGroupUsers(userid, type));
user.setFriends(messageHelper.getAllFriendUsers(userId, type));
user.setGroups(messageHelper.getAllGroupUsers(userId, type));
return user;
}else{
user = ImAio.getUser(userid);
//user = Jim.getUser(userId);
if(user == null) {
return null;
}
@ -129,14 +127,14 @@ public class UserReqHandler extends AbstractCmdHandler {
Group copyGroup = ImKit.copyGroupWithoutUsers(group);
List<User> users = null;
if(flag == 1){
users = ImAio.getAllUserByGroup(group.getGroup_id());
//users = Jim.getAllUserByGroup(group.getGroup_id());
}else if(flag == 0){
users = group.getUsers();
}
if(users != null && !users.isEmpty()){
List<User> copyUsers = new ArrayList<User>();
for(User userObj : users){
User onlineUser = ImAio.getUser(userObj.getId());
/*User onlineUser = Jim.getUser(userObj.getId());
//在线
if(onlineUser != null && type == 0){
User copyOnlineUser = ImKit.copyUserWithoutFriendsGroups(onlineUser);
@ -145,7 +143,7 @@ public class UserReqHandler extends AbstractCmdHandler {
}else if(onlineUser == null && type == 1){
User copyOnlineUser = ImKit.copyUserWithoutFriendsGroups(onlineUser);
copyUsers.add(copyOnlineUser);
}
}*/
}
copyGroup.setUsers(copyUsers);
}

View File

@ -3,24 +3,24 @@
*/
package org.jim.server.command.handler.processor;
import org.tio.core.ChannelContext;
import org.jim.common.ImChannelContext;
import org.jim.common.ImConst;
/**
* 不同协议CMD命令处理接口
* @author WChao
*
*/
public interface CmdProcessor {
public interface CmdProcessor extends ImConst {
/**
* 不同协议判断方法
* @param channelContext
* @param imChannelContext
* @return
*/
public boolean isProtocol(ChannelContext channelContext);
boolean isProtocol(ImChannelContext imChannelContext);
/**
* 该proCmd处理器名称(自定义)
* @return
*/
public String name();
String name();
}

Some files were not shown because too many files have changed in this diff Show More