(一).前言
通信过程中,对接收数据,若是网络环境糟糕,会出现多个数据包同时到达;
在recv 读取数据时,一般读取固定长度的数据,可能出现读出下图,读出不完整数据包的情况,若是直接处理,数据不完整,若是忽略,那么下次读取的时候,读到的数据也不完整,出现数据丢失,或数据发送错误的断言;
最近研究一些前辈对这种情况的处理方法,就是使用环形缓冲区,写这篇博客记录下;
(二).环形缓冲的介绍链接
http://blog.csdn.net/Aguangg_6655_la/article/details/55806382?locationNum=1&fps=1
(三).实现的源码
1.buf.h
#ifndef _COMM_BUF_H_
#define _COMM_BUF_H_
#include <sys/mman.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define UNUSED(x) ( (void)(x) )
typedef unsigned char u_char;
struct buf_s {
u_char *buf_file; /*mmap file*/
u_char *buf_start; /*内存起始位*/
u_char *buf_end; /*内存结束位*/
u_char *buf_pos; /*数据起始位*/
size_t buf_size; /*缓冲区总大小*/
size_t buf_len; /*当前占用空间大小*/
};
struct buf_s *buf_calloc(size_t size);
struct buf_s *buf_mmap(size_t size, const char *file);
int buf_append(struct buf_s **buf, u_char *data, size_t len);
void buf_reset(struct buf_s *buf);
int buf_pull(struct buf_s *buf, u_char *data, size_t len);
int buf_del(struct buf_s *buf, size_t len);
void buf_free(struct buf_s *buf);
#endif
2.buf.c
#include "buf.h"
/*
建立缓冲区结构,并分配内存
*/
struct buf_s *buf_calloc(size_t size)
{
struct buf_s *buf;
buf = (struct buf_s*)calloc(1, sizeof(struct buf_s));
if (buf == NULL) { /*内存分配出错*/
return NULL;
}
buf->buf_start = (u_char *)calloc(1, size);
if (buf->buf_start == NULL) {
free(buf);
return NULL;
}
buf->buf_size = size;
buf->buf_pos = buf->buf_start;
buf->buf_end = buf->buf_start + size;
/*buf_file, buf_len = 0, NULL*/
return buf;
}
/*
建立缓冲区结构,并分配内存
*/
#if !defined(_WIN32)
struct buf_s *buf_mmap(size_t size, const char *file)
{
struct buf_s *buf;
void *map;
int fd, i;
/*初始化结构*/
if ((buf = buf_calloc(size)) == NULL) {
buf_free(buf);
return NULL;
}
/*判断mmap文件是否存在*/
if (access(file ,F_OK) == -1) { /*file not exist*/
if ((fd = open(file, O_RDWR|O_CREAT)) == -1) {
buf_free(buf);
return NULL;
}
if (write(fd, buf->buf_start, size) == -1) {
buf_free(buf);
close(fd);
return NULL;
}
map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
buf_free(buf);
free(buf->buf_start);
close(fd);
return NULL;
}
close(fd);
free(buf->buf_start);
buf->buf_start = buf->buf_pos = (u_char *)map;
buf->buf_end = buf->buf_start + size;
buf->buf_file = (u_char*)file; /*need test*/
return buf;
} else { /*file exist*/
if ((fd = open(file, O_RDWR)) == -1) {
buf_free(buf);
return NULL;
}
map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
buf_free(buf);
free(buf->buf_start);
close(fd);
return NULL;
}
free(buf->buf_start);
close(fd);
buf->buf_start = (u_char*)map;
buf->buf_end = buf->buf_start + size;
buf->buf_file = (u_char*)file;
for (i=0; i<(int)size; i++) { /*找回pos位置*/
if (buf->buf_start[i] != 0x00) {
buf->buf_pos = buf->buf_start+i;
break;
}
}
for (i=(int)size-1; i>=0; i--) { /*找回len*/
if (buf->buf_start[i] != 0x00) {
buf->buf_len = buf->buf_start + i - buf->buf_pos + 1;
break;
}
}
return buf;
}
}
#endif
/*
添加数据进入缓冲区
*/
int buf_append(struct buf_s **buf, u_char *data, size_t len)
{
/*缓冲区溢出*/
if (((*buf)->buf_size - (*buf)->buf_len) < len) {
/*初始化结构*/
u_char *new_start = NULL;
size_t new_len = len + (*buf)->buf_len;
/*为buf_size的整数倍*/
new_len = (new_len + (*buf)->buf_size) & (~((*buf)->buf_size - 1));
/*重新分配内存*/
new_start = (u_char*)realloc((*buf)->buf_start, new_len);
if(!new_start){
return -1;
}
(*buf)->buf_start = new_start;
(*buf)->buf_size = new_len;
}
/*移动数据*/
//printf("len:%d end:%x pos:%x\n", len, buf->buf_end, buf->buf_pos);
if ((size_t)((*buf)->buf_end - (*buf)->buf_pos) < len) {
memmove((*buf)->buf_start, (*buf)->buf_pos, (*buf)->buf_len);
memset((*buf)->buf_start+(*buf)->buf_len, 0, (*buf)->buf_size-(*buf)->buf_len);
(*buf)->buf_pos = (*buf)->buf_start;
}
/*添加数据*/
memcpy((*buf)->buf_pos+(*buf)->buf_len, data, len);
(*buf)->buf_len += len;
return (*buf)->buf_len;
}
void buf_reset(struct buf_s *buf)
{
memset(buf->buf_start, 0, buf->buf_size);
buf->buf_pos = buf->buf_start;
buf->buf_len = 0;
}
/*
将数据从缓冲区读出来
*/
int buf_pull(struct buf_s *buf, u_char *data, size_t len)
{
int ret;
if (buf->buf_len == 0) {
errno = ENODATA;
return -1;
}
if (len >= buf->buf_len) {
memcpy(data, buf->buf_pos, buf->buf_len);
memset(buf->buf_pos, 0, buf->buf_len);
buf->buf_pos = buf->buf_start;
ret = buf->buf_len;
buf->buf_len = 0;
//buf->buf_pos = buf->buf_pos +len;
}else {
memcpy(data, buf->buf_pos, len);
memset(buf->buf_pos, 0, len);
buf->buf_pos = buf->buf_pos +len;
buf->buf_len = buf->buf_len -len;
ret = len;
}
return ret;
}
/*
将数据从缓冲区删除
*/
int buf_del(struct buf_s *buf, size_t len)
{
int ret;
if (buf->buf_len == 0) {
errno = ENODATA;
return -1;
}
if (len >= buf->buf_len) {
memset(buf->buf_pos, 0, buf->buf_len);
buf->buf_pos = buf->buf_start;
ret = buf->buf_len;
buf->buf_len = 0;
//buf->buf_pos = buf->buf_pos +len;
}else {
memset(buf->buf_pos, 0, len);
buf->buf_pos = buf->buf_pos +len;
buf->buf_len = buf->buf_len -len;
ret = len;
}
return ret;
}
/*
释放内存
*/
void buf_free(struct buf_s *buf)
{
if(buf == NULL) {
return;
}
if (buf->buf_file != NULL) {
UNUSED(NULL);
#if !defined(_WIN32)
munmap(buf->buf_start, buf->buf_size);
#endif
}
else {
if(buf->buf_start)
free(buf->buf_start);
}
free(buf);
}
(四).在通信中的使用
RobotManager.h
#ifndef __CROBOTMANAGER_H__
#define __CROBOTMANAGER_H__
#include "../include/head.h"
typedef class RobotList RL;
class CRobotManager
{
public:
CRobotManager();
~CRobotManager();
void sendDateByTerminal(const char *readBuf);
static void * recvDate(void *arg);
bool startThread();
bool machine_client();
void freeBuffer();
void dispatchRecvPacket();
inline void setRobotList(RL *robot)
{
m_cRobotList = robot;
}
private:
pthread_t pth_recv;
int m_nSfd;
bool m_boStartRobot;
RL * m_cRobotList;
struct buf_s *m_pReadBuf;
};
#endif
RobotManager.cpp
#include "RobotManager.h"
#include "../message/MsgPacket.h"
#include <signal.h>
#include <errno.h>
#include "../buf/buf.h"
CRobotManager::CRobotManager()
{
pth_recv = 0;
m_nSfd = -1;
m_boStartRobot = false;
m_pReadBuf = NULL;
}
CRobotManager::~CRobotManager()
{
if(pth_recv)
pthread_join(pth_recv, NULL);
freeBuffer();
}
void CRobotManager::freeBuffer()
{
if(m_pReadBuf)
{
buf_free(m_pReadBuf);
m_pReadBuf = NULL;
}
}
void CRobotManager::dispatchRecvPacket()
{
CMsgHead *pPacketHead = NULL;
while(m_pReadBuf->buf_len){
if(m_pReadBuf->buf_len < sizeof(CMsgHead)){
return;
}
if(m_pReadBuf->buf_len < 0 || m_pReadBuf->buf_len > m_pReadBuf->buf_size)
{
return;
}
pPacketHead = (CMsgHead *)m_pReadBuf->buf_pos;
if(!pPacketHead)
{
return;
}
if(pPacketHead && pPacketHead->m_flag != 0xFFFF)
{
printf("[warning]:packet header:msg.m_head.m_flag != 0xFFFF !!!!!\n");
u_char *dataPos = m_pReadBuf->buf_pos;
size_t bufSize = m_pReadBuf->buf_len;
do{
if(bufSize < sizeof(CMsgHead))
{
buf_del(m_pReadBuf, m_pReadBuf->buf_len);
printf("can not research packet header, packet droped\n");
return;
}
bufSize++;
dataPos++;
pPacketHead = (CMsgHead *)dataPos;
}while(pPacketHead->m_flag != 0xFFFF);
size_t moveLen = dataPos - m_pReadBuf->buf_pos;
buf_del(m_pReadBuf, moveLen);
}
if(pPacketHead->m_msgLen < 0)
{
if(m_pReadBuf->buf_len < sizeof(CMsgHead))
{
buf_reset(m_pReadBuf);
}else{
buf_del(m_pReadBuf, sizeof(CMsgHead));
}
return;
}
if(m_pReadBuf->buf_len < (pPacketHead->m_msgLen + sizeof(CMsgHead)))
{
return;
}
int nTotalSize = pPacketHead->m_msgLen + sizeof(CMsgHead);
int nLen = nTotalSize;
nTotalSize = (nTotalSize + 128) & (~(128 - 1));
CMsgPacket msg(nTotalSize);
msg.setPacketHead((char*)m_pReadBuf->buf_pos);
msg.setPacketBody((char*)m_pReadBuf->buf_pos);
Value readRoot = msg.getReadRoot();
if(!readRoot[RERROR].isNull() || readRoot[RERROR].isBool())
{
return;
}
int type = readRoot[TYPE].asInt();
switch(type)
{
case 1001:
{
break;
}
default:
{
break;
}
}
buf_del(m_pReadBuf, nLen);
}
}
void *CRobotManager::recvDate(void *arg)
{
CRobotManager * robotManager = (CRobotManager *)arg;
unsigned char recvBuf[1024];
while(robotManager->m_boStartRobot){
memset(recvBuf, 0, 1024);
int len = recv(robotManager->m_nSfd, recvBuf, 1024, 0);
if(len == 0)
{
close(robotManager->m_nSfd);
robotManager->m_boStartRobot = false;
printf("recv len = 0\n");
}else if(len < 0){
if(errno == EAGAIN || errno == EINTR)
continue;
close(robotManager->m_nSfd);
robotManager->m_boStartRobot = false;
printf("recv len < 0\n");
}
buf_append(&robotManager->m_pReadBuf, recvBuf, len);
robotManager->dispatchRecvPacket();
}
return NULL;
}
void CRobotManager::sendDateByTerminal(const char *readBuf)
{
char sendBuf[1024];
memset(sendBuf, 0, 1024);
CMsgPacket packet(1024);
Value sendRoot;
if(strncmp(readBuf, "1016", 4) == 0)
{
sendRoot[TYPE] = 1016;
sendRoot[UID] = m_nUserId;
}
else if(strncmp(readBuf, "1002", 4) == 0)
{
sendRoot[TYPE] = 1002;
sendRoot["phoneId"] = "A1";
sendRoot["systemType"] = 1;
sendRoot["account"] = "ssh5";
sendRoot["password"] = "application";
}
else
{
printf("please input operate instruction: \n");
return;
}
packet.setSendStrBody(sendRoot);
int len_packet = packet.enpacket(sendBuf);
int write_len = write(m_nSfd, sendBuf, len_packet);
if(write_len < 0)
{
close(m_nSfd);
m_boStartRobot = false;
}
}
bool CRobotManager::startThread()
{
printf("startThread\n");
m_pReadBuf = buf_calloc(16*1024);
if(m_pReadBuf == NULL){
return false;
}
if(pthread_create(&pth_recv, NULL, recvDate, (void*)this) != 0)
{
return false;
}
if(!m_cRobotList->m_cTime.startTime()){
return false;
}
m_cRobotList->m_cTime.setStartHeart(true);
return true;
}
bool CRobotManager::machine_client()
{
struct sockaddr_in m_addr;
int sfd = 0;
if((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("machine socket error\n");
return false;
}
bzero(&m_addr, sizeof(struct sockaddr_in));
m_addr.sin_family = AF_INET;
struct hostent *hptr = gethostbyname(MACHINE_IP);
char ip[32];
sprintf(ip, "%s", inet_ntoa(*(struct in_addr *)hptr->h_addr_list[0]));
if(inet_pton(AF_INET, ip, &m_addr.sin_addr)<0)
{
sfd = 0;
return false;
}
m_addr.sin_port = htons(MACHINE_PORT);
if(connect(sfd, (struct sockaddr *)&m_addr, sizeof(m_addr)) < 0)
{
return false;
}
m_nSfd = sfd;
if(!startThread())
{
close(sfd);
return false;
}
m_boStartRobot = true;
return true;
}