Linux开发_文件发送与接收

110 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

介绍Linux下网络编程实例,文件传输实例、多线程处理服务器请求、curl命令使用实例、通过curl命令完成HTTP接口调用。

任务1:网络编程

1.1 练习题说明

【1】实现TCP服务器与TCP客户端之间的基本通信,收发数据 (按照上课的思路流程看函数文档)

【2】实现TCP服务器与TCP客户端之间的文件传输。(单个文件传输)

验证方式: (1) 同一台电脑演示 (2)同桌之间演示

考虑的问题:

(1) 网络的传输环境,考虑应答问题

(2) 数据丢包之后如何处理? 可以重发

(3) 超时处理

(4) 服务器与客户端之间连接断开处理。(客户端和服务器两边都需要重新连接)

文件传输可以在广告机中使用。

(5) 每个数据的单位: 分包发送

(扩展要求): 显示接收进度百分比,显示接收的文件名称,推荐: 定义结构体(使用数据结构)

【3】[] (扩展)实现TCP服务器与TCP客户端之间的目录传输(一级目录)。

【4】[4] (扩展)实现网络聊天室(模仿QQ群发送消息的效果)

(1) 加入进去的成员都是客户端

(2) 服务器负责给各个客户端转发消

一般情况下,推荐最大每次传输的字节数不超过1024字节。

(1)文件发送客户端

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <sys/select.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 ​
 /*
 TCP客户端:
 1. sockt创建套接字
 2. 连接服务器
 */
 int socket_cfd; /*保存客户端套接字*/
 int main(int argc,char **argv)
 {
     if(argc!=4)
     {
         printf("./server <192.168.xx.xx 服务器的IP地址> <服务器端口号> <文件名称>\n");
         exit(-1);
     }
     
     FILE *file=fopen(argv[3],"wb");
     if(file==NULL)
     {
         printf("文件创建失败!");
         exit(0);
     }
     
     int err; //存放返回值状态
     /*1. 创建套接字*/
     socket_cfd=socket(AF_INET,SOCK_STREAM,0);
     if(socket_cfd<0)
     {
         printf("服务器端创建失败!\n");
         exit(-1);
     }
     
     /*2.连接服务器*/
     struct sockaddr_in ServerAddr;
     ServerAddr.sin_family=AF_INET; //IPV4协议
     ServerAddr.sin_port=htons(atoi(argv[2]));      //服务器的端口号 最大值65535
     ServerAddr.sin_addr.s_addr=inet_addr(argv[1]); //IP地址赋值
     
     err=connect(socket_cfd,(const struct sockaddr *)&ServerAddr,sizeof(struct sockaddr));
     if(err!=0)
     {
         printf("服务器连接失败!\n");
         exit(-1);
     }
     
     /*3. 收发数据: 接收服务器发送的数据*/
     char rx_buff[100];
     fd_set readfds; //存放读事件
     int data;
     int ack=666;
     while(1)
     {
         FD_ZERO(&readfds); //清空文件描述符
         FD_SET(socket_cfd,&readfds); //添加需要监控的文件描述符 
         data=select(socket_cfd+1,&readfds,NULL,NULL,NULL);
         if(data>0)
         {
             err=read(socket_cfd,rx_buff,100);
             if(err==0 && data==1)
             {
                 printf("服务器已经与客户端断开连接!\n");
                 break;
             }
             fwrite(rx_buff,1,err,file);//写入数据
             printf("客户端成功接收%d个字节\n",err);
             
             write(socket_cfd,&ack,4); //发送应答信号。
         }
     }
     close(socket_cfd);
     fclose(file);
     return 0;
 }

(2)文件发送客户端

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
 ​
 /*
 TCP服务器:
 1. sockt创建套接字
 2. bind绑定端口
 3. 设置监听的数量
 4. 等待客户端连接
 */
 int socket_sfd; /*保存了服务器端的套接字*/
 int socket_cfd; /*已经连接成功的客户端套接字*/
 ​
 /*
 服务器是发送端
 */
 int main(int argc,char **argv)
 {
     if(argc!=4)
     {
         printf("./server <192.168.xx.xx 本地IP地址> <服务器端口号> <TX文件名称>\n");
         exit(-1);
     }
     
     FILE *file=fopen(argv[3],"rb");
     if(file==NULL)
     {
         printf("文件打开失败!\n");
         exit(0);
     }
     
     int err; //存放返回值状态
     /*1. 创建套接字*/
     socket_sfd=socket(AF_INET,SOCK_STREAM,0);
     if(socket_sfd<0)
     {
         printf("服务器端创建失败!\n");
         exit(-1);
     }
     
     /*2. 绑定端口*/
     struct sockaddr_in ServerAddr;
     ServerAddr.sin_family=AF_INET; //IPV4协议
     ServerAddr.sin_port=htons(atoi(argv[2]));      //服务器的端口号 最大值65535
     ServerAddr.sin_addr.s_addr=inet_addr(argv[1]); //IP地址赋值
     err=bind(socket_sfd,(const struct sockaddr *)&ServerAddr,sizeof(struct sockaddr));
     if(err!=0)
     {
         printf("服务器端 端口绑定失败!\n");
         exit(-1);
     }
     
     /*3. 设置监听的数量*/
     listen(socket_sfd,10);
     
     /*4. 等待客户端连接*/
     struct sockaddr_in ClientAddr; //存放已经连接成功客户端信息
     socklen_t addrlen=sizeof(struct sockaddr);    //客户端的地址长度
     socket_cfd=accept(socket_sfd,(struct sockaddr *)&ClientAddr,&addrlen);
     if(socket_cfd<0)
     {
         printf("服务器端,连接客户端失败!\r\n");
         exit(-1);
     }
     
     /*打印一些客户端的信息*/
     printf("成功连接的客户端端口号:%d\n",ntohs(ClientAddr.sin_port));
     printf("成功连接的客户端IP地址:%s\n",inet_ntoa(ClientAddr.sin_addr));
     
     /*5. 收发数据 :服务器给客户端发送10个字节的数据*/
     char tx_buff[100];
     int len;
     int ack;
     while(1)
     {
         len=fread(tx_buff,1,100,file);
         printf("服务器成功发送:%d 字节\r\n",len);
         err=write(socket_cfd,tx_buff,len);
         while(1)
         {
             err=read(socket_cfd,&ack,4);  //等待客户端的应答
             if(err>0&&ack==666)
             {
                 break;
             }
         }   
         if(len!=100)break;
     }
     close(socket_sfd);
     fclose(file);
     return 0;
 }

任务2:线程编程

 #include <pthread.h>
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
 void *(*start_routine) (void *), void *arg);
 Compile and link with -pthread.
 -lpthread
 理解: 一个线程就是一个while(1)。
 ​
 [root@wbyq linux-share-dir]# gcc app.c
 /tmp/cccOs4TK.o: In function `main':
 app.c:(.text+0x69): undefined reference to `pthread_create'
 app.c:(.text+0x8d): undefined reference to `pthread_create'
 collect2: ld 返回 1
 [root@wbyq linux-share-dir]# gcc app.c -lpthread

练习:

【1】学习线程的基本使用

【2】实现一个服务器实现多个客户端的连接,实现通信。

思路: 一个客户端就是一个独立的线程。

【3】 扩展练习:实现服务器同时对多个客户端进行文件发送。

(1) 服务器连接上一个客户端就创建一个线程。

(2) 线程的函数需要写几个? 1个

1个函数需要考虑的问题: 函数的可重入性能!

需要考虑到资源抢占! 使用信号量!

(1) CRC16、CRC32校验、

(3)和校验 ”abcd <校验和>” -->和

(4)异或校验。 (1) 文件加密解码

(2) 不添加三个变量,将两个变量的值交换

int a=666; int b=888;

(5)MD5值校验。

客户端:

 #include <stdio.h>
 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>
 #include <arpa/inet.h>   //使用大小端转换函数. find /
 #include <string.h>
 ​
 ​
 /*服务器端口号定义*/
 #define P_host 8080
 ​
 //  ./app   192.168.18.3
 /*TCP客户端代码*/
 int main(int argc,char *argv[])
 {
       if(argc!=2)
       {
           printf("参数错误:./app <服务器IP地址>\n");
             return -1;
       }
        
        
      int clientfd;
      struct sockaddr_in server_address;     //存放服务器的IP地址信息
      memset(&server_address,0,sizeof(struct sockaddr_in)); //初始化内存空间
      server_address.sin_family=AF_INET;            //IPV4协议
      server_address.sin_port=htons(P_host);        //端口号赋值
      server_address.sin_addr.s_addr=inet_addr(argv[1]);    //本地IP地址
      
     
      /*1 .创建套接字*/
     clientfd=socket(AF_INET,SOCK_STREAM,0);
    if(clientfd<0)
     {
         printf("客户端网络套接字创建失败!\n");  
         return -1;
     }
     
     /*2. 连接服务器*/
     if(connect(clientfd,(const struct sockaddr *)&server_address,sizeof(struct sockaddr))!=0)
     {
           printf("客户端连接服务器失败!\n");  
         return -1;  
     }
     
     
     char buff[1024];
     
     while(1)
     {
         gets(buff);
         write(clientfd,buff,strlen(buff)); 
     }
         return 0;
 }

服务器:

 #include <stdio.h>
 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>
 #include <arpa/inet.h>   //使用大小端转换函数
 #include <string.h>
 #include <sys/select.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <pthread.h>
 ​
 ​
 //函数声明
 void *start_routine_1(void *dev);
 void *start_routine_2(void *dev);
 ​
 typedef void *(*start_routine) (void *);
 start_routine fun[]={start_routine_1,start_routine_2};
  
  
 pthread_t thread_id[2];                //存放线程的标识符
 int clientfd[2];                       //保存TCP客户端的网络套接字
 struct sockaddr_in client_address[2];  //存放客户端的信息
 socklen_t address_len[2];              //存放客户端结构体信息的长度
 ​
 ​
 /*服务器端口号定义*/
 #define P_host 8080
 ​
 /*TCP服务器代码*/
 int main(int argc,char *argv[])
 {
      int socketfd;
      struct sockaddr_in server_address;     //存放服务器的IP地址信息
      memset(&server_address,0,sizeof(struct sockaddr_in)); //初始化内存空间
      memset(client_address,0,sizeof(struct sockaddr_in)*2); //初始化内存空间
      server_address.sin_family=PF_INET;            //IPV4协议
      server_address.sin_port=htons(P_host);        //端口号赋值
      server_address.sin_addr.s_addr=INADDR_ANY;    //本地IP地址
     
 ​
   /*1 .创建套接字*/
    socketfd=socket(PF_INET,SOCK_STREAM,0);
    if(socketfd<0)
     {
         printf("服务器网络套接字创建失败!\n");  
         return -1;
     }
          
    /*2. 绑定端口,创建服务器*/
    if(bind(socketfd,(const struct sockaddr *)&server_address,sizeof(struct sockaddr))!=0)
     {
         printf("服务器绑定端口失败!\n"); 
         return -1;  
     }
    
    /*3. 设监听的端口数量*/
    if(listen(socketfd,10)!=0)
     {
        printf("服务器端口监听失败!\n");  
        return -1;    
     }
    
     int i;
     
     for(i=0;i<2;i++)
     {
          address_len[i]=sizeof(struct sockaddr);  //计算结构体大小 20 
       /*4. 等待客户端连接*/
         if((clientfd[i]=accept(socketfd,(struct sockaddr *)&client_address[i],&address_len[i]))<0)
         {
             printf("等待客户端连接失败!\n"); 
             break;  
         } 
         
         //创建线程
         if(pthread_create(&thread_id[i],NULL,fun[i],NULL)!=0)
         {
            printf("线程_%d_创建失败!\n",i);       
         }   
     }
     
     while(1)
     {
             
     }
         
    //阻塞方式等待线程的结束
     pthread_join(thread_id[0],NULL);
     pthread_join(thread_id[1],NULL);
     return 0;   
 }
 ​
 ​
 //线程1
 void *start_routine_1(void *dev)
 {
      while(1)
      {
           printf("TCP客户端1连接!\n");
           sleep(2);
      }
      //终止线程
      pthread_exit(NULL);
 }
 ​
 ​
 //线程2
 void *start_routine_2(void *dev)
 {
     while(1)
     {
           printf("TCP客户端2连接!\n");
           sleep(2);
     }
      //终止线程
      pthread_exit(NULL);
 }

任务3:使用的网络服务器接口

C语言:面向过程 、 面向对象

【1】车牌号识别

【2】二维码生成

【3】获取北京时间

获取接口的网址:www.k780.com/

【A】 调用网络的接口的方式: (天气预报)

 curl http://api.k780.com:88/?app=weather.future'&'weaid=南昌
 '&&'appkey=10003'&'sign=b59bc3ef6191eb9f747dd4e83c99f2a4'&'format=json >123.txt

注意: &是特殊的符号,需要使用单引号或者双引号括起来。

Curl: 命令行的浏览器。

【B】调用二维码生成的接口

 curl http://api.k780.com:88/?app=qr.get'&'data=www.wanbangee.com'&'level=L'&'size=6 >123.png

练习:

【1】天气预报查询

查询的格式: ./app <城市的中文名称>

img

【2】获取北京标准时间,显示出来

img

【3】 二维码生成创建 运行格式: ./app <生成的二维码内容>

生成之后直接显示在屏幕上。可以使用eog命令

【4】 车牌号的识别

【5】调用百度地图API接口 lbsyun.baidu.com/index.php?t…