• 正文
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

C語言初學者編程水平上不來?不妨嘗試這10個C語言例子

03/03 15:17
1646
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

初學者通過下面幾個c語言,大家可以提高自己的編程水平,

1. 打印任意一段內(nèi)存的數(shù)據(jù)

void?print_array(char?*title,?unsigned?char?*data,?int?len)?{
????printf("%s:n",?title);
????for?(int?i?=?0;?i?<?len;?i++)?{
????????//?每行開頭打印當前字節(jié)的地址
????????if?(i?%?16?==?0)?{
????????????printf("0x%08X:?",?(unsigned?int)(data?+?i));
????????}

????????//?打印當前字節(jié)的十六進制形式
????????printf("%02X?",?data[i]);

????????//?每行打印16個字節(jié)后換行,并打印ASCII字符
????????if?((i?+?1)?%?16?==?0?||?i?==?len?-?1)?{
????????????//?對齊填充
????????????for?(int?j?=?0;?j?<?16?-?(i?%?16)?-?1;?j++)?{
????????????????printf("???");
????????????}
????????????printf("?|?");
????????????//?打印ASCII字符
????????????for?(int?j?=?i?-?(i?%?16);?j?<=?i;?j++)?{
????????????????if?(data[j]?>=?32?&&?data[j]?<=?126)?{
????????????????????printf("%c",?data[j]);
????????????????}?else?{
????????????????????printf(".");
????????????????}
????????????}
????????????printf("n");
????????}
????}
????printf("----------------------------------------n");
}

2. 實現(xiàn)下面信令的封裝和解析,只寫出結(jié)構(gòu)即可

名稱 字段個數(shù) 說明
起始字節(jié) 1 只能為0x7e
命令 1
參數(shù) 2 高字節(jié)在低位,低字節(jié)在高位
長度 2 包括子命令和數(shù)據(jù)區(qū)長度,高字節(jié)在低位,低字節(jié)在高位
子命令 1
數(shù)據(jù)區(qū) N 可變長度,最大長度為20
校驗字 1 從命令開始累計到數(shù)據(jù)區(qū)
結(jié)束符 1 只能為0x7e

數(shù)據(jù)幀封裝


int?msg_send(u8?cmd,?u16?param,?u8?subcmd,?u8?*data,?u8?len)
{
?int?pos?=?0;
?u8?crc?=?0;
?int?i,j;
?int?status?=?0;
?int?ret?=?0;
?u8?rawdata[MAX_FRM_LEN]?=?{0};

?//填充7e
?pos?=?0;
?rawdata[pos]?=?0x7e;
?pos+=1;
?rawdata[pos]?=?cmd;
?pos+=1;
?setdata16(&rawdata[pos],cmd);
?pos+=2;
?setdata16(&rawdata[pos],len+1);
?pos+=2;
?rawdata[pos]?=?subcmd;?
?pos+=1;

?for(i=0;i<len;i++)
?{
??rawdata[pos?+i]?=?data[i];
?}
?pos+=len;

?//crc?sum?first
?for(i=1;i<pos;i++)
?{
??crc?+=?rawdata[i];
?}


?rawdata[pos]?=?crc;
?pos+=1;
?rawdata[pos]?=?0x7e;
?pos+=1;

?print_array("[frm]",rawdata,pos);

?return?pos;
}

int?main()
{
?int?len;
?int?ret;
?u16?param?=?0x9527;
?u8?data[FRM_DATA_MAX_LEN]={0x1,0x2,0x3,0x4};
?len?=?msg_send(MSG_TYPE_QUERY,?param,?1,?data,?4);??
?return?1;
}

數(shù)據(jù)幀解析

用上一個程序執(zhí)行結(jié)果,作為解析函數(shù)的測試數(shù)據(jù)

?u8?buf[]={0x7E,0x01?,0x00?,0x01?,0x00?,0x05?,0x01?,0x01?,0x02?,0x03?,0x04?,0x12?,0x7E?};
int?my_check_crc(char?*data)
{
?int?i;
?u8?crc=0;
?u16?datalen=0;

?getdata16(&datalen,&data[4]);
?for(i=1;i<1+6+datalen-1;i++)
?{
??crc+=data[i];
?}
?printf("crc=%x??%xn",crc&0xff,data[1+6+datalen-1]);//datalen包括子命令1個字節(jié)
?return?(crc&0xff)==(data[1+6+datalen-1]&0xff);
}

int?frm_parse(PENG_FRM_MSG_S?*pmsg,u8?data[],int?len)
{
?int?ret?=?-1;
?int?pos?=?0;
?
?if(len<9)
?{
??printf("invalid?frm?len?%dn",len);
??return?-1;
?}
?//check?crc
?ret?=?my_check_crc(data);
?if(ret?!=?1)
?{
??printf("crc?check?errorn");
??return?-1;
?}
?pos?=?0;
?pmsg->startCode?=?data[pos];
?pos++;
?pmsg->cmd?=?data[pos];
?pos++;
?getdata16(&pmsg->param,&data[pos]);
?pos?+=?2;
?getdata16(&pmsg->len,&data[pos]);
?pos?+=?2;
?pmsg->subcmd?=?data[pos];
?pos++;

?if(pmsg->len+8?!=?len)
?{
??printf("err?invalid?frm?len=%dn",pmsg->len);?
??return?-1;
?}
?memcpy(pmsg->data,&data[pos],pmsg->len-1);
?return?1;
}

int?main()
{
?int?len;
?int?ret;
?PENG_FRM_MSG_S?msg;?
?PENG_FRM_MSG_S?*pmsg?=?&msg;?
?u8?buf[]={0x7E,0x01?,0x00?,0x01?,0x00?,0x05?,0x01?,0x01?,0x02?,0x03?,0x04?,0x12?,0x7E?};

?ret?=?frm_parse(pmsg,buf,sizeof(buf));
?printf("cmd:%x?param:%x,len:%x,subcmd:%xn",
??pmsg->cmd,pmsg->param,pmsg->len,pmsg->subcmd);
?
?print_array("[data]<<<",pmsg->data,pmsg->len-1);
???
?return?1;
}

思考:如果起始字符和結(jié)束符之間的數(shù)據(jù)如果有0x7e/0x7d需要轉(zhuǎn)義為0x7d5e/0x7d5d,應該如何處理?

參考下面一篇文章
《7E頭解析的那些事兒(幀格式分析實例)》

3. ?編寫基于udp C/S架構(gòu)的服務器客戶端程序

    udp_server.c
#include?<stdlib.h>
#include?<stdio.h>
#include?<errno.h>
#include?<string.h>
#include?<unistd.h>
#include?<netdb.h>
#include?<sys/socket.h>
#include?<netinet/in.h>
#include?<sys/types.h>
#include?<arpa/inet.h>

#define?SERVER_PORT?8888?
#define?MAX_MSG_SIZE?1024?

void?udps_respon(int?sockfd)?
{?
?struct?sockaddr_in?addr;?
?int?addrlen,n;?
?char?msg[MAX_MSG_SIZE];?

?while(1)?
?{?/*?從網(wǎng)絡上讀,并寫到網(wǎng)絡上?*/?
??bzero(msg,sizeof(msg));?//?初始化,清零
??addrlen?=?sizeof(struct?sockaddr_in);?
??
??//addr存放客戶端的port?ip信息
??n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct?sockaddr*)&addr,&addrlen);?//?從客戶端接收消息
??
??msg[n]=0;?
??/*?顯示服務端已經(jīng)收到了信息?*/?
??fprintf(stdout,"Server?have?received?%s??%sn",msg,inet_ntoa(addr.sin_addr));?//?顯示消息
??
??
??sendto(sockfd,msg,strlen(msg),0,(struct?sockaddr*)&addr,addrlen);?
?}?
}?

int?main(void)?
{?
?int?sockfd;?
?struct?sockaddr_in?addr;?

?/*?服務器端開始建立socket描述符?*/?
?sockfd=socket(AF_INET,SOCK_DGRAM,0);?
?if(sockfd<0)?
?{?
??fprintf(stderr,"Socket?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?/*?服務器端填充?sockaddr結(jié)構(gòu)?*/?
?bzero(&addr,sizeof(struct?sockaddr_in));?
?addr.sin_family=AF_INET;?
?addr.sin_addr.s_addr=htonl(INADDR_ANY);?
?addr.sin_port=htons(SERVER_PORT);?

?/*?捆綁sockfd描述符?*/?
?if(bind(sockfd,(struct?sockaddr?*)&addr,sizeof(struct?sockaddr_in))<0)?
?{?
??fprintf(stderr,"Bind?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?udps_respon(sockfd);?//?進行讀寫操作
?close(sockfd);?
}?
    udp_client.c
#include?<stdlib.h>
#include?<stdio.h>
#include?<errno.h>
#include?<string.h>
#include?<unistd.h>
#include?<netdb.h>
#include?<sys/socket.h>
#include?<netinet/in.h>
#include?<sys/types.h>
#include?<arpa/inet.h>

#define?SERVER_PORT?8888?
#define?MAX_BUF_SIZE?1024?

void?udpc_requ(int?sockfd,const?struct?sockaddr_in?*addr,int?len)?
{?
?char?buffer[MAX_BUF_SIZE];?
?int?addrlen,n;?
?struct?sockaddr_in?addr_server;?
?
?while(1)?
?{??/*?從鍵盤讀入,寫到服務端?*/?
??printf("Please?input?char:n");
??fgets(buffer,MAX_BUF_SIZE,stdin);?
??
??sendto(sockfd,buffer,strlen(buffer),0,addr,len);?
??bzero(buffer,MAX_BUF_SIZE);?
??
??addrlen?=?sizeof(struct?sockaddr);?
??n=recvfrom(sockfd,buffer,MAX_BUF_SIZE,0,(struct?sockaddr*)&addr_server,&addrlen);
??buffer[n]=0;
??printf("recv:%sn",buffer);
?}?
}?

int?main(int?argc,char?**argv)?
{?
?int?sockfd;?
?struct?sockaddr_in?addr;?

?if(argc!=2)?
?{?
??fprintf(stderr,"Usage:%s?server_ipn",argv[0]);?
??exit(1);?
?}

?/*?建立?sockfd描述符?*/?
?sockfd=socket(AF_INET,SOCK_DGRAM,0);?
?if(sockfd<0)?
?{?
??fprintf(stderr,"Socket?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?/*?填充服務端的資料?*/?
?bzero(&addr,sizeof(struct?sockaddr_in));?
?addr.sin_family=AF_INET;?
?addr.sin_port=htons(SERVER_PORT);
?if(inet_aton(argv[1],&addr.sin_addr)<0)??/*inet_aton函數(shù)用于把字符串型的IP地址轉(zhuǎn)化成網(wǎng)絡2進制數(shù)字*/?
?{?
??fprintf(stderr,"Ip?error:%sn",strerror(errno));?
??exit(1);?
?}?

?udpc_requ(sockfd,&addr,sizeof(struct?sockaddr_in));?//?進行讀寫操作
?close(sockfd);?
}?

編譯:

gcc?udp_server.c?-o?s
gcc?udp_client.c?-o?c

先運行服務器

./s

再打開一個終端,運行客戶端

./c

客戶端輸入任意字符串會直接返回給服務器。

4. 基于步驟2/3通過udp實現(xiàn)信令在client/server之間傳輸,只寫出結(jié)構(gòu)即可

clien.c

#define?SERVER_PORT?8888?
#define?MAX_BUF_SIZE?1024?
typedef?struct?sockaddr?SA;

void?sendfrm_thread(int?sockfd,const?struct?sockaddr_in?*addr,int?len)?
{?
?u8?data[FRM_DATA_MAX_LEN]={0x1,0x2,0x3,0x4};
?int?addrlen,frmlen;?
?struct?sockaddr_in?addr_server;?
?u16?param?=?0x9527;
?u8?rawdata[MAX_FRM_LEN]?=?{0};
?
?while(1)?
?{?????
??frmlen?=?msg_send(rawdata,MSG_TYPE_QUERY,?param,?1,?data,?4);
??
??sendto(sockfd,rawdata,frmlen,0,(SA?*)addr,len);?
??
??sleep(2);
?}?
}?
int?main(int?argc,char?**argv)?
{?
?int?sockfd;?
?struct?sockaddr_in?addr;?

?if(argc!=2)?
?{?
??fprintf(stderr,"Usage:%s?server_ipn",argv[0]);?
??exit(1);?
?}

?sockfd=socket(AF_INET,SOCK_DGRAM,0);?
?if(sockfd<0)?
?{?
??fprintf(stderr,"Socket?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?bzero(&addr,sizeof(struct?sockaddr_in));?
?addr.sin_family=AF_INET;?
?addr.sin_port=htons(SERVER_PORT);
?if(inet_aton(argv[1],&addr.sin_addr)<0)??
?{?
??fprintf(stderr,"Ip?error:%sn",strerror(errno));?
??exit(1);?
?}?

?sendfrm_thread(sockfd,&addr,sizeof(struct?sockaddr_in));?
?close(sockfd);?
}?

server.c

void?rcvfrm_thread(int?sockfd)?
{?
?int?ret;
?struct?sockaddr_in?addr;?
?int?addrlen,len;?
?PENG_FRM_MSG_S?msg;?
?PENG_FRM_MSG_S?*pmsg?=?&msg;?

?char?buf[MAX_MSG_SIZE];?

?addrlen?=?sizeof(struct?sockaddr_in);?

?while(1)?{
??bzero(buf,sizeof(buf));?
??
??len=recvfrom(sockfd,buf,MAX_MSG_SIZE,0,(struct?sockaddr*)&addr,&addrlen);

??buf[len]=0;?
??ret?=?frm_parse(pmsg,buf,len);
??
??printf("n[msg]<<<?cmd:%x?param:%x,len:%x,subcmd:%xn",
???pmsg->cmd,pmsg->param,pmsg->len,pmsg->subcmd);
??
??//print_array("[data]<<<",pmsg->data,pmsg->len-1);

??
?}?
}?

int?main(void)?
{?
?int?sockfd;?
?struct?sockaddr_in?addr;?

?sockfd=socket(AF_INET,SOCK_DGRAM,0);?
?if(sockfd<0)?
?{?
??fprintf(stderr,"Socket?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?bzero(&addr,sizeof(struct?sockaddr_in));?
?addr.sin_family=AF_INET;?
?addr.sin_addr.s_addr=htonl(INADDR_ANY);?
?addr.sin_port=htons(SERVER_PORT);?

?if(bind(sockfd,(struct?sockaddr?*)&addr,sizeof(struct?sockaddr_in))<0)?
?{?
??fprintf(stderr,"Bind?Error:%sn",strerror(errno));?
??exit(1);?
?}?

?rcvfrm_thread(sockfd);
?close(sockfd);?
}?

5. 使用數(shù)據(jù)庫sqlite,將步驟4的信令insert到數(shù)據(jù)庫中,

qslite操作可以參考下面文章:

嵌入式數(shù)據(jù)庫sqlite3【基礎篇】-基本命令操作,小白一看就懂》

6. 用c語言執(zhí)行步驟5的數(shù)據(jù)庫操作指令,并將代碼合并到步驟4的架構(gòu)中

C語言操作數(shù)據(jù)庫,可以參考:《如何用C語言操作sqlite3,一文搞懂》

7. 用任意一款抓包工具抓取一個ip數(shù)據(jù)包,保存到數(shù)組frm[]

《網(wǎng)絡/命令行抓包工具tcpdump詳解》

《一文包你學會網(wǎng)絡數(shù)據(jù)抓包》

拷貝出數(shù)據(jù):

80?8F?1D?C7?A6?07?D8?BB?C1?C8?51?C3?08?00?45?00?00?34?FC?6E?40?00?80?06?00?00?C0?A8?00?6B?8E?FA?C4?CA?21?AD?01?BB?25?C1?79?D5?00?00?00?00?80?02?FA?F0?14?FF?00?00?02?04?05?B4?01?03?03?08?01?01?04?02

轉(zhuǎn)換成數(shù)組:

unsigned?char?frm[]={
0x80,0x8F,0x1D,0xC7,0xA6,0x07,0xD8,0xBB,0xC1,0xC8,0x51,0xC3,0x08,0x00,0x45,0x00,0x00,0x34,0xFC,0x6E,0x40,
0x00,0x80,0x06,0x00,0x00,0xC0,0xA8,0x00,0x6B,0x8E,0xFA,0xC4,0xCA,0x21,0xAD,0x01,0xBB,0x25,0xC1,0x79,0xD5,
0x00,0x00,0x00,0x00,0x80,0x02,0xFA,0xF0,0x14,0xFF,0x00,0x00,0x02,0x04,0x05,0xB4,0x01,0x03,0x03,0x08,0x01,
0x01,0x04,0x02
};

8. 提取出ip數(shù)據(jù)包的mac頭,ip數(shù)據(jù)包的ip頭

注意:字節(jié)序問題

mact頭提取



#define?MAC_FMG?"%02x:%02x:%02x:%02x:%02x:%02xn"
#define?printfmac(x)?printf(MAC_FMG,x[0],x[1],x[2],x[3],x[4],x[5])

struct?mac_h{
?u8?dst[6];
?u8?src[6];
?u16?pro;
};
int?parse_mac_h(u8?*data,struct?mac_h?*pmac_h,int?len)
{
?int?pos?=?0;
?
?if(len<6+6+2)
?{
??return?-1;
?}
?pos?=?0;
?memcpy(pmac_h->dst,&data[pos],6);
?pos+=6;
?memcpy(pmac_h->src,&data[pos],6);
?pos+=6;
?pmac_h->pro?=?data[pos]<<8?|?data[pos+1];
?pos+=2;
}


int?main()
{
?struct?mac_h?mac_header;
?......
?parse_mac_h(frm,&mac_header,sizeof(frm));
?......
?return?1;
}

執(zhí)行結(jié)果如下:

ip頭

#define?IPADDR_FMG?"%d.%d.%d.%dn"
#define?printipaddr(x)?printf(IPADDR_FMG,((u8*)&(x))[0],((u8*)&(x))[1],((u8*)&(x))[2],((u8*)&(x))[3])

void?dump_iphdr(struct?iphdr?*iph)
{
?printf("ihl:%dnversion:%dntos:%dntot_len:%xnid:%xnfrag_off:%xnttl:%dnprotocol:%dncheck:%xn",
??iph->ihl,
??iph->version,
??iph->tos,
??iph->tot_len,
??iph->id,
??iph->frag_off,
??iph->ttl,
??iph->protocol,
??iph->check??
?);
?printipaddr(iph->saddr);??
?printipaddr(iph->daddr);?
}


int?parse_iphdr(u8?*data,struct?iphdr?*iph,int?len)
{
?int?pos?=?0;
?
?iph->ihl?=?data[pos]&0xf;
?iph->version?=?data[pos]>>4;
?pos+=1;
?iph->tos?=?data[pos];
?pos+=1;
?getdata16(&iph->tot_len,&data[pos]);
?pos+=2;
?getdata16(&iph->id,&data[pos]);
?pos+=2;
?getdata16(&iph->frag_off,&data[pos]);
?pos+=2;

?iph->ttl?=?data[pos];
?pos+=1;
?iph->protocol?=?data[pos];
?pos+=1;
?getdata16(&iph->check,&data[pos]);
?pos+=2;

?getdata32(&iph->saddr,&data[pos]);
?pos+=4;
?getdata32(&iph->daddr,&data[pos]);
?pos+=4;?
}

在這里插入圖片描述

思考:為什么不可以直接用下面代碼提取數(shù)據(jù)信息:

?memcpy(&mac_header,&frm[0],sizeof(struct?mac_h));
?memcpy(&ip_header,&frm[14],sizeof(struct?iphdr));

9. 將步驟8的結(jié)構(gòu)體變量賦一些初始值,并按照mac頭IP頭格式將結(jié)構(gòu)體內(nèi)容填充到一段buf中

void?send_pkt(struct?mac_h?*mach,struct?iphdr?*iph,char?*pdata,int?datalen)
{
?int?pos?=?0;
?char?buf[14+20+1500]={0};

?/*add?mac頭*/
?pos?=0?;
?memcpy(&buf[pos],mach->dst,6);
?pos+=6;
?memcpy(&buf[pos],mach->src,6);
?pos+=6;
?setdata16(&buf[pos],mach->pro);
?pos+=2;
?
?/*add?ip頭*/
?buf[pos]?=?(char)(iph->ihl?|?iph->version<<4);
?pos+=1;
?buf[pos]?=?iph->tos;
?pos+=1;
?setdata16(&buf[pos],iph->tot_len);
?pos+=2;
?setdata16(&buf[pos],iph->id);
?pos+=2;
?setdata16(&buf[pos],iph->frag_off);
?pos+=2;

?buf[pos]?=?iph->ttl;
?pos+=1;
?buf[pos]?=?iph->protocol;
?pos+=1;
?setdata16(&buf[pos],iph->check);
?pos+=2;

?setdata32(&buf[pos],iph->saddr);
?pos+=4;
?setdata32(&buf[pos],iph->daddr);
?pos+=4;?

?memcpy(&buf[pos],pdata,datalen);
?pos+=datalen;?
?print_array("nfrm",buf,pos);
}


main()
{
...
?datalen?=?ip_header.tot_len;
?memcpy(data,frm?+?sizeof(struct?mac_h)+sizeof(struct?iphdr),datalen-20);
?send_pkt(&mac_header,&ip_header,data,datalen-20);
...
}

10. 將3步驟的結(jié)構(gòu)體變量保存到文件,并讀取出來,

?void?save_head(struct?mac_h?*mach,struct?iphdr?*iph,char?*pdata,int?datalen)
{
?int?fd??=-1;
?int?len?=?0;
?struct?mac_h?mac_head_test;
?struct?iphdr?ip_head_test;
?char?buf[1500];



?fd??=open(FAILE_NAME,O_RDWR|O_CREAT);
?if(fd?<?0)
?{
??perror("open?failn");
??return;
?}
?write(fd,mach,sizeof(struct?mac_h));
?write(fd,iph,sizeof(struct?iphdr));
?write(fd,pdata,datalen);

?lseek(fd,0,SEEK_SET);

?len?=?read(fd,&mac_head_test,sizeof(struct?mac_h));
?len?=?read(fd,&ip_head_test,sizeof(struct?iphdr));
?len?=?read(fd,buf,1500);

?buf[len]?=?'';

?printf("------recover?from?file--------------n");
?dump_machdr(&mac_head_test);
?dump_iphdr(&ip_head_test);?

?print_array("ndata",buf,len);
}



save_head(&mac_header,&ip_header,data,datalen-20);

完整代碼獲取,公眾號后臺回復:?frm

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗和培訓經(jīng)驗。曾任職ZTE,某研究所,華清遠見教學總監(jiān)。擁有多篇網(wǎng)絡協(xié)議相關(guān)專利和軟件著作。精通計算機網(wǎng)絡、Linux系統(tǒng)編程、ARM、Linux驅(qū)動、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實際項目出發(fā),保持原理+實踐風格,適合Linux驅(qū)動新手入門和技術(shù)進階。