
C#中Session类似于c/s版中的静态全局变量。
当连接上某个网站,就建立了一个Session,直到你断开于这个网站的连接,这个Session才销毁掉,可以把在整个过程都需要的信息保存在里面,比如登陆信息等等。
具体参考代码如下:
session["username"]="游客"这样就把字符串游客存进session,之后response.redirct("1.aspx")跳转回1.aspx.在1的load事件判断;
if(session["username"]!=null)
{
bt1.text = session["username"].tostring()+"已经登陆了"
}
session的用法就是这样,在使用session要增加一个全局应用程序类,就是新建项里面的global.asax。
。。你子线程控制同步了么? 断错误一般是内存 *** 作出错 和oci 或者pthread的关系不大!
void* OracleProcess(GPS_DATA GpsRec) // 数据库数据处理
{
interval = 0
struct HashItem* pHash
pHash = inithashtable(MAX_REC<<2)
char sql[384] = {0}
char temp[256] = {0}
char tName[10] = {0} // 表名字
int i,k
int j = TotalRec >>RATE
double distance
for(i=0i <ji++)
{
sprintf(temp,"%s%f%f%f%d",gps_last[i].tid,gps_last[i].lon,gps_last[i].lat,gps_last[i].speed,gps_last[i].udate)
InsertHash(temp, pHash, MAX_REC<<2) // 插入最后GPS信息到hash
memset(temp,0x00,256)
}
for(i = 0i <TotalReci++)
{
for(k=0k<jk++) // 查询车机是否在册
if(strcmp(GpsRec[i].tid,tid[k]) == 0)
break
if(k <j)
{
if(GpsRec[i].udate != 0.00)
{
distance = InfoUpdate(GpsRec,i)// 最新GPS数据更新
sprintf(temp,"%s%f%f%f%d",GpsRec[i].tid,GpsRec[i].lon,GpsRec[i].lat,GpsRec[i].speed,GpsRec[i].udate)
if(GetHashTablePos(temp, pHash, MAX_REC<<2) == -1) // 查找hash是否存在
{
if (distance >0.0001)
{
sprintf(tName,"GPS_%d_Y",tf[k])
InsertHash(temp, pHash, MAX_REC<<2)// 插入
sprintf(sql,"insert into %s (id,tm_id,lon,lat, speed, utc_time, udate,mileage,DIRECTION,DISTANCE) values (seq_gps.nextVal,'%s','%f','%f','%f','%d','%d','%f','%d','%f','%d')",
tName,GpsRec[i].tid,GpsRec[i].lon,GpsRec[i].lat,GpsRec[i].speed,GpsRec[i].utime,GpsRec[i].udate,GpsRec[i].mileage,GpsRec[i].dir,distance,interval)
printf("%s\n",sql)
oci_excu(oracle_env,(text *)sql,0) // 插入数据
memset(tName,0x00,10)
}
}
memset(sql,0x00,384)
memset(temp,0x00,256)
}
}
}
memset(GpsRec,0x00,sizeof(GpsRec))
free(pHash)
pthread_exit(NULL)
}
void TcpProcess(int tfd) // 处理TCP连接上的事务
{
struct timeval ntime
int index = 0,times,ret
int rlen = 0,rflag = 0
char recvbuf[513] = {0}
bzero(recvbuf,513)
while(1)
{
ret = rlen = read(tfd,recvbuf,512)
if(rlen <= 0)
break
if((rlen%32) == 0) // 32长度为标准TCP信息
{
times = 0
ret >>= 5
while(ret--)
{
if(tflag[tfd] == tfd) // 已经存在的socket
{
LOVENIX *info = (LOVENIX *)malloc(sizeof(LOVENIX))
memset(info,0x00,sizeof(LOVENIX))
if(recvbuf[times] == 0x58 || recvbuf[times] == 0x59)
ProtocolAnalysisLovenixTcp(&recvbuf[times],info)
else if(recvbuf[times] == 0x24)
ProtocolAnalysisLovenixUdp(&recvbuf[times],info)
sprintf(info->tid,"%s",seq[tfd]) // 合成车辆ID
DataProcess(info) // 处理GPS数据
free(info)
gettimeofday(&ntime, NULL)
cntime[tfd] = ntime.tv_sec // 更新时间
times += 32
}
}
}
else if(rlen >32)
{
if(!rflag)
{
if((index = RegLovenix(tfd,recvbuf)) >-1)
{
sprintf(seq[tfd],"%s",tid[index]) // 将对应的socket设备ID保存
gettimeofday(&ntime, NULL)
sfd[tfd] = tfd
cntime[tfd] = ntime.tv_sec
tflag[tfd] = tfd
rflag = 1
}
}
}
if(rlen <512) // 已经读完
break
memset(recvbuf,0x00,rlen)
}
}
void *TcpServer(void *arg)
{
int port = (unsigned int) arg
int efd,i
struct timeval ntime
int listener, nfds, n, listen_opt = 1, lisnum
struct sockaddr_in my_addr, their_addr
socklen_t len = sizeof(their_addr)
lisnum = MAXLISTEN
for(i=0i<MAX_RECi++)
{
sfd[i] = 0
tflag[i] = 0
}
if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) // 开启 socket 监听
{
lprintf(lfd, FATAL, "TCP Socket error!\n")
exit(1)
}
else
lprintf(lfd, INFO, "TCP socket creat susscess!\n")
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (void *) &listen_opt,(int) sizeof(listen_opt)) // 设置端口多重邦定
setnonblocking(listener)
bzero(&my_addr, sizeof(my_addr))
my_addr.sin_family = PF_INET
my_addr.sin_port = htons(port)
my_addr.sin_addr.s_addr = INADDR_ANY
if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
{
lprintf(lfd, FATAL, "TCP bind error!\n")
exit(1)
}
else
lprintf(lfd, INFO, "TCP bind susscess!\n")
if (listen(listener, lisnum) == -1)
{
lprintf(lfd, FATAL, "TCP listen error!\n")
exit(1)
}
else
lprintf(lfd, INFO, "TCP listen susscess!\n")
kdpfd = epoll_create(MAXEPOLLSIZE) // 创建 epoll句柄,把监听socket加入到epoll集合里
ev.events = EPOLLIN | EPOLLET // 注册epoll 事件
ev.data.fd = listener
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) <0)
lprintf(lfd, FATAL, "EPOLL_CTL_ADD error!\n")
while (1)
{
sem_wait(&sem_tcp)// 等待 sem_TCP
sem_wait(&sem_tp) // 将tp值减一
nfds = epoll_wait(kdpfd, events, MAXEPOLLSIZE, 1)// 等待有事件发生
if (nfds == -1)
lprintf(lfd, FATAL,"EPOLL_WAIT error!\n")
for (n = 0n <nfds++n) // 处理epoll所有事件
{
if (events[n].data.fd == listener) // 如果是连接事件
{
if ((efd = accept(listener, (struct sockaddr *) &their_addr,&len)) <0)
{
lprintf(lfd, FATAL, "accept error!\n")
continue
}
else
lprintf(lfd, INFO, "Client from :%s\tSocket ID:%d\n", inet_ntoa(their_addr.sin_addr) ,efd)
setnonblocking(efd) // 设置新连接为非阻塞模式
ev.events = EPOLLIN | EPOLLET// 注册新连接
ev.data.fd = efd
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, efd, &ev) <0) // 将新连接加入EPOLL的监听队列
lprintf(lfd, FATAL, "EPOLL_CTL_ADD error!\n")
else
{
gettimeofday(&ntime, NULL)
cntime[efd] = ntime.tv_sec
sfd[efd] = efd
}
}
else if (events[n].events &EPOLLIN)
tpool_add_work(pool, TcpProcess, (void*)events[n].data.fd) // 读取分析TCP信息
else
{
close(events[n].data.fd)
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd, &ev)
}
}
sem_post(&sem_cm)
sem_post(&sem_udp)
}
close(listener)
}
int DataProcess(LOVENIX *info) // 处理GPS数据
{
if(sflag == 0 &&(CacheRec != TotalRec)) // 缓存1可用且没有满
{
gps_cache[CacheRec].lat = info->lat
gps_cache[CacheRec].mileage = info->mileage
gps_cache[CacheRec].lon = info->lon
gps_cache[CacheRec].speed = atod(info->speed, strlen(info->speed))*0.514444444*3.6
gps_cache[CacheRec].udate = atoi(info->udate)
gps_cache[CacheRec].utime = atoi(info->utime)
gps_cache[CacheRec].dir = atoi(info->dir)
sprintf(gps_cache[CacheRec].tid ,"%s",info->tid)
CacheRec++
// printf("CacheRec %d\tTotalRec %d \t sflag:%d\n",CacheRec,TotalRec,sflag)
if(CacheRec == TotalRec)
{
sflag = 1
pthread_attr_init(&attr) // 初始化属性值,均设为默认值
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)// 设置线程为分离属性
if (pthread_create(&thread, &attr,(void*) OracleProcess,(void*)gps_cache)) // 创建数据处理线程
lprintf(lfd, FATAL, "oracle pthread_creat error!\n")
CacheRec = 0
}
}
else if(sflag == 1 &&(Cache1Rec != TotalRec)) // 缓存2可用且没有满
{
gps_cache1[Cache1Rec].mileage = info->mileage
gps_cache1[Cache1Rec].lat = info->lat
gps_cache1[Cache1Rec].lon = info->lon
gps_cache1[Cache1Rec].speed = atod(info->speed, strlen(info->speed))*0.514444444*3.6
gps_cache1[Cache1Rec].udate = atoi(info->udate)
gps_cache1[Cache1Rec].utime = atoi(info->utime)
gps_cache1[Cache1Rec].dir = atoi(info->dir)
sprintf(gps_cache1[Cache1Rec].tid ,"%s",info->tid)
Cache1Rec++
if(Cache1Rec == TotalRec)
{
sflag = 0
pthread_attr_init(&attr) // 初始化属性值,均设为默认值
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)// 设置线程为分离属性
if (pthread_create(&thread, &attr,(void*) OracleProcess,(void*)gps_cache1)) // 创建数据处理线程
lprintf(lfd, FATAL, "oracle pthread_creat error!\n")
Cache1Rec = 0
}
}
else
{
lprintf(lfd, FATAL, "No cache to use!\n")
return (0)
}
return (1)
}
0.什么是RedisRedis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
---维基百科
一般开发中用户状态使用session或者cookie,两种方式各种利弊。
Session:在InProc模式下容易丢失,并且引起并发问题。如果使用SQLServer或者SQLServer模式又消耗了性能
Cookie则容易将一些用户信息暴露,加解密同样也消耗了性能。
Redis采用这样的方案解决了几个问题,
1.Redis存取速度快。
2.用户数据不容易丢失。
3.用户多的情况下容易支持集群。
4.能够查看在线用户。
5.能够实现用户一处登录。(通过代码实现,后续介绍)
6.支持持久化。(当然可能没什么用)
2.实现思路
1.我们知道session其实是在cookie中保存了一个sessionid,用户每次访问都将sessionid发给服务器,服务器通过ID查找用户对应的状态数据。
在这里我的处理方式也是在cookie中定义一个sessionid,程序需要取得用户状态时将sessionid做为key在Redis中查找。
2.同时session支持用户在一定时间不访问将session回收。
借用Redis中Keys支持过期时间的特性支持这个功能,但是在续期方面需要程序自行拦截请求调用这个方法(demo有例子)
下面开始代码说明
3.Redis调用接口
首先引用ServiceStack相关DLL。
在web.config添加配置,这个配置用来设置Redis调用地址每台服务用【,】隔开。主机写在第一位
1 <appSettings>
2
3 <!--每台Redis之间用,分割.第一个必须为主机-->
4 <add key="SessionRedis" value="127.0.0.1:6384,127.0.0.1:6384"/>
5
6 </appSettings>
初始化配置
static Managers()
{
string sessionRedis= ConfigurationManager.AppSettings["SessionRedis"]
string timeOut = ConfigurationManager.AppSettings["SessionRedisTimeOut"]
if (string.IsNullOrEmpty(sessionRedis))
{
throw new Exception("web.config 缺少配置SessionRedis,每台Redis之间用,分割.第一个必须为主机")
}
if (string.IsNullOrEmpty(timeOut)==false)
{
TimeOut = Convert.ToInt32(timeOut)
}
var host = sessionRedis.Split(char.Parse(","))
var writeHost = new string[] { host[0] }
var readHosts = host.Skip(1).ToArray()
ClientManagers = new PooledRedisClientManager(writeHost, readHosts, new RedisClientManagerConfig
{
MaxWritePoolSize = writeReadCount,//逗写地链接池链接数
MaxReadPoolSize = writeReadCount,//逗读地链接池链接数
AutoStart = true
})
}
为了控制方便写了一个委托
/// <summary>
/// 写入
/// </summary>
/// <typeparam name="F"></typeparam>
/// <param name="doWrite"></param>
/// <returns></returns>
public F TryRedisWrite<F>(Func<IRedisClient, F>doWrite)
{
PooledRedisClientManager prcm = new Managers().GetClientManagers()
IRedisClient client = null
try
{
using (client = prcm.GetClient())
{
return doWrite(client)
}
}
catch (RedisException)
{
throw new Exception("Redis写入异常.Host:" + client.Host + ",Port:" + client.Port)
}
finally
{
if (client != null)
{
client.Dispose()
}
}
}
一个调用的例子其他的具体看源码
/// <summary>
/// 以Key/Value的形式存储对象到缓存中
/// </summary>
/// <typeparam name="T">对象类别</typeparam>
/// <param name="value">要写入的集合</param>
public void KSet(Dictionary<string, T>value)
{
Func<IRedisClient, bool>fun = (IRedisClient client) =>
{
client.SetAll<T>(value)
return true
}
TryRedisWrite(fun)
}
4.实现Session
按上面说的给cookie写一个sessionid
/// <summary>
/// 用户状态管理
/// </summary>
public class Session
{
/// <summary>
/// 初始化
/// </summary>
/// <param name="_context"></param>
public Session(HttpContextBase _context)
{
var context = _context
var cookie = context.Request.Cookies.Get(SessionName)
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
SessionId = NewGuid()
context.Response.Cookies.Add(new HttpCookie(SessionName, SessionId))
context.Request.Cookies.Add(new HttpCookie(SessionName, SessionId))
}
else
{
SessionId = cookie.Value
}
}
}
去存取用户的方法
/// <summary>
/// 获取当前用户信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public object Get<T>() where T:class,new()
{
return new RedisClient<T>().KGet(SessionId)
}
/// <summary>
/// 用户是否在线
/// </summary>
/// <returns></returns>
public bool IsLogin()
{
return new RedisClient<object>().KIsExist(SessionId)
}
/// <summary>
/// 登录
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
public void Login<T>(T obj) where T : class,new()
{
new RedisClient<T>().KSet(SessionId, obj, new TimeSpan(0, Managers.TimeOut, 0))
}
6.续期
默认用户没访问超过30分钟注销用户的登录状态,所以用户每次访问都要将用户的注销时间推迟30分钟
这需要调用Redis的续期方法
/// <summary>
/// 延期
/// </summary>
/// <param name="key"></param>
/// <param name="expiresTime"></param>
public void KSetEntryIn(string key, TimeSpan expiresTime)
{
Func<IRedisClient, bool>fun = (IRedisClient client) =>
{
client.ExpireEntryIn(key, expiresTime)
return false
}
TryRedisWrite(fun)
}
封装以后
/// <summary>
/// 续期
/// </summary>
public void Postpone()
{
new RedisClient<object>().KSetEntryIn(SessionId, new TimeSpan(0, Managers.TimeOut, 0))
}
这里我利用了MVC3中的ActionFilter,拦截用户的所有请求
namespace Test
{
public class SessionFilterAttribute : ActionFilterAttribute
{
/// <summary>
/// 每次请求都续期
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
new Session(filterContext.HttpContext).Postpone()
}
}
}
在Global.asax中要注册一下
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new SessionFilterAttribute())
}
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters)
}
5.调用方式
为了方便调用借用4.0中的新特性,把Controller添加一个扩展属性
public static class ExtSessions
{public static Session SessionExt(this Controller controller)
{
return new Session(controller.HttpContext)
}
}
调用方法
public class HomeController : Controller
{
public ActionResult Index()
{
this.SessionExt().IsLogin()
return View()
}
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)