关于在tcp或udp上搭载私有协议

很多人都碰到这个问题,需要在tcp或者udp上面搭载私有协议,比如xmodem,和telnet一样,和服务器进行交互。此协议经过测试可以实现32k文件传输。

可以进行再扩充,但是 工作时30k大小足够,所以就没对协议进行修改。

以下是协议内容:

协议相关的控制符:

    SOH             0x01     //包头
    STX             0x02 //开始传输数据
    EOT             0x04
    ACK             0x06
    NAK             0x15
    CAN             0x18
    CTRLZ             0x1A

每个包的长度为

 ___________________________________________________________________________________
|     |            |                  

|  1   |      1  |   1 |   1    |    1    |    256b    |      1   |
| SOH | 剩余包数 | 信息包序号 | 包类别 |   包长度|  数据区段  |  校验和   |
|_____|____________|___________________|__________|____________|___________________


说明:

SOH 帧的开头字节,代表信息包中的第一个字节;

剩余包数:这个包一共还有几个包未接收。

信息包序号: 256取模所得到当前包号,第一个信息包的序号为 1,而信息包序号范围 0~255

包类别:属于那种类型的数据包

包长度:当前包中有效数据的长度;

  数据区段: 数据区段的长度固定为 128字节(属于消息内容);

  校验和: 使用的是异或校验;


接收过程:

接收一个包的内容,接收后进行处理(数据校验),处理成功后发送ACK(一个字节)给发送方.否则发送NAK给发送方。

若还存在着包,继续接收这个包。

发送过程:

若发送包的数据小于256b时,多余部分用CTRLZ 进行填充

 发送一个包的内容,等待接收方ACK确认,若没有收到ACK,则重新发送。

若包的内容没有发完,继续发送包。

以下就是源码:我将发送的内容封装成一个包进行发送,,以下是一个包

packet.h,和packet.c

#ifndef _PACKET_H_
#define _PACKET_H_

这个是我定义的协议包类别:
typedef enum _packet_type_t{
    DP_TYPE_SN=0,            /*S 桌牌标号*/
    DP_TYPE_CARD=1,            /*R 桌牌名片*/
    DP_TYPE_SUMMARY=2,        /*R 会议概要(与会人员 会议单位,会议标题 系统时间等)*/
    DP_TYPE_CONTENT_PIC=3,    /*R 会议内容,以图片形式发送*/
    DP_TYPE_CHAPT_PIC=4,    /*R 会议议程,以图片形式*/
    DP_TYPE_SIGN_MSG=5,     /*S 签到消息*/
    DP_TYPE_SCROLL_MSG=6,    /*R 滚动消息*/
    DP_TYPE_SERVER=7,        /*S 发送呼叫服务消息*/
    DP_TYPE_MESSAGE=8,        /*R 服务器下发短信息*/
    DP_TYPE_VOTE_CMD=9,        /*R 服务器发起投票*/
    DP_TYPE_VOTE_RESULT=10,    /*S 发送投票结果*/
    DP_TYPE_UPDAT_DESKCARD =12,//更新桌排
    DP_TYPE_CMD_DOWNLOAD_MESSAGE,//=100,    /*下载系统消息*/
    DP_TYPE_CMD_DOWNLOAD_MEETING_DATA,    /*下载会议数据包*/
    DP_TYPE_CMD_DOWNLOAD_DESK_LOGO,//桌牌名片
    DP_TYPE_CMD_DOWNLOAD_UPG_FW,//下载固件
    DP_TYPE_CMD_HEARTBEAT_PACKET_REQUEST =99,//请求发送一个心跳包
    DP_TYPE_CMD_HEARTBEAT_PACKET_RECEIVE =100,
}T_Packet_Type,*PT_Packet_Type;

enum{
//1代表茶水,2代表纸笔,3代表麦克风,4代表服务人员
    SERVICE_DRINKS=1,
    SERVICE_PEN,
    SERVICE_MIC,
    SERVICE_WAITER
};


extern  int sockfd;
int send_data(T_Packet_Type type,unsigned char *data,int len);
int receive_data(T_Packet_Type *type,unsigned char *data,int *len);

//#define     packet_debug            printf
#define     packet_debug(...)

//使用4 字节的头
#define     USE_HEAD_4

#endif//_PACKET_H_

以下是packet.c的内容:

#include "packet.h"
#include "config.h"

#define            SOH             0x01
#define            STX             0x02
#define            EOT             0x04
#define            ACK             0x06
#define            NAK             0x15
#define            CAN             0x18
#define            CTRLZ           0x1A

#define    PACKET_DATA_LEN            128

#define    RETRY_TIMES                (1)

//一个包的结构体定义,接收时按这个结构体进行接收
typedef struct packet_t{
    unsigned char packet_head;
    unsigned char left_packet_num;
    unsigned char packet_num;
    unsigned char packet_type;
    unsigned char packet_data_len;
    unsigned char data[PACKET_DATA_LEN];
    unsigned char check_sum;
}T_Packet,*PT_Packet;

//进行分包服务的
typedef struct service_list_t{
    T_Packet packet;
    struct service_list_t *next;
}T_Service_List,*PT_Service_List;

//是否是包头
#define  isPacketHead(phead)    (*phead==STX)   

//设置为包头

#define  set_packet_Head(phead) (*(phead))=STX

//调试信息
void debug_print_packet(PT_Packet packet)
{

    printf( "head %d %d %d %d %d ",packet->packet_head,packet->left_packet_num,\
        packet->packet_num,packet->packet_type,packet->packet_data_len);
    printf("   checksum =%d \r\n", packet->check_sum);

}

//最基础的一个发送数据,按发送数据大小进行发送。

int client_tcp_send(unsigned char *buf , int size)
{
    int len;
    int ret = 0,  retry =0;
    do{
        len = send(sockfd, buf, size, 0);
        if(len < 0)
        {
            if(errno == EAGAIN)//无数据读
            {
                ret = 0;
                break ;
            }
            else if(errno == EINTR)//由于信号中断没有读到数据
            {
                ret = 0;
                break ;
            }else
            {
                ret = -1;
                return ret;
            }
        }else if(len == 0)
        {    
            ret = -1;
            return ret;
        }        
        buf += len;
        size -= len;
        ret += len;
    }while(size > 0);

    return ret;
}

//这个fd是否具有可读属性
int sock_can_read(int fd)
{
    int rc;
    fd_set fds;
    static struct timeval out={0,40000};
    FD_ZERO(&fds);
    FD_SET(fd,&fds);

    out.tv_sec  = 0;
    out.tv_usec = 40000;//0.1s
    
    rc = select(fd+1, &fds, NULL, NULL, &out);
    if (rc < 0)   //error
        return -1;    

    return FD_ISSET(fd,&fds) ? 1 : 0;
}

//最基础的一个接收数据,按接收数据大小进行接收。

//返回0 表示 无数据 返回-1 网络错误 返回>0 读取的字节数
int client_tcp_recv(unsigned char *buf, int size)
{    
    int len;
    int ret = 0;
    do{
        if(sock_can_read(sockfd) != 1){
            //printf("no data ret =%d\r\n",ret);
            ret =0;
            break;
        }
        len = recv(sockfd, buf, size, 0);
        if(len < 0)
        {
            if(errno == EAGAIN)//无数据读
            {
                ret = 0;
                break ;
            }
            else if(errno == EINTR)//由于信号中断没有读到数据
            {
                ret = 0;
                break;
            }else
            {
                ret = -1;
                return ret;
            }
        }else if(len == 0)        
        {    
            return ret;
        }        
        buf += len;
        size -= len;
        ret += len;
    }while(size > 0);

    return ret;
}

//每个接收服务都是一个单链表,在接收过程或发送过程中是动态分配的,需要手动释放
static int free_one_service(PT_Service_List list)
{
    if(list)
    {
        free(list);
        list = NULL;
        //list->next = NULL;
    }
}
//添加一个服务包
static int _addOneService(PT_Service_List *phead, PT_Service_List list)
{

    if(list == NULL && phead == NULL)
        return 0;

    PT_Service_List head = *phead;

    if(head == NULL){
        *phead = list;//修改下
        list->next =NULL;
        return 1;
    }

//不是同一个类别不添加
#if 0
    if(head->packet.packet_type != list->packet.packet_type){
        printf("not same type\r\n");
        return 0;
    }
#endif
    while(head->next)
    {
        //printf("next\r\n");
        head = head->next;
    }

    list->next =NULL;
    head->next = list;
    
    return 1;
}

static unsigned char _calculate_check_sum(PT_Packet packet)
{
    unsigned char i =0;
    unsigned check_sum = 0;
    
    check_sum = packet->left_packet_num;
    check_sum ^=packet->packet_num;
    check_sum ^=packet->packet_type;
    check_sum ^=packet->packet_data_len;

    for(i=0;i < PACKET_DATA_LEN ;i++)
    {
        check_sum ^= packet->data[i];
    }

    return check_sum;
}    

static int _free_all_service_list(PT_Service_List list_head)
{
    PT_Service_List phead = list_head,pnext;

    packet_debug("enter _free_all_service_list\r\n");
    if(phead == NULL)
        return 0;
    
    while(phead)
    {
        pnext = phead->next;
        free_one_service(phead);
        phead = pnext;
    }

    packet_debug("exit _free_all_service_list\r\n");
}



//准备一个发送列表包
static PT_Service_List _prepare_send_packet(T_Packet_Type type,unsigned char *data,int len)
{
    if(data == NULL)
        return;
    //int len = strlen(data);
    int index=0;
    int max_index = (len / PACKET_DATA_LEN) + 1;

    PT_Service_List head =NULL ,service_list =NULL;

    packet_debug("max_index =%d \r\n",max_index);
    while(max_index)
    {
        service_list = (PT_Service_List)malloc(sizeof(T_Service_List));
        set_packet_Head(&service_list->packet.packet_head);
        //service_list->packet.packet_head = STX;
        service_list->packet.left_packet_num = max_index-1;
        service_list->packet.packet_num  = index+1;
        service_list->packet.packet_type = (unsigned char)type;
        
        if(max_index == 1)
        {
            memset(service_list->packet.data, CTRLZ, PACKET_DATA_LEN);
            service_list->packet.packet_data_len = len%PACKET_DATA_LEN;
            memcpy(service_list->packet.data , data+index*PACKET_DATA_LEN,service_list->packet.packet_data_len);
        }else{
            service_list->packet.packet_data_len = PACKET_DATA_LEN;
            memcpy(service_list->packet.data , data+index*PACKET_DATA_LEN,PACKET_DATA_LEN);
        }
//计算校验和    
        service_list->packet.check_sum   = _calculate_check_sum(&service_list->packet);
        service_list->next =NULL;
        //debug_print_packet(service_list);
        _addOneService(&head,service_list);
        index++;
        max_index--;
    }

    return head;
}

//等待ack确认 返回值为-1  网络错误
// 返回值为 1  收到ack
//返回值为2 收到nak,
//否则接收失败

//就是发送一个ACK 字符

//发送成功 返回1  发送错误返回0  网络错误返回-1
int _send_packet(PT_Packet  packet)
{
    int ret,retry = RETRY_TIMES;
send_again:
    ret = client_tcp_send((unsigned char*)packet , sizeof(T_Packet));
    if(ret < 0)
        return ret;
    else
    if(ret == sizeof(T_Packet))
        return 1;

    if(retry > 0)
    {
        retry--;
        goto send_again;
    }else{
        
        return 0;//发送失败
    }

    //ack 1
    return 1;
}


static int _send_service_list(PT_Service_List list)
{
    int ret =1;

    while(list){
        //debug_print_packet(&list->packet);
        ret = _send_packet(&list->packet);
        if(ret <= 0)
            break;
        list = list->next;
    }

    return ret;
}


//进行数据包的校验,校验成功返回1 否则返回0
//异或校验
static int _checkSum(PT_Packet packet)
{
    unsigned char i =0;
    unsigned check_sum = 0;

    if(packet ==0 || !isPacketHead(&packet->packet_head)){
    //if(packet ==0 || packet->packet_head != STX){
        //printf("packet =%d packet->packet_head =%d\r\n",packet,packet->packet_head);
        return 0;
    }

    check_sum = packet->left_packet_num;
    check_sum ^=packet->packet_num;
    check_sum ^=packet->packet_type;
    check_sum ^=packet->packet_data_len;

    for(i=0;i < PACKET_DATA_LEN ;i++)
    {
        check_sum ^= packet->data[i];
    }

    //
    if(check_sum == packet->check_sum)
        return 1;
    else{
        printf("check_sum =%d , packet->check_sum =%d \r\n",check_sum,packet->check_sum);
        return 0;
    }
}


//接收 一个数据包接收到了一个数据包返回1  
//网络异常 -1    没有收到一个包,或数据出错返回0
static int _receive_Onepacket(PT_Packet packet)
{
    int ret =0,retry = RETRY_TIMES;
    unsigned char  head;
    unsigned char *ppacket = (unsigned char*)packet;
    ppacket = ppacket + sizeof(head);
read_head:
    ret = client_tcp_recv((unsigned char*)(&head),sizeof(head));
    if(ret < 0)
        return ret;
    
    if(ret != sizeof(head))
        return 0;
    //printf("head =%d\r\n",head);

    if(!isPacketHead(&head)){
        goto read_head;
    }

    packet->packet_head = head;
again:
    ret = client_tcp_recv(ppacket,sizeof(T_Packet)-sizeof(head));

    if(ret < 0)
    {
        return -1;
    }
    
    if(ret != sizeof(T_Packet) - sizeof(head))
        return 0;

    //debug_print_packet( packet);
    if( _checkSum(packet) != 1 ){
        if(retry){
            retry--;
            goto again;
        }else
            return 0;
    }

    return 1;
}

//读取包
/************************************
**************************************
*输入参数: head  设置为空
**************************************
**************************************/
static PT_Service_List receive_service_list(int *err)
{
    PT_Service_List head=NULL,list =NULL;
    static T_Service_List packet;
    int ret;
    unsigned char left_packet_num;
    
    packet.next = NULL;
    ret = _receive_Onepacket(&packet.packet);
    if(ret <= 0){
        head = NULL;
        goto exit;
    }

copy:
    head = (PT_Service_List)malloc(sizeof(T_Service_List));
    if(head)
        memcpy(head,&packet,sizeof(T_Service_List));
    else
        goto exit;
    head->next = NULL;
    //printf("receive_service_list head =%d\r\n",head);
    if(head->packet.left_packet_num <=0)
        goto exit;
//继续进行接收
again:
    list = (PT_Service_List)malloc(sizeof(T_Service_List));
    if(list == NULL){
        _free_all_service_list(head);
        return NULL;
    }
    ret = _receive_Onepacket(&list->packet);
//网络出错  数据出错直接退出。
    if(ret <= 0){
        free(list);
        _free_all_service_list(head);
        head = NULL;
        goto exit;
    }
    else{
        left_packet_num = list->packet.left_packet_num;
        
        //printf("packet data left_packet_num =%d\r\n",left_packet_num);
        //读到了一个包
        if( _addOneService(&head,list) !=1 ){
            printf("add failed\r\n");
        }
        list = NULL;

        if(left_packet_num > 0)
            goto again;
    }
exit:
    *err = ret;
    return head;
}

//成功返回1 失败返回0   网络错误返回 -1
int send_data(T_Packet_Type type,unsigned char *data,int len)
{
    int ret;
    PT_Service_List head = _prepare_send_packet(type,data,len);
    
    ret = _send_service_list(head);
    packet_debug("send_data ret =%d\r\n",ret);
    _free_all_service_list(head);
    
    return ret;
}

int receive_data(T_Packet_Type *type,unsigned char *data,int *len)
{
    int err;
    if(type == NULL || data ==NULL || len == NULL)
        return 0;
    *len = 0;
    int size=0;
    *type = -1;

    PT_Service_List head = receive_service_list(&err);

    if(err < 0)
        return err;
    
    if(head == NULL)
        return 0;

    //printf("head = %d\r\n",head);
    //进行数据的拼接

    *type = head->packet.packet_type;

    while(head)
    {    
        memcpy( data+size ,head->packet.data , head->packet.packet_data_len);
        size += (unsigned char)head->packet.packet_data_len;
        //printf("left_num =%d,len=%d packet_len =%d\r\n",head->packet.left_packet_num,size,head->packet.packet_data_len);
        head = head->next;
    }

    _free_all_service_list(head);

    *len = size;
    return *len;
}

详细参考代码:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值