Netty网络聊天室之会话管理

原创 2017年08月05日 16:36:08

写过web的同学们应该对Session这个东西很熟悉。浏览器第一次与服务器建立连接的时候,服务器就会自动为之分配一个Session。Session可以用来判断用户是否经过登录验证,也可以保存用户的各种信息。

其实,Session是很常用的技术。不管是WEB,还是游戏服务,还是联网的桌面程序,都有session的身影。有了Session,我们可以向里面保存各种个人参数,还可以利用session来向客户端发送消息。极大方便了程序对客户端的管理。

Mina IO框架默认有IoSession这个对象,Netty可就没有了。所以我们可以自己创建一个Session抽象。

关于Session,我们希望它有这样的作用。

1. 客户端链路第一次激活时,服务端为之创建一个Session;

2. 可以使用Session向用户发送消息;

3. 可以保存一些重要的且不需要持久化的用户信息;

4. 只能由服务端控制它的生命周期消亡;

public class IoSession {
	
	private static final Logger logger = LoggerFactory.getLogger(IoSession.class);

	/** 网络连接channel */
	private Channel channel;

	private User user;

	/** ip地址 */
	private String ipAddr;

	private boolean reconnected;
	
	/** 拓展用,保存一些个人数据  */
	private Map<String, Object> attrs = new HashMap<>();

	public IoSession() {

	}

	public IoSession(Channel channel) {
		this.channel = channel;
		this.ipAddr = ChannelUtils.getIp(channel);
	}
	
	public void setUser(User user) {
		this.user = user;
	}
	
	/**
	 * 向客户端发送消息
	 * @param packet
	 */
	public void sendPacket(Packet packet) {
		if (packet == null) {
			return;
		}
		if (channel != null) {
			channel.writeAndFlush(packet);
		}
	}

	public String getIpAddr() {
		return ipAddr;
	}

	public void setIpAddr(String ipAddr) {
		this.ipAddr = ipAddr;
	}

	public boolean isReconnected() {
		return reconnected;
	}

	public void setReconnected(boolean reconnected) {
		this.reconnected = reconnected;
	}

	public User getUser() {
		return user;
	}
	
	public boolean isClose() {
		if (channel == null) {
			return true;
		}
		return !channel.isActive() ||
			   !channel.isOpen();
	}
	
	/**
	 * 关闭session 
	 * @param reason {@link SessionCloseReason}
	 */
	public void close(SessionCloseReason reason) {
		try{
			if (this.channel == null) {
				return;
			}
			if (channel.isOpen()) {
				channel.close();
				logger.info("close session[{}], reason is {}", getUser().getUserId(), reason);
			}else{
				logger.info("session[{}] already close, reason is {}", getUser().getUserId(), reason);
			}
		}catch(Exception e){
		}
	}

}

Session被关闭可以有一系列原因,所以我们最后有一个枚举保存各种原因,像这样

package com.kingston.net;

public enum SessionCloseReason {
	
	/** 正常退出 */
	NORMAL,
	
	/** 链接超时 */
	OVER_TIME,
	

}

在Netty,channel是通讯的载体,为了方便对channel的各种操作,加了一个channel的工具类(ChannelUtils.java)

public final class ChannelUtils {
	
	public static AttributeKey<IoSession> SESSION_KEY = AttributeKey.valueOf("session");
	
	/**
	 * 添加新的会话
	 * @param channel
	 * @param session
	 * @return
	 */
	public static boolean addChannelSession(Channel channel, IoSession session) {
		Attribute<IoSession> sessionAttr = channel.attr(SESSION_KEY);
		return sessionAttr.compareAndSet(null, session);
	}
	
	public static IoSession getSessionBy(Channel channel) {
		Attribute<IoSession> sessionAttr = channel.attr(SESSION_KEY);
		return sessionAttr.get() ;
	}
	
	public static String getIp(Channel channel) {
		return ((InetSocketAddress)channel.remoteAddress()).getAddress().toString().substring(1);
	}

}

使用了IoSession,先前用于管理用户通讯的工具类,也相应发生变化

public enum ServerManager {

	INSTANCE;

	private Logger logger = LoggerFactory.getLogger(ServerManager.class);

	/** 缓存通信上下文环境对应的登录用户(主要用于服务) */ 
	private Map<IoSession, Long> session2UserIds  = new ConcurrentHashMap<>();

	/** 缓存用户id与对应的会话 */
	private ConcurrentMap<Long, IoSession> userId2Sessions = new ConcurrentHashMap<>();


	public void sendPacketTo(Packet pact,Long userId){
		if(pact == null || userId <= 0) return;

		IoSession session = userId2Sessions.get(userId);
		if (session != null) {
			session.sendPacket(pact);
		}
	}

	/**
	 *  向所有在线用户发送数据包
	 */
	public void sendPacketToAllUsers(Packet pact){
		if(pact == null ) return;

		userId2Sessions.values().forEach( (session) -> session.sendPacket(pact));
	}

	/**
	 *  向单一在线用户发送数据包
	 */
	public void sendPacketTo(Packet pact,ChannelHandlerContext targetContext ){
		if(pact == null || targetContext == null) return;
		targetContext.writeAndFlush(pact);
	}


	public IoSession getSessionBy(long userId) {
		return this.userId2Sessions.get(userId);
	}

	public boolean registerSession(User user, IoSession session) {

		session.setUser(user);
		userId2Sessions.put(user.getUserId(), session);

		logger.info("[{}] registered...", user.getUserId());

		return true;
	}

	/**
	 *   注销用户通信渠道
	 */
	public void ungisterUserContext(Channel context ){
		if(context  == null){

			return;
		}
		IoSession session = ChannelUtils.getSessionBy(context);
		Long userId = session2UserIds.remove(session);
		userId2Sessions.remove(userId);
		if (session != null) {
			session.close(SessionCloseReason.OVER_TIME);
		}
	}

}

加入IoSession后,先前的业务需要做点修改,比如在客户端链路建立后,需要创建新的session对象。

在MessageTransportHandler类增加方法

@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		if (!ChannelUtils.addChannelSession(ctx.channel(), new IoSession(ctx.channel()))) {
			ctx.channel().close();
			logger.error("Duplicate session,IP=[{}]",ChannelUtils.getIp(ctx.channel()));
		}
	}

全部代码已在github上托管

服务端代码请移步 --> netty聊天室服务器

客户端代码请移步 --> netty聊天室客户端





版权声明:本文为博主原创文章,未经博主允许不得转载。

Netty中的session属性设置和链接事件捕获

Netty中的session属性设置和链接事件捕获 Netty的使用比mina更灵活,也更复杂,下面通过一个例子,主要说明netty中如何对某个链接设置属性,并顺带描述了链接事件的捕获。   i...
  • guanxinquan
  • guanxinquan
  • 2013年09月03日 16:09
  • 6752

Mina、Netty、Twisted一起学(六):session

在同步阻塞的网络编程中,代码都是按照TCP操作顺序编写的,即创建连接、多次读写、关闭连接,这样很容易判断这一系列操作是否是同一个连接。而在事件驱动的异步网络编程框架中,IO操作都会触发一个事件调用相应...
  • xiao__gui
  • xiao__gui
  • 2014年09月10日 13:51
  • 5018

Netty实现简单HTTP服务器

Netty实现简单HTTP服务器 public class HttpServer { public static void main(String[] args) throws Interrup...
  • Coder_py
  • Coder_py
  • 2017年06月07日 23:08
  • 588

netty的IM项目小结

关于netty的主要用途的理解,netty的线程模型和IO模型高效的理解。 关于自己一个个人小IM项目的介绍和反思。...
  • u011518120
  • u011518120
  • 2017年02月27日 22:22
  • 2909

用Netty开发中间件:高并发性能优化

用Netty开发中间件:高并发性能优化最近在写一个后台中间件的原型,主要是做消息的分发和透传。因为要用Java实现,所以网络通信框架的第一选择当然就是Netty了,使用的是Netty 4版本。Nett...
  • dc_726
  • dc_726
  • 2015年10月08日 20:52
  • 48634

Java游戏服务器-Netty自动重连与会话管理

 网游少不了网络通信,不像写C++时自己造轮子,Java服务器使用Netty。Netty做了很多工作,使编写网络程序变得轻松简单。灵活利用这些基础设施,以实现我们的需求。 其中一个需求是自动重...
  • mingtianhaiyouwo
  • mingtianhaiyouwo
  • 2015年11月24日 15:13
  • 924

[置顶] 基于netty实现的远程服务框架

HSF服务管理平台 基于netty实现远程服务框架,为终端提供REST形式的HTTP服务。 目前只实现部分功能,可以提供REST形式和传统形式的HTTP服务,其特点主要包括: 基于netty实...
  • lanjian056
  • lanjian056
  • 2016年09月30日 14:53
  • 1949

spring+netty服务器搭建

游戏一般是长连接,自定义协议,不用http协议,BIO,NIO,AIO这些我就不说了,自己查资料 我现在用spring+netty搭起简单的游戏服 思路:1自定义协议和协议包;2spring+ne...
  • u012930316
  • u012930316
  • 2017年06月26日 18:18
  • 3334

Netty网络聊天室之基础网关搭建

基本Netty通信框架开发一个模仿QQ的聊天室。使用Netty作为通信网关,使用JavaFX开发客户端界面,使用Spring作为IOC容器,使用MyBatics支持持久化。本文将着重介绍服务端客户端框...
  • littleschemer
  • littleschemer
  • 2016年02月17日 23:28
  • 10311

使用最新Netty实现一个简单的聊天程序

1、概述 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 官网:h...
  • anxpp
  • anxpp
  • 2016年08月06日 22:55
  • 6800
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Netty网络聊天室之会话管理
举报原因:
原因补充:

(最多只允许输入30个字)