linux c语言高级编程-网络编程进阶

本篇主要介绍自定义网络协议

以文件传输为例:

协议:自已收发数据时,对数据的说明。双方定制的。
一般有两种模式:图表、.h文件

文件
   编写说明
      id 每次发送有编号,代表哪次发的数据
      cmd 代表对方要做什么
      len 数据总长度  例如200M*1024*1024
      size 本次发送的长度
      err  错误号
      note 附助说明
      cknum 校验码
   要发送的数据
      文件内容(数据)
      
1)图表
2)结构体
   .h
   数据头
   #define REQ_FILE_SEND 0x01  //请求
   #define REV_FILE_SEND 0x02  //应答       
   struct Head{
      int id;
      int cmd;
      int len;
      int size;
      int err;
      char note[256];
      int chnum;
   };
   数据体
   struct Data{
      char buf[1024];
   };


直接上代码

//协义头 protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H
//定义错误号
#define ERR_CHECKNUM        00000001  //校验错误
#define ERR_GETFILE_NOTHING 00000002  //没有要获取的文件

//定义宏指令
#define REQ_CMD_GETFILELS 000000001  //获取文件列表请求
#define REV_CMD_GETFILELS 000010001  //获取文件列表应答
#define REQ_CMD_GETFILE   000000002  //获取文件
#define REV_CMD_GETFILE   000010002  //获取文件

//定义协议头
typedef struct HEAD{
   int id;  //代表本次发送数据的编号
   int cmd; //指令 
   int len; //总长 
   int size;//本次长度
   int err; //错误号 0为正确,非零为错误
   int cknum;//校验码
} head_t;

//定义发送的数据
//1. 获取文件列表请求  
struct REQFILELS{
   char dirname[256]; //请求列表时,可指定目录名
};
//2. 获取列表的应答
//发送的数据为 char数组类型,长度在head中指定。文件名与文件名之间用 "|"分隔

//3.获取文件请求
struct REQFILE{
   char filename[256]; //请求时,必须指定要下载的包含目录的文件名
};
//4.获取文件应答
//发送文件数据,本次发送的长度,记录在head中,当累计长度>=总长,代表发送完毕
struct REVFILE{
   char filename[256];
   char data[2048];
};

#endif
       
          
//read.c 客户端
#include 
#include 
#include 
#include 
#include 
#include "libsock/libcli.h"
#include "protocol.h"

char sendbuf[4096];
//校验码
unsigned short cal_chksum(char *addr,int len){
    int nleft=len;
    int sum=0;
    unsigned short *w=(unsigned short*)addr;
    unsigned short answer=0;
    //把ICMP报头二进制数据以2字节为单位累加起来
    while(nleft>1){
       sum+=*w++;
       nleft-=2;
    }
    //若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
    if( nleft==1){
       *(unsigned char *)(&answer)=*(unsigned char *)w;
       sum+=answer;
    }
    sum=(sum>>16)+(sum&0xffff);
    sum+=(sum>>16);
    answer=~sum;
    return answer;
}
//回调,用来接收数据
int recvdata(char *buf,int size){
    struct HEAD *head=(struct HEAD*)buf;
    //先校验数据
    /*
    int cknum=head->cknum;
    head->cknum=0;
    if (cal_chksum(buf,sizeof(struct HEAD)+head->size)!=cknum){
        //接收数据有误,装载协议头
        head->cmd=head->cmd | 00010000;
        head->err=ERR_CHECKNUM;
        head->cknum=cal_chksum(buf,sizeof(struct HEAD));
        clidev.send(buf,sizeof(struct HEAD));
        return -1;
    }
    */
    printf("----------------------------- recv \n");
    //解析协议
    switch(head->cmd){
    case REV_CMD_GETFILELS: //客户端的应答 要文件列表
        {
          char *data=buf+sizeof(struct HEAD);
          char *result=strtok(data,"|");
          while(result != NULL ){
              printf("%s\n",result);
              result=strtok(NULL,"|");
          }
        }
        break;
    case REV_CMD_GETFILE: //客户端的应答 要文件
        {
           static int allsize=0;
           static fd=0;
           char *filename=buf+sizeof(struct HEAD);
           //打开文件
           if (allsize==0){
              printf("--------- file 1\n");
              fd=open(filename,O_CREAT | O_RDWR);
           }
           if (fd==-1) return -1;
           printf("--------- file 2\n");
           //保存数据
           write(fd,buf+sizeof(struct HEAD)+256,head->size-256);
           allsize+=head->size-256;
           //关闭文件
           if(allsize>=head->len){
              printf("--------- file 3\n");
              close(fd);
              fd=0;
              allsize=0;
           }
        }
        break;
    }
}

int main(int argc,char **argv){
    int i=0;
    //启动服务
    int err=clidev.start(argv[1],atoi(argv[2]),recvdata);
    if (err==-1) return -1;

    char buf[256];
    while(1){
        scanf("%s",buf);
        if (strcmp(buf,"l")==0) {
           printf("input list dir:\n");
           scanf("%s",buf);
           //装载协议头 
           struct HEAD *head=(struct HEAD*)sendbuf;
           head->id=i++;
           head->cmd=REQ_CMD_GETFILELS;
           head->len=256;
           head->size=256;
           head->err=0;
           head->cknum=0;
           //装载数据
           char *data=sendbuf+sizeof(struct HEAD);
           strcpy(data,buf);//指定要获取当前目录文件列表
           printf("----dir =%s\n",data);
           //校验码
           head->cknum=cal_chksum(sendbuf,sizeof(struct HEAD)+head->size);
           //发送
           clidev.send(sendbuf,sizeof(struct HEAD)+head->size);
        }
        else if (strcmp(buf,"f")==0){
           printf("input filename:\n");
           scanf("%s",buf);
           //装载协议头 
           struct HEAD *head=(struct HEAD*)sendbuf;
           head->id=i++;
           head->cmd=REQ_CMD_GETFILE;
           head->len=256;
           head->size=256;
           head->err=0;
           head->cknum=0;
           //装载数据
           char *data=sendbuf+sizeof(struct HEAD);
           strcpy(data,buf);//指定要获取当前目录文件
           //校验码
           head->cknum=cal_chksum(sendbuf,sizeof(struct HEAD)+head->size);
           //发送
           clidev.send(sendbuf,sizeof(struct HEAD)+head->size);
        }
        else if (strcmp(buf,"q")==0) break; //关闭窗口
    }
}


//write.c 服务端
#include 
#include 
#include 
#include "libsock/libsev.h"
#include "protocol.h"
unsigned short cal_chksum(char *addr,int len){
    int nleft=len;
    int sum=0;
    unsigned short *w=(unsigned short*)addr;
    unsigned short answer=0;
    //把ICMP报头二进制数据以2字节为单位累加起来
    while(nleft>1){
       sum+=*w++;
       nleft-=2;
    }
    //若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
    if( nleft==1){
       *(unsigned char *)(&answer)=*(unsigned char *)w;
       sum+=answer;
    }
    sum=(sum>>16)+(sum&0xffff);
    sum+=(sum>>16);
    answer=~sum;
    return answer;
}
char *getfilels(char *path){
   static char ls[4096];
   struct stat st;
   char pathname[256];
   DIR* fp=opendir(path);
   if (fp==NULL) return NULL;
   struct dirent *db;
   int i=0;
   while((db=readdir(fp))!=NULL){
      if (db->d_name[0]!='.') { //打印文件名或字符串
           sprintf(pathname,"%s%s",path,db->d_name);
           stat(pathname,&st); //获取文件属性
           if (S_ISREG(st.st_mode)){
              if (i==0) strcpy(ls,pathname);
              else sprintf(ls,"%s|%s",ls,pathname);
              i++;
           }
        }
   }
   closedir(fp);
   if (i==0) return NULL;
   else return ls;
}
//回调,用来接收数据
int recvdata(int sockfd,char *buf,int size){
    struct HEAD *head=(struct HEAD*)buf;
    printf("-------------1\n");
    //先校验数据
    /*
    int cknum=head->cknum;
    head->cknum=0;
    if (cal_chksum(buf,sizeof(struct HEAD)+head->size)!=cknum){
        //接收数据有误
        //装载协议头
        head->cmd=head->cmd | 00010000;
        head->err=ERR_CHECKNUM; 
        head->cknum=cal_chksum(buf,sizeof(struct HEAD));
        sevdev.send(fd,buf,sizeof(struct HEAD));  
        printf("-------------2\n");
        return -1;
    }
    */
    //解析数据
    switch(head->cmd){
    case REQ_CMD_GETFILELS: //客户端的请求 要文件列表
      {
         char *dir=buf+sizeof(struct HEAD);
         printf("-------------3 %s\n",dir);
         //装载协议头
         head->cmd=REV_CMD_GETFILELS;
         head->err=0;
         char *filels=buf+sizeof(struct HEAD);
         strcpy(filels,getfilels(dir));
         head->len=strlen(filels);
         head->size=strlen(filels);
         head->cknum=cal_chksum(buf,sizeof(struct HEAD)+head->size);
         //发送数据
         sevdev.send(sockfd,buf,sizeof(struct HEAD)+head->size);
      }
      break;
    case REQ_CMD_GETFILE: //客户端的请求 要文件
      {
         char *filename=buf+sizeof(struct HEAD);
         printf("---------- get file 1 %s\n",filename);
         int fd1=open(filename,O_RDONLY);
         if (fd1==-1){
            //发送失败应答
         }
         else{
            printf("---------- get file 2 \n");
            int allsize=lseek(fd1,0,2);
            lseek(fd1,0,0);
            head->cmd=REV_CMD_GETFILE;
            head->err=0;
            head->len=allsize;
            while(allsize>0){
                size=allsize%2048;
                if (size==0) size=2048;
                size=read(fd1,buf+sizeof(struct HEAD)+256,size);
                head->size=size+256;
                head->cknum=cal_chksum(buf,sizeof(struct HEAD)+size+256);
                allsize-=size;
                printf("---------- get file 3 %d \n",size);
                //发送数据
                sevdev.send(sockfd,buf,sizeof(struct HEAD)+256+size);
            }
            printf("---------- get file 4 \n");
            close(fd1);
         }
      }
      break;
    }

}

int main(int argc,char **argv){
    //启动服务
    int err=sevdev.start(atoi(argv[1]),recvdata);
    if (err==-1) return -1;

    char buf[256];
    while(1){
        scanf("%s",buf);
        if (strcmp(buf,"q")==0) break; //关闭窗口
    }
    //停止服务
    sevdev.stop();
}

你可能感兴趣的:(C语言高级编程,linux,c语言高级编程,网络编程进阶)