服务器使用环形缓冲区处理接收数据

(一).前言
通信过程中,对接收数据,若是网络环境糟糕,会出现多个数据包同时到达;

在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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值