相信做过web的小哥应该都知道session的概念,简单来说,session翻译是会话,用来保存用户浏览web页面的一些信息(用户信息)。
我们的框架也需要这么一个session,用于保存用户信息,与客户端连接的管道(Channel)。
有了这个session,我们能做到在系统的各个地方能获取到该用户的信息,及回消息给客户端
增加的类
AttributeUtil
IUser
Session
SessionAttributeKey
SessionManager
提前写这几个类为session类服务,
1.我们有一个用户类,里面是具体的用户信息,但是我们写的是一个框架,不可能把具体的用户属性写好,所以我们写一个IUser接口,里面放3个肯定会有的方法,getId()/getStatus()/getRole(),用户id、状态、角色。
2.时间管理类TimeUtil,可以获取时间,用于设置session的创建时间
现在就是关键类Session的书写,里面很简单,channel/user/createTime,与客户端的管道/用户信息/创建时间。
里面的几个关键方法,
构造函数--》设置channel和createTime创建时间
registerUser--》玩session里面写入user,一般是在登录之后调用
close--》关闭与客户端的连接
因为我们所有的连接最开始有的一个数据都是基于连接(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());
SessionManager
我们的框架也需要这么一个session,用于保存用户信息,与客户端连接的管道(Channel)。
有了这个session,我们能做到在系统的各个地方能获取到该用户的信息,及回消息给客户端
增加的类
AttributeUtil
IUser
Session
SessionAttributeKey
SessionManager
TimeUtil
1.我们有一个用户类,里面是具体的用户信息,但是我们写的是一个框架,不可能把具体的用户属性写好,所以我们写一个IUser接口,里面放3个肯定会有的方法,getId()/getStatus()/getRole(),用户id、状态、角色。
2.时间管理类TimeUtil,可以获取时间,用于设置session的创建时间
现在就是关键类Session的书写,里面很简单,channel/user/createTime,与客户端的管道/用户信息/创建时间。
里面的几个关键方法,
构造函数--》设置channel和createTime创建时间
registerUser--》玩session里面写入user,一般是在登录之后调用
close--》关闭与客户端的连接
而且我把所有的方法修饰符都改成了private或者默认,使这些仅作用于类里,及包内。为了所有的session操作都通过SessionManager进行。
因为我们所有的连接最开始有的一个数据都是基于连接(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游戏服务器开发的相关知识 676231524