socket编程---通过tcp发送目录(Ubuntu下实现)

1、可以在不同主机的Ubuntu之间通信,为了方便看效果,这里的话就在一台主机的一个Ubuntu下传送

2、首先需要创建一个客户端client发送,和一个server接受,这里就分别编写两个.c代码来实现:client.c、server.c

首先来看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
socket编程---通过tcp发送目录(Ubuntu下实现)_第1张图片
socket编程---通过tcp发送目录(Ubuntu下实现)_第2张图片
2)然后进入两个file目录内 ls -l 查看一下
socket编程---通过tcp发送目录(Ubuntu下实现)_第3张图片
socket编程---通过tcp发送目录(Ubuntu下实现)_第4张图片

思路:通过递归读取目录内的文件以及目录;发送某一个文件时,先发其所在目录名以及文件名先传输过去,在服务端创建目录以及文件后,再开始数据传输以及拷贝;

如果想试试不同主机之间的发送,只要修改为相应的ip就可以了,但是得是一个局域网内哦!

你可能感兴趣的:(c语言,socket)