首页 > 系统相关 >Linux网盘程序——客户端(完整注释版)

Linux网盘程序——客户端(完整注释版)

时间:2023-10-09 11:01:03浏览次数:45  
标签:文件 res buffer 网盘 MSG file Linux msg 客户端

客户端

#include<cstdio>//C++标准库的头文件
#include<unistd.h>//Unix标准头文件
#include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换
#include<string.h>//字符串头文件
#include<stdlib.h>//包含了一些标准库函数,用于内存分配、释放以及其他一些通用的实用功能
#include<pthread.h>//线程相关,用于支持多线程编程
#include<sys/stat.h>//用于获取文件状态信息的函数和数据结构,例如文件的大小、权限
#include<sys/types.h>//包含了一些系统相关的数据类型和常量,例如文件描述符
#include<errno.h>//包含了错误码(errno)的定义,用于在程序中处理错误情况
#include<fcntl.h>//包含了文件控制操作相关的常量和函数,例如打开文件、关闭文件、读取文件等


#define MSG_TYPE_LOGIN 0//表示登录类型
#define MSG_TYPE_FILENAME 1//表示查询文件目录操作类型
#define MSG_TYPE_DOWNLOAD 2//表示下载文件操作类型
#define MSG_TYPE_UPLOAD   3//表示上传文件操作类型
#define MSG_TYPE_UPLOAD_DATA  4//表示上传文件数据类型

typedef struct msg
{
	int type;//协议类型  0 表示登陆包  1.文件名传输包 2.文件下载包 ……
	char fname[50];//存放文件名
	char buffer[1024];//存放文件数据
	int bytes;//这个字段用来记录传输文件时每个数据包实际的文件字节数
}MSG; //这个结构体会根据业务需求的不断变化,可能会增减新的字段。

char up_file_name[20] = { 0 };
int fd = -1; //这个是用来打开文件进行读写的文件描述符,默认情况下为0表示没有打开文件
//客户端的线程函数

//上传文件数据的线程函数
void* upload_file_thread(void* args)
{
	// 客户端实现上传文件到服务器的逻辑思路
	//1 首先要打开文件
	MSG up_file_msg = { 0 };	//上传文件消息的结构体变量
	char buffer[1024] = { 0 }; // 用来保存读取文件的数据缓冲区
	int client_socket = *((int*)args); //定义套接字描述符接收形参
	int res = 0;	//实际读写字节数
	int fd = -1;	//文件描述符
	fd = open("./download/css.txt", O_RDONLY); //打开文件, O_RDONLY表示只读
	if (fd < 0)
	{
		perror("open up file error : ");
		return NULL;
	}

	up_file_msg.type = MSG_TYPE_UPLOAD_DATA;//类型设置为上传数据文件,便于服务器识别
	while ((res = read(fd, buffer, sizeof(buffer))) > 0)//从文件中读取数据,并将读取的数据存储到 buffer 中。读取的字节数保存在 res 变量中
	{
		// 要把文件数据内容拷贝到 MSG 结构体中的 buffer 中
		memcpy(up_file_msg.buffer, buffer, res);//将从文件读取的数据复制到 up_file_msg.buffer 中
		up_file_msg.bytes = res;
		//下面的res是复用,和上面的res无关
		res = write(client_socket, &up_file_msg, sizeof(MSG));// write 函数将 up_file_msg 结构体中的数据通过套接字发送给服务器
		memset(buffer, 0, sizeof(buffer));//清缓存
		memset(up_file_msg.buffer, 0, sizeof(up_file_msg.buffer));
	}
	close(fd);//关目录
}

//客户端的多线程的线程函数
void* thread_func(void* arg) {
	int client_socket = *((int*)arg);//定义套接字描述符接收形参
	MSG recv_msg = { 0 };//定义接收消息的结构体变量
	int res;//实际读写字节数
	
	//循环read不断接收从服务器端发来的数据
	while (1) {
		res = read(client_socket, &recv_msg, sizeof(MSG));
		if (recv_msg.type == MSG_TYPE_FILENAME) {//服务器发过来的是包含文件名的数据报,就输出文件名
			printf("server path filename=%s\n", recv_msg.fname);
			memset(&recv_msg, 0, sizeof(MSG));
		}
		else if (recv_msg.type == MSG_TYPE_DOWNLOAD)  ///服务器发过来的是包含文件的数据,就做好接收准备
		{
			//1.要确定下这个文件放在哪个目录下,我们可以调mkdir函数创建一个目录:默认东西都放在download1目录下
			if (mkdir("download1", S_IRWXU) < 0)
			{
				if (errno == EEXIST)//如果目录已经存在就无视,否则输出报错信息
				{
					//printf("dir exist continue!\n");
				}
				else
				{
					perror("mkdir error");
				}
			}
			//2.目录创建没问题之后,就要开始创建文件
			if (fd == -1)//如果文件还没有打开过
			{
				//O_CREAT 表示如果文件不存在则创建
				//O_WRONLY 表示文件将以写入方式打开,允许你写入文件的内容。
				//0666 表示文件所有者、文件组和其他用户都有读取和写入权限。
				fd = open("./download1/hello2", O_CREAT | O_WRONLY, 0666);//打开成功之后肯定会有个文件描述符返回
				if (fd < 0)//表示创建/写入失败
				{
					perror("file open error:");
				}
			}
			//通过上面的创建目录,以及文件描述符的判断通过后,就可以从MSG结构体里面的buffer取数据了
			//将 recv_msg.buffer 中的数据写入由文件描述符 fd 标识的文件
			//写入的字节数由 recv_msg.bytes 指定
			res = write(fd, recv_msg.buffer, recv_msg.bytes);
			if (res < 0)//表示写入失败
			{
				perror("file write error:");
			}
			//那么我们怎么判断文件内容都全部发完了呢?可以通过recv_msg.bytes是否小于recv_buffer的最大字节数1024
			if (recv_msg.bytes < sizeof(recv_msg.buffer))
			{
				printf("file download finish!\n");
				close(fd);
				fd = -1;
			}
		}
	}
}

void net_disk_ui()
{
	printf("=========================TCP网盘程序=================================\n");
	printf("=========================功能菜单=================================\n");
	printf("\t\t\t1、查询文件\n");
	printf("\t\t\t2、下载文件\n");
	printf("\t\t\t3、上传文件\n");
	printf("\t\t\t4、UI界面\n");
	printf("\t\t\t0、退出系统\n");
	printf("=====================================================================\n");
	printf("请选择你要执行的操作: ");

}
int main() {
	int client_socket;//创建客户端套接字描述符
	pthread_t thread_id;
	pthread_t thread_send_id;
	int res;
	char c;
	MSG send_msg = { 0 };//定义发送给服务器消息的结构体变量
	
	client_socket = socket(AF_INET, SOCK_STREAM, 0);//创建客户端套接字
	if (client_socket < 0) {
		perror("client socket failed:");
		return 0;
	}

	struct sockaddr_in server_addr;// server_addr,用于存储套接字的地址信息
	server_addr.sin_family = AF_INET;//AF_INET 表示IPv4地址族
	server_addr.sin_addr.s_addr = inet_addr("192.168.43.128");//这里填Ubantu虚拟机的网卡IP地址,如果服务器和客户端在同一台机子上,则IP地址可以写成127.0.0.1
	server_addr.sin_port = htons(6666);//端口号赋值
	//创建好套接字之后,通过connect连接到服务器
	if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
		perror("connect error!");
		return 0;
	}
	printf("客户端连接服务器成功!\n");

	//创建线程,支持多线程
	pthread_create(&thread_id, NULL, thread_func, &client_socket);
	net_disk_ui();//输出UI界面
	
	while (1) 
	{
		c = getchar();
		switch (c) {//根据键盘输入来执行不同的操作
			case '1':
				//要让服务器给我们发送目录信息
				//这个while循环本身也是死循环,那么我们要让客户端也创建线程,让接受服务器的数据的代码放到线程里面
				send_msg.type = MSG_TYPE_FILENAME;//指定命令类型为查询文件目录类型
				res=write(client_socket, &send_msg, sizeof(MSG));//把send_msg数据发送出去
				if (res < 0) {//表示发送失败
					perror("send msg error:");
				}
				memset(&send_msg, 0, sizeof(MSG));//清缓存
				break;
			case '2':
				send_msg.type = MSG_TYPE_DOWNLOAD;//指定命令类型为下载文件操作类型
				res = write(client_socket, &send_msg, sizeof(MSG));
				if (res < 0)
				{
					perror("send msg error:");
				}
				memset(&send_msg, 0, sizeof(MSG));
				break;
			case '3':
				send_msg.type = MSG_TYPE_UPLOAD;//指定命令类型为上传文件操作类型
				strcpy(up_file_name, "css.txt");//上传文件的文件名
				printf("input upload filename:");
				puts(up_file_name);
				//在上传文件给服务器之前,你要先发送一个数据包给服务器,告诉服务器我这边准备上传文件了
				strcpy(send_msg.fname, up_file_name);
				res = write(client_socket, &send_msg, sizeof(MSG));
				if (res < 0)
				{
					perror("send upload package error:");
					continue;
				}
				memset(&send_msg, 0, sizeof(MSG));
				pthread_create(&thread_send_id, NULL, upload_file_thread, &client_socket);
				break;
				//由于考虑到上传文件是需要比较长的时间,考虑到如果文件很大,那么就需要非常长的时间,这个时候如果写
				// //在这里那么久卡诺导致其他功能卡住排队等待,因此需要把发送文件内容的代码放进线程里面,因此需要创建线程
				// //还需要创建一个新的线程,来专门处理文件上传任务
			case '\n':
				net_disk_ui();
				break;
			case '0':
				return 0;
			
		}
		printf("按4即可显示UI界面\n");
	}
	
	return 0;
}

 

标签:文件,res,buffer,网盘,MSG,file,Linux,msg,客户端
From: https://www.cnblogs.com/CS-liujiajun/p/17750978.html

相关文章

  • linux如何查看操作系统版本信息
    linux查看版本信息,命令更全面。一、linux下如何查看已安装的centos版本信息:1.Linux查看当前操作系统版本信息 cat/proc/versionLinuxversion2.6.32-696.el6.x86_64([email protected])(gccversion4.4.720120313(RedHat4.4.7-18)(GCC))#1SMPTueMa......
  • Linux Bridge与veth
    创建一对veth(VirtualEthernet)接口,并将它们连接到不同的网络命名空间,然后通过LinuxBridge相连#创建第一个veth对ipnetnsaddns1#创建命名空间ns1ipnetnsexecns1iplinksetloup#在ns1中启用loopback接口iplinkaddveth1typevethpe......
  • Linux------微内核和宏内核以及混合型内核
    宏内核(MonolithicKernel)单一内核,宏内核将大多数操作系统功能集成在一个单一的内核中。性能:由于所有的功能都在内核空间运行,所以宏内核通常具有较高的性能,因为它可以直接访问内核数据结构和函数复杂性:宏内核的复杂性较高,容易导致内核变的庞大和难以维护。Linux......
  • Linux Centos7 安装Docker环境
    一、Docker介绍Docker是一个开源的容器引擎,基于Go语言开发,同时基于Apache2.0协议开发。对Docker简单的理解就是使用Docker可以把一台服务器隔离成一个个独立的容器,我们可以把这地方描述的容器理解成一个沙盒。在每个容器中运行一个程序,不同的容器之间相互隔离,容器的创建,停止,以及......
  • linux学习记录 10.9
    知识点:git-版本管理工具,一个树的结构来维护所有历史版本,可持久化,支持多人合作工作区:仓库的目录。工作区是独立于各个分支的。 =》暂存区:工作区写入版本库前的缓存区=》 版本库:将所有版本用一棵树的形式存下来每次head只会处于一个结点,要将当前暂存区存到版本库里的时候,就......
  • linux学习记录 10.8
    acterminal分配了如下信息:(1)user用户名  (2)hostnameip地址(3)password密码homework4getinfo查看上述信息 知识点:1、ssh登录到某个自己的服务器sshuser@hostname=登录服务器 exit/logout/ctrl+d=退出退出后进入.ssh看到一个known_hosts就会记录刚......
  • Arm Linux内存管理(一)
    ArmLinux内存管理(一)大文  2人赞同了该文章一、Armlinux的基本概念1.ArmLinux物理内存Arm平台内存大小的定义在DTS设备树中定义arch/arm/boot/dts/vexpress-v2p-ca9.dts中内核在启动过程中,需要解析dts文件。代码的调用关系为:start_kernel()->setu......
  • 学习笔记421—Win7下使用U盘安装linux Ubuntu16.04双系统图文教程
    Win7下使用U盘安装linuxUbuntu16.04双系统图文教程安装步骤:1、下载Ubuntu16.04镜像软件;2、使用ultraISO软件制作U盘启动盘;3、利用U盘启动盘来安装Ubuntu系统;4、使用EasyBCD创建启动系统启动引导;5、重启系统即可。Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源G......
  • Linux入门知识教程
    网络连接概念IP地址时一种逻辑地址,用来标识网络中一个个主机IP地址=网络地址+主机地址IP地址是一个4*8bit(1字节)由0/1组成的数字串(IP协议)子网掩码NETMASK子网掩码只有一个功能,就是将IP地址划分为网络地址+主机地址子网掩码与IP地址进行与运算(都为1的......
  • Linux file system All In One
    LinuxfilesystemAllInOne图解Linux文件系统filetype-fileddirectorybblocklsymbollink(softlink/hardlink)filepermissions3个字组,共三组;userpermission用户权限grouppermission组权限otherpermission其他权限每组符号表示的含义......