很多人都碰到这个问题,需要在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;
}
详细参考代码: