首先来看client.c的代码
#include
#include
#include
#include
#include
#include
#include
#include
#include "send.h"
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("<%s> \n",argv[0]);
return -1;
}
/* 创建套接字--》获取待连接套接字 */
int soc_fd = socket(AF_INET, SOCK_STREAM, 0);
if(soc_fd == -1)
{
perror("socket failed!");
return -1;
}
/* 定义ipv4地址结构体变量 */
struct sockaddr_in ser_addr;
bzero(&ser_addr, sizeof(ser_addr));
/* 配置服务器端IPV4地址结构体 */
ser_addr.sin_family = AF_INET; // 选择ipv4地址族
ser_addr.sin_port = htons(30000); // 端口号:0~65535-->把主机字节序转化为网络字节序
/* 这个ip是你自己的ip */
ser_addr.sin_addr.s_addr = inet_addr("192.168.21.61"); // IP地址-->把主机字节序转化为网络字节序
/* 打电话、请求连接 */
int ret = connect(soc_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
if(ret == -1)
{
perror("connect failed!\n");
close(soc_fd);
return -1;
}
/* 给开发板发送目录 */
send_dir_to_arm(argv[1], soc_fd); // from send.c
/* 挂电话、中断链接 */
close(soc_fd);
return 0;
}
以及需要的send.c和send.h
/***********************************************************
- Copyright (C), Ksd xiongzh
- Filename : send.c
- Author : xiongzh Date : 2019-8-14
- Description :
1、发送目录的函数的具体实现
- Others : none
- Function List :
1. send_dir_to_arm():实现发送目录的递归函数
2. copy_file_to_arm(): 把一个文件拷贝到开发板
3. func(): copy_file_to_arm()需要创建的线程函数
*************************************************************/
#include "send.h"
#include
/***********************************************************
- Description : 把单个文件内的数据发送给开发板
- Calls : bzero()、fread()、write()
strcpy()
- Iuput arg : 1、已连接套接字的地址
- Return : none
- Others : 1、需要注意在读取二进制文件时,使用write()发送的字节数,如果用strlen()函数
来计算缓冲区内的字符串长度的话,会计算不准确,因为不知道二进制文件内有
多少个‘\0’。会导致发送数据缺失;
2、两个write()之间如果不用延迟函数的话,会都被开发板端的read读取,就不
是想要的效果,我需要的是读取完第一个write()写的,再读第二个的
*************************************************************/
void *func(void *arg)
{
int soc_fd = *((int *)arg); // 获取已连接套接字
/* 读取文件的的数据并发送给开发板 */
while(1)
{
bzero(file_node.file_buff, 20);
int ret =fread(file_node.file_buff, 1, 20, file); // 每次读取文件内20个字节
//write(soc_fd, file_node.file_buff, strlen(file_node.file_buff));
write(soc_fd, file_node.file_buff, ret); // 把读取的数据发送至开发板
if(ret < 20)
{
break;
}
}
// 不加延迟的话,对端还没有读取完,这端又写入的话,会被一起读出
usleep(100000);
bzero(file_node.file_buff, 20);
strcpy(file_node.file_buff, "over");
/* 发送完一个文件后再发送一个“over”标记 */
write(soc_fd, file_node.file_buff, strlen(file_node.file_buff));
pthread_exit(NULL);
}
/***********************************************************
- Description : 把目录名、文件名先发送给开发板,然后再调用线程拷贝数据
- Calls : sprintf()、fopen()、strcpy()
write()、pthread_create()
pthread_join()
- Iuput arg : 1、目录名字 2、文件名 3、已连接套接字
- Return : none
- Others : 其实不需要创建线程,只不过没用到,有点虚
*************************************************************/
static void copy_file_to_arm(char *dir_name1, char *d_name, int soc_fd)
{
char name[100];
bzero(name, 100);
/* 合成文件的绝对路径名 */
sprintf(name, "%s%s", dir_name1, d_name);
file = fopen(name, "rb");
if(file == NULL)
{
perror("open file1 failed\n");
exit(1);
}
/* 先把目录名以及需要拷贝的文件名发送至开发板 */
strcpy(file_node.dir_name, dir_name1);
strcpy(file_node.file_name, name);
write(soc_fd, &file_node, sizeof(file_list));
/* 创建线程发送并拷贝数据 */
pthread_t tid;
pthread_create(&tid, NULL, func, (void *)&soc_fd);
/* 阻塞等待线程被接合 */
void *p;
pthread_join(tid, &p);
}
/***********************************************************
- Description : 把目录发送至开发板
- Calls : opendir()、readdir()、sprintf()
copy_file_to_arm()
- Iuput arg : 1、目录名字 2、已连接套接字
- Return : none
- Others : 递归实现目录内的目录的拷贝
*************************************************************/
void send_dir_to_arm(char *dir_name, int soc_fd)
{
/* 打开目录 */
DIR *dir = opendir(dir_name);
struct dirent *ep = calloc(1,sizeof(struct dirent)); // 定义目录结构体
int i=0;
/* 定义需要合成的字符串的缓冲区 */
char dir2_buff[100];
char dir1_buff[100];
char sys_buff[100];
while(1)
{
/* 读取目录内的文件 */
ep = readdir(dir);
if(ep == NULL)
{
/* 读完了或者出错跳出循环 */
break;
}
if(i > 1)
{
/* 把读取的目录项再一次拷贝 */
if(ep->d_type == DT_DIR)
{
/* 合成绝对路径 */
sprintf(dir1_buff,"%s%s/",dir_name,ep->d_name);
send_dir_to_arm(dir1_buff, soc_fd);
}
else
{
/* 发送文件 */
copy_file_to_arm(dir_name, ep->d_name, soc_fd);
}
}
i++;
}
closedir(dir);
}
send.h
#ifndef __CREATE_DIR_H__
#define __CREATE_DIR_H__
#include
#include
#include
#include
// 定义接受数据的结构体变量并取别名
typedef struct file
{
char dir_name[100];
char file_name[100];
char file_buff[100];
}file_list;
file_list file_node; // 定义接受数据的结构体
FILE *file2; // 定义全局的文件指针
int flag; // 定义一个标志
void recv_dir_and_create(int acc_fd);
#endif
再让我们来看下server.c
#include
#include
#include
#include
#include
#include
#include
#include "create_dir.h"
int main(int argc, char *argv[])
{
/* 创建套接字--》获取待连接套接字 */
int soc_fd = socket(AF_INET, SOCK_STREAM, 0);
if(soc_fd == -1)
{
perror("socket failed!");
return -1;
}
/* 定义ipv4地址结构体变量 */
struct sockaddr_in ser_addr;
bzero(&ser_addr, sizeof(ser_addr));
/* 配置IPV4地址结构体 */
ser_addr.sin_family = AF_INET; // 选择ipv4地址族
ser_addr.sin_port = htons(30000); // 端口号:0~65535-->把主机字节序转化为网络字节序
ser_addr.sin_addr.s_addr = inet_addr("192.168.21.61"); // IP地址-->把主机字节序转化为网络字节序
/* 绑定号码(IP地址和端口号)*/
int ret = bind(soc_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
if(ret == -1)
{
perror("bind faield!\n");
close(soc_fd);
return -1;
}
/* 设置铃声-->设置监听套接字-->把待连接套接字变成监听套接字 */
ret = listen(soc_fd, 1);
if(ret == -1)
{
perror("listen faield!\n");
close(soc_fd);
return -1;
}
/* 定义ipv4地址结构体变量 */
struct sockaddr_in client_addr;
int len = sizeof(client_addr);
bzero(&client_addr, sizeof(client_addr));
/* 阻塞等待连接 */
int acc_fd = accept(soc_fd, (struct sockaddr *)&client_addr, (socklen_t *)&len);
if(acc_fd == -1)
{
perror("accept failed!\n");
close(soc_fd);
return -1;
}
bzero(&file_node, sizeof(file_list));
/* 接受目录并拷贝 */
recv_dir_and_create(acc_fd); // from create_dir.c
fclose(file2);
close(acc_fd);
close(soc_fd);
return 0;
}
然后是它所需的create_dir.c以及create_dir.h
create_dir.c
/***********************************************************
- Copyright (C), Ksd xiongzh
- Filename : create_dir.c
- Author : xiongzh Date : 2019-8-14
- Description :
1、接受目录并拷贝
- Others : none
- Function List :
1. recv_dir_and_create() : 接受ubuntu发来的目录并拷贝整个目录
*************************************************************/
#include "create_dir.h"
/***********************************************************
- Description : 接受ubuntu发来的目录并拷贝整个目录
- Calls : read()、bzero()、sprintf()
access()、system()、fopen()
strcmp()、fwrite()
- Iuput arg : 1、已连接套接字
- Return : none
- Others : 接受目录后得先创建名字相同的目录,以及文件
创建目录先合成字符串然后system()
创建文件不需要用touch,fopen是会自动创建
*************************************************************/
void recv_dir_and_create(int acc_fd)
{
int ret = 0;
while(1)
{
/* 先读取发过来的目录名以及文件名 */
ret = read(acc_fd, &file_node, sizeof(file_list));
if(ret == 0)
{
break;
}
/* 合成字符串 */
char sys_buff[50];
bzero(sys_buff, 50);
sprintf(sys_buff, "mkdir %s", file_node.dir_name);
if(access(file_node.dir_name, F_OK))
{
/* 目录不存在就创建 */
system(sys_buff);
flag = 1;
}
else if(!access(file_node.dir_name, F_OK) && flag == 0)
{
/* 最外层目录存在就退出 */
printf("dir exist!\n");
exit(1);
}
/* 创建文件并打开 */
file2 = fopen(file_node.file_name, "wb+");
while(1)
{
/* 读取发送过来的数据并写入文件 */
bzero(file_node.file_buff, 100);
ret = read(acc_fd, file_node.file_buff, 20);
if(!strcmp(file_node.file_buff, "over") || ret == 0 )
{
break;
}
fwrite(file_node.file_buff, 1, ret, file2);
}
}
}
create_dir.h
#ifndef __CREATE_DIR_H__
#define __CREATE_DIR_H__
#include
#include
#include
#include
// 定义接受数据的结构体变量并取别名
typedef struct file
{
char dir_name[100];
char file_name[100];
char file_buff[100];
}file_list;
file_list file_node; // 定义接受数据的结构体
FILE *file2; // 定义全局的文件指针
int flag; // 定义一个标志
void recv_dir_and_create(int acc_fd);
#endif
1、在客户端代码的目录内 gcc *c -o client -pthread
2、在服务端代码的目录内 gcc *c -o server
3、先在服务端代码的目录内 ./server
4、然后在客户端代码的目录内把事先准备好的目录发送过去: ./client file/ (file是事先准备的目录)
5,接下来看看结果:
1)先分别在server和client目录内tree下两个file
2)然后进入两个file目录内 ls -l 查看一下