360行车记录仪项目开发日志(服务端开发)(二)前后置服务器通信

高并发服务器整体框架

前置服务器

通常前置机从架构上解决两个问题

1,前端很多种类业务数据需要与后端核心业务数据交互,如果后端核心业务一个类型开发一个接口肯定是得不偿失的,所以利用前置机做数据收集清洗转换格式汇总给后端。访问路径是业务客户端到前置机伪服务端,伪服务端也就是真正的客户端然后最后才是业务服务器的服务端。

2,如果接口多了核心业务安全性也会下降,那有了前置机,大家先访问前置机,而不直接访问服务器,一般再配合审计的安全手段,很好的隐藏了后端服务器的存在。

前置服务器和后置服务器通信

这里我选用共享内存

共享内存

共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。

共享内存的特点

1、共享内存是以传输数据为目的
2、共享内存无同步无互斥
3、共享内存是所有进程间通信速度最快的。
4、共享内存的生命周期随内核

共享内存的优缺点

1、优点:使用共享内存进行进程间的通信非常方便,而且函数的接口也简单,数据的共享使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
 

由于其没有同步互斥的机制,且双方通过共享内存通信无法知晓什么时候写入,什么时候读取,修改了共享内存哪一段内容,因此我结合了信号量,以及消息队列来完成前后置服务器的消息互通

共享内存架构

 我将共享内存划分为一个一个的数据块,并在开头加上索引区,通过索引区找到对应的数据,具体实现,是由于共享内存本身是内存,可以通过地址偏移来划分区域。

共享内存类封装

由于要让线程池的任务都访问同一块共享内存,所以我将共享内存封装成单例类,只能初始化一次,而且不同线程访问共享内存需要互斥访问,因此我还加入了一个共享内存锁.

CEnjoymemory.h

#include<sys/types.h>
#include<sys/ipc.h>
#include<stdio.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <string.h>
#include <pthread.h>
#include "protocol.h"
#define DATASIZE 50 //共享内存每块的大小
using namespace std;
class CEnjoyMemory
{
    //构造函数私有化
	CEnjoyMemory();
	int shmid;
	int msggid;
	int semid;
	void* shmaddr;
    //提供静态私有实例化对象指针
	static CEnjoyMemory* Instance;
public:
    //提供静态公有接口
	static CEnjoyMemory* getInstance();
	void write(void* arg,int type);
	static pthread_mutex_t memorymutex;

};

信号量操作


union semun {
	int              val;    /* 设置值 */
	struct semid_ds* buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short* array;  /* 设置全部 获取全部 */
	struct seminfo* __buf;  /* Buffer for IPC_INFO
								(Linux-specific) */
};

typedef struct sendmsgbuf {
	long mtype;       /* message type, must be > 0 */
	char mtext[1];    /* message data */
}MSG;

int sem_create(key_t key, int nsems)
{
	int res = semget(key, nsems, IPC_CREAT | 0777);
	if (res < 0)
	{
		perror("create error");
	}
	return res;

}

int sem_setVal(int semid, int semidex, int val)
{
	union semun arg;
	arg.val = val;
	int res = semctl(semid, semidex, SETVAL, arg);
	if (res < 0)
	{
		perror("set error");
	}
	return res;
}

//信号量P操作 上锁-1
int sem_p(int semid, int semidex)
{
	struct sembuf buf = { semidex,-1,SEM_UNDO };
	int res = semop(semid, &buf, 1);
	if (res < 0)
	{
		perror("semop p error");
	}
	return res;
}


//信号量V操作 解锁 +1
int sem_v(int semid, int semidex)
{
	struct sembuf buf = { semidex,1,SEM_UNDO };
	int res = semop(semid, &buf, 1);
	if (res < 0)
	{
		perror("semop v error");
	}
	return res;
}

 CEnjoymemory.cpp

CEnjoyMemory::CEnjoyMemory()
{
	this->shmid = shmget((key_t)10001, sizeof(int)*100 + DATASIZE * 100, IPC_CREAT | 0777);
	this->msggid = msgget((key_t)10086, IPC_CREAT | 0777);
	if ((shmid == -1) && (msggid == -1))
	{
		perror("create error");
	}
	this->semid = sem_create((key_t)10002, 1);
	sem_setVal(semid, 0, 1);
	shmaddr = shmat(shmid, NULL, 0);
}



CEnjoyMemory* CEnjoyMemory::getInstance()
{
	if (CEnjoyMemory::Instance == NULL)
	{
		CEnjoyMemory::Instance = new CEnjoyMemory();
	}
	return Instance;
}

void CEnjoyMemory::write(char* arg)
{
	
	int flag[100] = { 0 };
	int index = 0;
	MSG buf = { 0 };
	cout << "写进程开始" << endl;
	sem_p(this->semid, 0);
	//读索引区
	memcpy(flag, shmaddr, sizeof(flag));
	for (int i = 0; i < (sizeof(flag) / sizeof(int)); i++)
	{
		//索引区为0,说明此块没数据 可以写
		if (flag[i] == 0)
		{
			index = i;
			break;
		}
	}
	cout << "index=" << index << endl;
	flag[index] = 1;
	//先写索引区
	memcpy((void*)(shmaddr + index * sizeof(int)), &flag[index], sizeof(int));
	memcpy((void*)(shmaddr + sizeof(flag) + DATASIZE * index), arg, DATASIZE);
	buf.mtype = 1;
	//将第几块作为消息队列的信息发送,告诉读端读第几块内存
	sprintf(buf.mtext, "%d", index);
	if (msgsnd(msggid, &buf, sizeof(MSG), 0) == -1)
	{
		perror("send error");
		return;
	}
	bzero(&buf, sizeof(MSG));
	cout << "写完数据了" << endl;
	sem_v(semid, 0);
	//sleep(3);
}

线程写入内容部分代码

mptr = CEnjoyMemory::getInstance();

//单例共享内存写入
pthread_mutex_lock(&CEnjoyMemory::memorymutex);
mptr->write(buf);
pthread_mutex_unlock(&CEnjoyMemory::memorymutex);

后置服务器读

void Rmemory::run()
{
	int flag[100] = { 0 };//用来读索引区
	int index = 0;
	MSG rbuf = { 0 };
	login LG = { 0 };
	videoupload VD = { 0 };
	videolist VL = { 0 };
	videoplay VP = { 0 };
	playrecord PR = { 0 };
	finder FD = { 0 };
	int id = 0;
	while (1)
	{
		cout << "读进程开始" << endl;
		if (msgrcv(msggid, &rbuf, sizeof(MSG), 1, 0) == -1)
		{
			perror("rcv error");
			return ;
		}
		sem_p(semid, 0);
		int num = atoi(rbuf.mtext);
		cout << "读到的下标是" << num << endl;
		shmaddr = shmat(shmid, NULL, 0);
		memcpy(&(flag[num]), (void*)(shmaddr + (num * sizeof(int))), sizeof(int));
		if (flag[num] == 1)
		{
			memcpy(&FD, (void*)(shmaddr + sizeof(flag) + num * DATASIZE), sizeof(FD));
			if (FD.type == 1)//登录
			{
				memcpy(&LG, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(LG));
				loginTask* tk = new loginTask(id++, FD.fd, LG);
				pool->addTask(tk);

			}
			else if (FD.type == 2)//注册
			{
				memcpy(&LG, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(LG));
				registerTask* tk = new registerTask(id++, FD.fd, LG);
				pool->addTask(tk);
			}
			else if (FD.type == 3)//视频上传
			{
				memcpy(&VD, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(VD));
				videouploadTask* tk = new videouploadTask(id++, FD.fd, VD);
				pool->addTask(tk);
			}
			else if (FD.type == 4)//视频列表
			{
				memcpy(&VL, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(VL));
				videolistTask* tk = new videolistTask(id++, FD.fd, VL);
				pool->addTask(tk);
			}
			else if (FD.type == 5)//视频播放
			{
				memcpy(&VP, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(VP));
				videoplayTask* tk = new videoplayTask(id++, FD.fd, VP);
				pool->addTask(tk);
			}
			else if (FD.type == 6)//视频播放记录上传
			{
				memcpy(&VP, (void*)(shmaddr + sizeof(flag) + num * DATASIZE + sizeof(FD)), sizeof(VP));
				videoplayTask* tk = new videoplayTask(id++, FD.fd, VP);
				pool->addTask(tk);
			}
			//清空数据区数据 
			memset((void*)(shmaddr + sizeof(flag) + num * DATASIZE), 0, DATASIZE);
			flag[num] = 0;
			memcpy((void*)(shmaddr + (num * sizeof(int))), &flag[num], sizeof(int));
			/*cout << "id=  " << LG.name << endl;
			cout << "passwd=  " << LG.passwd << endl;*/
			cout << "读完数据" << endl;
		}
		//shmdt(shmaddr);
		sem_v(semid, 0);
	}

}

运行结果

客户端访问服务器做登录业务

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 360m320行车记录仪电路图,是指一款用于汽车行车记录的电子产品的电路图。电路图呈长方形,长360个像素,宽320个像素,其中显示了该记录仪的主要电子元件以及它们之间的连接方式。 该电路图通常包括主控芯片、图像传感器、存储卡模块、LCD显示屏、电源模块、GPS模块等常用元件。主控芯片作为该记录仪的核心,负责数据处理和功能控制;图像传感器则负责采集拍摄路况画面;存储卡模块用于保存路况记录数据;LCD显示屏用于实时显示拍摄的画面;电源模块则提供电能供电;GPS模块则能够定位并标记记录数据发生时的位信息。 在电路图上,这些元件之间通过一些元器件,如电阻、电容、极管、晶体管等,进行连接,从而完成电路的构建。整个电路可以呈现一个闭环,使得记录仪能够正常工作。 360m320行车记录仪电路图对于工程师们进行产品设计和生产非常重要,同时也对用户自行维修有指导作用。在使用记录仪时,有必要认真阅读电路图,正确使用,以确保其正常工作,提高驾车安全。 ### 回答2: 360m320行车记录仪电路图,是指一种用于汽车行车记录的电路板图示,总体上由五个模块组成,包括主板、摄像头、GPS模块、存储芯片、以及电源模块。 主板是整个电路的核心,其中包括了处理器、视频芯片以及其他必要的控制器。主板还连接了GPS模块和存储芯片,在电路中起着很重要的作用。 摄像头是记录行车画面的核心,主要由图像传感器、稳定会振动的装和悬挂装等组成。摄像头要能够地面记录图片,并要具备拍摄范围广、画质清晰、反应迅速等特点。 GPS模块主要记录行车轨迹和地理位信息,由一个GPS芯片和一个天线组成。其采用卫星式的定位方式,可以准确地记录行车轨迹和地理位。 存储芯片用于存储行车记录的视频、音频和GPS信息,其中比较流行的存储芯片有SD卡、TF卡和硬盘等,可以实现视频循环录制,即覆盖之前录制的内容。 电源模块含有整个电路板的供电模块,它要求为其他模块提供足够的电源,以确保支持行车记录仪的正常工作。 基于以上的五个模块构成,360m320行车记录仪电路图在行车安全、事故责任分析、交警执法和个人证明等方面具有不可替代的重要作用。 ### 回答3: 首先,需要了解行车记录仪是一种车载电子设备,它可以记录车辆行驶过程中的图像和数据,并作为事故证据。在行车记录仪的电路图中,有以下重要元件和电路: 1. 高清摄像头:行车记录仪的核心部件,用于拍摄车辆前方的图像。高清摄像头需要配备适当的光圈、焦距和传感器,以便清晰地记录行车信息。 2. 存储器:行车记录仪需要一个存储器来存储拍摄的图像和数据。一般来说,这里使用的是固态硬盘或微型SD卡。 3. 处理器:行车记录仪需要一个处理器来处理数据并将其存储到存储器中。处理器需要满足足够的计算速度以支持高清图像的处理。 4. 车辆电源:行车记录仪需要连接到车辆的电源系统中,以便提供必要的电源和充电。 5. 显示屏:有些行车记录仪可能具有内显示屏,用于在行车过程中查看记录的数据和图像。 6. 传感器:一些高级行车记录仪可以使用传感器来检测车辆的加速度、转向和速度等信息。 7. 联网模块:一些高级行车记录仪可能具有内的联网模块,以便与云端服务器相连,向用户提供远程查看和管理的功能。 总之,360m320行车记录仪电路图中充满了高科技元件和令人惊叹的电路设计,可以提供给用户最优质的行车体验,并为用户提供完美的事故证据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值