相信做过web的小哥应该都知道session的概念,简单来说,session翻译是会话,用来保存用户浏览web页面的一些信息(用户信息)。
我们的框架也需要这么一个session,用于保存用户信息,与客户端连接的管道(Channel)。
有了这个session,我们能做到在系统的各个地方能获取到该用户的信息,及回消息给客户端
增加的类
AttributeUtil
IUser
Session
SessionAttributeKey
SessionManager
TimeUtil
提前写这几个类为session类服务,
1.我们有一个用户类,里面是具体的用户信息,但是我们写的是一个框架,不可能把具体的用户属性写好,所以我们写一个IUser接口,里面放3个肯定会有的方法,getId()/getStatus()/getRole(),用户id、状态、角色。
2.时间管理类TimeUtil,可以获取时间,用于设置session的创建时间
现在就是关键类Session的书写,里面很简单,channel/user/createTime,与客户端的管道/用户信息/创建时间。
里面的几个关键方法,
构造函数–》设置channel和createTime创建时间
registerUser–》玩session里面写入user,一般是在登录之后调用
close–》关闭与客户端的连接
而且我把所有的方法修饰符都改成了private或者默认,使这些仅作用于类里,及包内。为了所有的session操作都通过SessionManager进行。
下面就是SessionManager,说之前先说下AttributeUtil、SessionAttributeKey
因为我们所有的连接最开始有的一个数据都是基于连接(channel),而netty的Channel继承了AttributeMap,可以往里面绑定数据。
调用方式就是channel.attr(attributeKey).set(value)/channel.attr(attributeKey).get()。
这样我们就可以写一个简单的封装AttributeUtil,暴露一个set和get方法。
而attributeKey的话是一个唯一常量,所以我们写在SessionAttributeKey中。
(详细了解可以看Netty的AttributeMap属性 https://blog.csdn.net/cmqwan/article/details/80775092)
而我们就可以将channel和session进行绑定。就先在SessionAttributeKey写一个Session的key,设置和获取的时候就是用AttributeUtil就好了。
具体说到SessionManager,首先设置SessionManager为单例模式
里面一个uidSessionMap,用于管理已经登录的session。
对应Session里面的方法,创建、注册、关闭。
关键:通过session发送信息sendMessage(Session session, String msg){session.getChannel().writeAndFlush(msg);}
这些类都写完了,就看怎么使用了
在NetworkListener的onConnected中SessionManager.getInstance().create(ctx.channel());
在NetworkListener的onDisconnected中SessionManager.getInstance().close(ctx.channel());
重新运行服务端和客户端,完美
看下具体代码吧
IUser
/**
* Copyright (C), 2015-2018
* FileName: IUser
* Author: zhao
* Date: 2018/6/22 15:46
* Description: 用户的抽象类,主要用于session之中
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
/**
* 〈一句话功能简述〉<br>
* 〈用户的抽象类,主要用于session之中〉
*
* @author zhao
* @date 2018/6/22 15:46
* @since 1.0.0
*/
public interface IUser {
/**
* 获取用户id
*
* @return 用户id
*/
Integer getId();
/**
* 获取用户状态id
*
* @return 状态id
*/
Integer getStatus();
/**
* 获取用户角色类型id
*
* @return 角色类型id
*/
Integer getRole();
}
TimeUtil
/**
* Copyright (C), 2015-2018
* FileName: TimeUtil
* Author: zhao
* Date: 2018/6/22 17:07
* Description: TimeUtil时间工具
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.util;
import java.util.Calendar;
import java.util.Date;
/**
* 〈一句话功能简述〉<br>
* 〈TimeUtil时间工具〉
*
* @author zhao
* @date 2018/6/22 17:07
* @since 1.0.0
*/
public class TimeUtil {
private TimeUtil() {
}
/**
* 获取系统距1970年1月1日总毫秒
*
* @return 距1970年1月1日总毫秒
*/
public static long getSysCurTimeMillis() {
return getCalendar().getTimeInMillis();
}
/**
* 获取系统时间
*
* @return 系统时间
*/
private static Calendar getCalendar() {
Calendar nowCalendar = Calendar.getInstance();
nowCalendar.setTime(new Date());
return nowCalendar;
}
}
Session
/**
* Copyright (C), 2015-2018
* FileName: Session
* Author: zhao
* Date: 2018/6/22 15:44
* Description: Session会话
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
import com.lizhaoblog.base.util.TimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.Channel;
/**
* 〈一句话功能简述〉<br>
* 〈Session会话,包括管道及用户信息〉
* 里面的所有信息都需要交由SessionManager操作
* 外部无法直接访问到这个类的方法
*
* @author zhao
* @date 2018/6/22 15:44
* @since 1.0.0
*/
public class Session {
private static final Logger logger = LoggerFactory.getLogger(Session.class);
/**
* 与客户端的管道
*/
private final Channel channel;
/**
* 用户信息
* 用户信息可能为空。
* 只有在register(登录)之后,里面才会赋值
*/
private IUser user;
/**
* 创建时间
*/
private final long createTime;
Session(Channel channel) {
this.channel = channel;
this.createTime = TimeUtil.getSysCurTimeMillis();
}
/**
* 玩session里面写入user,一般是在登录之后调用
* @param user 用户 信息
*/
void registerUser(IUser user) {
this.user = user;
}
/**
* 关闭与客户端的连接
*/
void close() {
if (channel == null) {
return;
}
try {
if (channel.isActive() || channel.isOpen()) {
channel.close().sync();
}
} catch (InterruptedException e) {
logger.error("channel.close find error ", e);
}
}
IUser getUser() {
return user;
}
Channel getChannel() {
return channel;
}
}
SessionAttributeKey
/**
* Copyright (C), 2015-2018
* FileName: SessionAttributeKey
* Author: zhao
* Date: 2018/6/22 16:12
* Description: session相关的AttributeKey
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
import io.netty.util.AttributeKey;
/**
* 〈一句话功能简述〉<br>
* 〈session相关的AttributeKey〉
*
* @author zhao
* @date 2018/6/22 16:12
* @since 1.0.0
*/
public class SessionAttributeKey {
/**
* AttributeKey Session
*/
public static final AttributeKey<Session> SESSION = AttributeKey.newInstance("SESSION");
}
AttributeUtil
/**
* Copyright (C), 2015-2018
* FileName: AttributeUtil
* Author: zhao
* Date: 2018/6/22 16:15
* Description: Attribute操作的工具类
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
/**
* 〈一句话功能简述〉<br>
* 〈Attribute操作的工具类〉
*
* @author zhao
* @date 2018/6/22 16:15
* @since 1.0.0
*/
public class AttributeUtil {
/**
* 获取绑定到channel上面的数据
*
* @param channel 与客户端连接的管道
* @param key {@link SessionAttributeKey}
* @param <T> 绑定的类型
* @return 绑定到channel上面的数据
*/
public static <T> T get(Channel channel, AttributeKey<T> key) {
return channel.attr(key).get();
}
/**
* 绑定某个数据到channel
*
* @param channel 与客户端连接的管道
* @param key {@link SessionAttributeKey}
* @param value 绑定的内容
* @param <T> 绑定的类型
*/
public static <T> void set(Channel channel, AttributeKey<T> key, T value) {
channel.attr(key).set(value);
}
private AttributeUtil() {
}
}
SessionManager
/**
* Copyright (C), 2015-2018
* FileName: SessionManager
* Author: zhao
* Date: 2018/6/22 16:40
* Description: Session管理类
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import io.netty.channel.Channel;
/**
* 〈一句话功能简述〉<br>
* 〈Session管理类〉
*
* @author zhao
* @date 2018/6/22 16:40
* @since 1.0.0
*/
public class SessionManager {
private static final Logger logger = LoggerFactory.getLogger(SessionManager.class);
private static final SessionManager instance = new SessionManager();
public static SessionManager getInstance() {
return instance;
}
private SessionManager() {
logger.info("SessionManager init success");
}
/**
* 已经登录的session管理
*/
private final ConcurrentMap<Integer, Session> uidSessionMap = new ConcurrentHashMap<>(16);
public Session getSession(Integer uid) {
return uidSessionMap.get(uid);
}
/**
* 创建session
*
* @param channel 与客户端连接的管道
* @return session
*/
public Session create(Channel channel) {
Session session = getSessionByChannel(channel);
if (session == null) {
session = new Session(channel);
AttributeUtil.set(channel, SessionAttributeKey.SESSION, session);
logger.info("session 创建成功");
} else {
logger.error("新连接建立时已存在Session,注意排查原因 " + channel.toString());
}
return session;
}
/**
* 注册sesson
*
* @param session session
* @param user 用户
*/
public void register(Session session, IUser user) {
session.registerUser(user);
uidSessionMap.put(session.getUser().getId(), session);
}
/**
* 通过channel关闭session
*
* @param channel 与客户端连接的管道
*/
public void close(Channel channel) {
Session session = getSessionByChannel(channel);
if (session != null) {
close(session);
}
}
/**
* 关闭session
*
* @param session 要关闭的session
*/
private void close(Session session) {
unregister(session);
session.close();
logger.info("session close success");
}
/**
* 将之前注册好的session从map中移除
*
* @param session 将之前注册好的session从map中移除
*/
private void unregister(Session session) {
if (session != null) {
AttributeUtil.set(session.getChannel(), SessionAttributeKey.SESSION, null);
IUser user = session.getUser();
if (user != null) {
boolean remove = uidSessionMap.remove(user.getId(), session);
logger.info("Session unregister, userId={}, remove={}", user.getId(), remove);
}
}
}
public Session getSessionByChannel(Channel channel) {
return AttributeUtil.get(channel, SessionAttributeKey.SESSION);
}
public void sendMessage(Channel channel, String msg) {
sendMessage(getSessionByChannel(channel), msg);
}
public void sendMessage(Session session, String msg) {
session.getChannel().writeAndFlush(msg);
}
}
AttributeUtil
/**
* Copyright (C), 2015-2018
* FileName: AttributeUtil
* Author: zhao
* Date: 2018/6/22 16:15
* Description: Attribute操作的工具类
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.session;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
/**
* 〈一句话功能简述〉<br>
* 〈Attribute操作的工具类〉
*
* @author zhao
* @date 2018/6/22 16:15
* @since 1.0.0
*/
public class AttributeUtil {
/**
* 获取绑定到channel上面的数据
*
* @param channel 与客户端连接的管道
* @param key {@link SessionAttributeKey}
* @param <T> 绑定的类型
* @return 绑定到channel上面的数据
*/
public static <T> T get(Channel channel, AttributeKey<T> key) {
return channel.attr(key).get();
}
/**
* 绑定某个数据到channel
*
* @param channel 与客户端连接的管道
* @param key {@link SessionAttributeKey}
* @param value 绑定的内容
* @param <T> 绑定的类型
*/
public static <T> void set(Channel channel, AttributeKey<T> key, T value) {
channel.attr(key).set(value);
}
private AttributeUtil() {
}
}
上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231564