游戏客户端Session的统一管理
看本系统文章需要些C语言、数据结构和网络基础知识!
说明:由于游戏服务器端会有成千上万的客户端进行连接请求,所以要求我们需要做一些简单的会话管理!如下图

1.简单说明
进行统一的分配和管理,就需要我们去统一分配资源,创建和销毁(也就内存统一进行管理),采取一种内存池方式来管理。
1)先分配一块大的堆内存,其中整个空间是N个Session结构组成的,并利用链表进行串联起来,请看后面代码示例。

2)当分配一个Session时,就从内存池空闲的链表中获取到一块Session结构体大小的内存给新的Session;
3)当释放一个Session时,就把该Session再次加入到内存池空闲的链表中。
2.创建Session结构体如下:
typedef struct Session_s {
	char ip[32];	// IP
	int  port;		// Port
	int  sock;		// socket
	int is_removed; // 是否被删除
	struct Session_s* next; // 指向下一个Session_t
	//void* data;	// 附加数据
} Session_t;3.接口设计:
// 初始化Session内存管理池
void SessionPoolInit();
// 释放Session内存管理池资源
void SessionPoolExit();
// 将Session保存到Session内存管理池中
// @sock:client socket
// @ip: client ip
// @port: client port
Session_t* SessionToPoolAdd(int sock, char* ip, unsigned short port);
// 关闭session
void SessionClose(Session_t* s);
// 接口:遍历所有在线的session调用回调函数
// 如果callback 返回1,停止往下遍历;
void SessionForeachOnline(int (*call_back)(Session_t* s, void* params), void* params);
// 循环遍历在线session是否有要删除的session,主要考虑到多线程和延缓关闭套接字问题
void SessionFromPoolClear(void(*closed_socket)(int sock, void* params), void* params);4.代码实现
.h文件
#ifndef __SESSION_H__
#define __SESSION_H__
#ifdef WIN32
#include<winsock2.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")
#endif
typedef struct Session_s {
	char ip[32];	// IP
	int  port;		// Port
	int  sock;		// socket
	int is_removed; // 是否被删除
	struct Session_s* next; // 指向下一个Session_t
	//void* data;	// 数据
} Session_t;
// 初始化Session内存管理池
void SessionPoolInit();
// 释放Session内存管理池资源
void SessionPoolExit();
// 将Session保存到Session内存管理池中
// @sock:client socket
// @ip: client ip
// @port: client port
Session_t* SessionToPoolAdd(int sock, char* ip, unsigned short port);
// 关闭session
void SessionClose(Session_t* s);
// 接口:遍历所有在线的session调用回调函数
// 如果callback 返回1,停止往下遍历;
void SessionForeachOnline(int (*call_back)(Session_t* s, void* params), void* params);
// 循环遍历在线session是否有要删除的session,主要考虑到多线程和延缓关闭套接字问题
void SessionFromPoolClear(void(*closed_socket)(int sock, void* params), void* params);
#endif
.c文件
#include "session.h"
#define my_malloc malloc
#define my_free free
#define MAX_SEESION 5000		// 最大会话量,自己可以设置
#define MAX_RECV_BUFFER 8096	// 接收数据缓冲大小
static struct {
	Session_t* online;	// 在线客户端链表首地址
	Session_t* pBase;			// 分配内存首地址
	Session_t* free_list_base;		// 空闲链表首
	int has_removed_session;	// 池中是否删除session
	//int readed;	// 缓冲区已经读取数据长度
	//char recv_buffer[MAX_RECV_BUFFER]; // 数据缓冲区
}SessionPool;
// ================================内部使用==================================
// 从池中申请Session_t
static Session_t* session_alloc() {
	Session_t* session;
	if (SessionPool.free_list_base) {
		session = SessionPool.free_list_base;
		SessionPool.free_list_base = SessionPool.free_list_base->next;
	}
	else {
        printf("警告:最大进行会话数量为:%d\n", MAX_SEESION);
		session = my_malloc(sizeof(Session_t));
	}
	memset(session, 0, sizeof(Session_t));
	return session;
}
// 从池中释放Session_t
static void session_free(Session_t* s) {
	if (s < SessionPool.pBase || s >= SessionPool.pBase + MAX_SEESION) {
		my_free(s);
	}
	else {
		// 将不用的session添加到空闲链表中
		s->next = SessionPool.free_list_base;
		SessionPool.free_list_base = s;
	}
}
// ================================END========================================
void SessionPoolInit()
{
    int i = 0;
	memset(&SessionPool, 0, sizeof(SessionPool));
	// 分配空间并初始化为0
	SessionPool.pBase = my_malloc(MAX_SEESION * sizeof(Session_t));
	memset(SessionPool.pBase, 0, MAX_SEESION * sizeof(Session_t));
	// 串成链表
    for (i = 0; i < MAX_SEESION; i++) { //
		SessionPool.pBase[i].next = SessionPool.free_list_base;
		SessionPool.free_list_base = &SessionPool.pBase[i];
	}
}
void SessionPoolExit()
{
	// 服务器一般很少编写,留作接口使用
}
Session_t* SessionToPoolAdd(int c_s, char* ip, unsigned short port)
{
	Session_t* s = session_alloc();
    size_t len = strlen(ip);
	if (len >= 32) {
		len = 32 - 1;
	}
	strncpy(s->ip, ip, len);
	s->ip[len] = 0;
	s->port = port;
	s->sock = c_s;
	// 添加进在线客户端链表中
	s->next = SessionPool.online;
	SessionPool.online = s;
	return s;
}
void SessionClose(Session_t* s)
{
	s->is_removed = 1;	// session被删除
	SessionPool.has_removed_session = 1;  // 池中有需要删除的session
}
void SessionForeachOnline(int (*call_back)(Session_t* s, void* params), void* params)
{
    Session_t* walk = NULL;
	if (call_back == NULL) {
		return;
	}
    walk = SessionPool.online;
	while (walk) {
		if (call_back(walk, params)) {
			return;
		}
		walk = walk->next;
	}
}
void SessionFromPoolClear(void(*closed_socket)(int sock, void* params), void* params)
{
    Session_t** walk = NULL;
	if (SessionPool.has_removed_session == 0) {
		return;
	}
    walk = &SessionPool.online;
	while (*walk) {
		if ((*walk)->is_removed) {
			Session_t* node = (*walk);
			*walk = node->next;
			node->next = NULL;
			if (closed_socket) {
				closed_socket(node->sock, params);
			}
			printf("\r\n");
			printf("client exit %s:%d\n", node->ip, node->port);
			printf("\r\n");
			closesocket(node->sock);
			node->sock = 0;
			node->is_removed = 0;
			session_free(node);
		}
		else {
			walk = &(*walk)->next;
		}
	}
	SessionPool.has_removed_session = 0;
}
5.测试小Demo
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "session.h"
int forkSession(Session_t* s, void* params)
{
	printf("=============================================\r\n");
	printf("||online client:[%d]--[%s]--[%d]||\r\n", s->sock, s->ip, s->port);
	printf("=============================================\r\n");
	return 0;
}
int main()
{
    Session_t* s1 = NULL;
    Session_t* s2 = NULL;
    Session_t* s3 = NULL;
    Session_t* s4 = NULL;
	// 测试
	// 初始化内存池
	SessionPoolInit();
	// 测试增加客户端
    s1 = SessionToPoolAdd(1000, "127.0.0.1", 6060);
    s2 = SessionToPoolAdd(2000, "127.0.0.1", 7070);
    s3 = SessionToPoolAdd(3000, "127.0.0.1", 8080);
    s4 = SessionToPoolAdd(4000, "127.0.0.1", 9090);
	// 测试循环在线客户端
	printf("$$$$$$$$$$$$$$$查询在线客户端$$$$$$$$$$$$$$$\r\n");
	SessionForeachOnline(forkSession, NULL);
	printf("$$$$$$$$$$$$$$$$$$$$$END$$$$$$$$$$$$$$$$$$$$\r\n");
	// 删除客户端
	SessionClose(s3);
	SessionFromPoolClear(NULL, NULL);
	//
	printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n");
	// 再测试循环有哪些在线客户端
	SessionForeachOnline(forkSession, NULL);
	printf("hello...jadeshu\n");
	system("pause");
	return 0;
}
测试Demo结果:

本节代码下载:点击下载
游戏客户端Session的统一管理的更多相关文章
- WiFi 统一管理以及设备自动化测试实践
		ATX 安卓设备 WiFi 统一管理以及设备自动化测试实践 (零散知识梳理总结) 此文为转载,感谢作者 目录 众所周知,安卓单台设备的UI自动化测试已经比较完善了,有数不清的自动化框架或者工具.但 ... 
- 利用log4j+mongodb实现分布式系统中日志统一管理
		背景 在分布式系统当中,我们有各种各样的WebService,这些服务可能分别部署在不同的服务器上,并且有各自的日志输出.为了方便对这些日志进行统一管理和分析.我们可以将日志统一输出到指定的数 ... 
- .NET Core微服务之基于Steeltoe使用Spring Cloud Config统一管理配置
		Tip: 此篇已加入.NET Core微服务基础系列文章索引 => Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ... 
- media静态文件统一管理      操作内存的流 - StringIO | BytesIO      PIL:python图片操作库  前端解析二进制流图片(了解)      Admin自动化数据管理界面
		一.media ''' 1. 将用户上传的所有静态文件统一管理 -- settings.py -- MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 2. 服务 ... 
- Active Directory、Exchange、单点登录,企业账号统一管理解决方案
		现在的公司一般都会有很多内部管理系统,比如OA.ERP.CRM.邮件系统等.员工入职之后如果每个系统都创建一个账号和密码,首先员工记系统账号就是一件非常头疼的事情,如果公司有一百个系统那就得创建一百个 ... 
- SpringCloud-微服务配置统一管理SpringCloud Config(七)
		前言:对于应用,配制文件通常是放在项目中管理的,它可能有spring.mybatis.log等等各种各样的配置文件和属性文件,另外你还可能有开发环境.测试环境.生产环境等,这样的话就得一式三份,若是传 ... 
- 服务器端Session和客户端Session(和Cookie区别)2
		https://blog.csdn.net/java_faep/article/details/78082802 我们可以得出如下结论: 关闭浏览器,只会是浏览器端内存里的session cookie ... 
- 服务器端Session和客户端Session(和Cookie区别)
		Session其实分为客户端Session和服务器端Session. 当用户首次与Web服务器建立连接的时候,服务器会给用户分发一个 SessionID作为标识.SessionID是一个由24个字符组 ... 
- 《Unity 3D游戏客户端基础框架》概述
		框架概述: 做了那么久的业务开发,也做了一年多的核心战斗开发,最近想着自己倒腾一套游戏框架,当然暂不涉及核心玩法类型和战斗框架,核心战斗的设计要根据具体的游戏类型而定制,这里只是一些通用的基础系统的框 ... 
随机推荐
- sql 时间转换格式 convert(varchar(10),字段名,转换格式)
			convert(varchar(10),字段名,转换格式) CONVERT(nvarchar(10),count_time,121) CONVERT为日期转换函数,一般就是在时间类型(datetime ... 
- Socket端口复用
			在网络应用中(如Java Socket Server),当服务关掉立马重启时,很多时候会提示端口仍被占用(因端口上有处于TIME_WAIT的连接).此时可通过 SO_REUSEADDR 参数( soc ... 
- js this 引起的祸
			注意:这里的 this 指向已经改变了. 所以不能再次使用 this 需在上面自定义变量. 那些年踩过的坑,记录一下. 
- 【Dubbo 源码解析】08_Dubbo与Spring结合
			Dubbo 与 Spring 结合 基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamesp ... 
- iOS Icon Size 快速得到三种大小的图标
			在iOS开发中,按钮图标可以放三种大小,常见的是22x22,44x44,66x66,一般来说我们可以在PS中做好图片后,再分别导出三种大小的图标,但是每次要修改图片的大小,操作比较繁琐.这里博主推荐一 ... 
- java 中的 Comparable 和 Comparator 与 Iterable 和 Iterator
			Comparable 和 Comparator Comparable 和 Comparator 是两个关系不大的类,其分别侧重于不同的方面. 其中,接口 Comparable<T> 强行对 ... 
- TCP、UDP详解与抓包工具使用
			参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ... 
- gitlab提交代码
			cd existing_foldergit initgit remote add origin http://10.26.1.9/root/yunlian.gitgit add .git commit ... 
- C#实现WinForm禁止最大化、最小化、双击标题栏、双击图标等操作的方法
			from:http://www.jb51.net/article/71319.htm 本文实例讲述了C#实现WinForm禁止最大化.最小化.双击标题栏.双击图标等操作的方法.分享给大家供大家参考.具 ... 
- Weex小笔记(自己理解,有错请指正)
			在Eros中,做的内容是封装了一些常用的框架,并且优化开发流程为将前端Vue文件打包出资源文件导入项目工程中(本地加载模式,需要注册文件.验证文件),然后原生移动端通过OC写module(功能模块类) ... 
