模拟网盘(基于多线程TCP)

2023-11-10

题目描述:

模拟一个网盘实现以下功能:
用户可以注册、登录、上传和下载文件
用户可以实现类似 ls的功能查询文件信息
多用户间文件可以共享

要求:
基于TCP协议
server端要用多线程实现
在linux下用C语言实现

需要实现的功能:

服务器端:

多线程的实现(注意多用户间的同步)
对于一个刚连接的用户,发送要求输入验证信息
录入用户的注册信息到文件
验证登录信息
处理用户上传的文件
处理用户查询自己现有文件的请求(ls)
对用户的下载请求做出响应
用户可以设置文件权限对他人共享

客户端:

上传
下载
操作服务器传来的菜单即可

程序逻辑:

客户端

流程图

在这里插入图片描述

准备阶段

客户端依次进行建立socket、connect

成功连接(connect)后,进入登陆阶段

程序发出提示信息(菜单一)0:退出 1:登陆 2:注册
然后读取请求(标志位),并发送给服务器(while(1))

  • 读到0:直接发送0给服务器然后退出
  • 读到1:发送1给服务器,然后录入用户名,并发送给服务器,等待服务器的反馈,服务器发回1表示登录成功否则是0表示登录失败(可能是不存在这个用户),登录成功程序需要创建一个本地文件夹(如果没有的话)来保存下载到本地的文件以及要上传的文件(调用函数C_mkdir(username)实现)
  • 读到2:发送2给服务器,然后录入用户名,并发送给服务器,等待服务器的反馈,服务器发回1表示注册成功然后返回再登录即可,否则是0表示注册失败(可能是已经有重名用户)

若登录成功,进入交互阶段

程序发出提示信息(菜单二)0:退出 3:查文件 4:文件共享 5:上传 6:下载
然后读取请求(标志位),并发送给服务器(while(1))

  • 若是3:收取服务器发来的消息即可(注意菜单结束的标志是一个#)
  • 若是4:提示发送要共享的文件名,发送文件名给服务器后,服务器会反馈一条消息,发回‘0’表示服务器端不存在此文件无法共享,发回1表示可以共享,然后在录入一个目标用户名发给服务器即可
  • 若是5:录入并发送待发送文件的文件名。服务器会判断文件是否存在并给出回应,收到反馈0表示已经存在同名文件,1表示服务器无此文件,然后不管哪种情况都开始上传文件(若存在则覆盖)send_file(sock,username,filename);
  • 若是6:录入并发送待下载的文件的文件名。服务器会判断文件是否存在并给出回应,收到反馈0表示服务器无此文件无法下载,1表示服务器存在文件,然后开始下载服务器传来的文件。recv_file(sock, username, filename);

服务器端

在这里插入图片描述

准备阶段

  • 服务器依次建socket、bind、listen后进入while(1)循环,在循环内部accept每接收到一个新的accept就调用pthread_creat创建一个新的线程来做处理。然后主进程依旧做accept。

成功连接后,进入登陆阶段

录入请求(while(1))(对应客户端的菜单1)

  • 若是0:
    断开连接
  • 若是1:
    收取用户名
    调用seek_someone(username)查询用户信息表,看看是否存在这个用户
    发送反馈(存在则登陆成功发1然后break、失败发0然后continue)
  • 若是2:
    录入用户名
    调用seek_someone(username)判断是否已经存在同名用户
    存在同名的话就发送反馈0表示注册失败,然后continue
    不存在同名的话就新建用户信息文件夹mkdir(abs_name,0777)(表示注册成功)然后发送反馈1表示成功,然后continue

登录成功后,进入交互阶段

服务器循环录入标志位(对应客户端的菜单2)

  • 若是3:
    调用发送文件信息表的函数file_list
    发送用户创建的文件和其他用户共享给他的文件两类信息给客户端
  • 若是4:
    录入客户待共享的文件名,查找是否存在此文件seek_file函数,不存在则发送反馈0。存在则发送反馈1,然后接着收取待分享的目标用户,然后利用系统的cp命令将待共享的文件从原用户的文件夹中复制到目标用户的share文件夹中。
  • 若是5:
    接受文件名,然后利用seek_file判断文件是否存在,发送反馈信息给客户端后调用recv_file(sock,username,filename);来接收文件
  • 若是6:
    接受文件名,然后利用seek_file判断文件是否存在,如果不存在发送反馈0给客户端,如果存在发送反馈1给客户端然后调用send_file(sock,username,filename);发送文件给客户

具体算法:

关于程序如何保存用户和文件信息

服务器所在的机器有一个网盘的主目录:./Server_file_folder,然后在此主目录下有子目录,每一个子目录对应一个用户,子目录名就是对应的用户名,判断网盘是否有此用户就是判断该目录下是否有以该用户名命名的文件夹,然后对于用户上传的文件,程序都保存在用户对应的文件夹内,对任何一个用户,其对应的文件夹内都有一个子文件夹share,该文件夹用来保存其他用户分享给自己的文件。
客户端同样的要有一个本地的网盘主目录./local_file,在该文件夹下是每个本地用户的目录,客户要上传的文件和从服务器下载的文件都保存在该文件夹下。

关于共享文件的实现方法:

本程序将每个用户的文件存在独立的文件夹内, 共享文件时,直接将共享给A的文件复制进入A的share文件夹即可。这里的实现方法是利用system()函数调用系统命令cp,调用格式是cp source_file destination_file,只需要先创建一个字符串然后依次将"cp",“空格”,“源文件”,“空格”,"目标文件"这几个字符串利用strcat()函数依次连接在一起然后作为参数传给system()即可。

收发文件结束的标志:

每次发送 BUFFEISIZE+1 个数据
其中第一个位置是标志位
标志位 0 表示后面没有文件了,接收完这个就可以结束了
标志位 1 表示后面还有文件

发送文件列表函数的实现

  • 函数原型:
void file_list(int sock, char *username)

参数是需要发送列表的用户的socket和他的用户名

要发送文件列表就要遍历网盘内的用户文件夹,然后发送所有文件名给客户端即可。
遍历输出某个文件夹下的所有文件(包括子文件夹的方法已经在输出linux下给定目录的所有子目录这篇文章给出)
这里再简单提一下:
我们用到
opendir()打开目录
closedir()关闭目录
readdir()读取目录
利用readdir()返回的句柄entry提取文件中我们需要的信息。
entry->d_name是我们需要的文件名,entry->d_type是文件的类型,需要注意的是entry->d_type== 4表示该文件是一个目录文件,不需要输出。

收发文件函数的实现

  • 函数原型:
void send_file(int sock, char *username, char *file_name)
void recv_file(int sock, char *username, char *file_name)
  • 参数分别是:收/发对象的socket,用户名,待操作文件的文件名。

发文件流程:

  • 首先需要打开文件,对于服务器端,其需要在用户主目录和用户的share两个目录下检索并打开文件,而对于客户端,其只需要再用户主目录打开文件。
  • 然后定义一个缓冲区char send_buf[C_BUFFER_SIZE+1];大小是C_BUFFER_SIZE+1其中第一个字节是一个标志位(取值为0/1)剩下的C_BUFFER_SIZE个字节是实际用来存文件的位置。
  • 然后需要利用fread读取文件,这里我们每次从文件中读取C_BUFFER_SIZE个字节,从缓冲区的第二个字节的位置开始存在缓冲区内。
int fread_len =fread(&send_buf[1], sizeof(char), C_BUFFER_SIZE, fp);
//这里的含义:从fp指向的文件中,读取 C_BUFFER_SIZE个单位的字节
//到以&send_buf[1]为头的区域内,其中每个单位的字节大小为sizeof(char)
//返回值赋给 fread_len,其表示实际从文件内读取到的长度
  • 然后就需要根据实际读取的情况判断:如果 fread_len < C_BUFFER_SIZE说明已经读完了整个文件,当前读取的文件中剩下的内容填不满整个缓冲区,这种情况下表示此条消息应该是最后一条,所以将标志位改为0:send_buf[0] = '0';否则将标志位改为1send_buf[0] = '1';表面当前信息后面还有其他消息。
    然后将带有标志位的缓冲区消息通过socket发出去即可:send(sock, send_buf, fread_len + 1, 0);

程序实际执行过程中没发出一条消息会在屏幕上显示一句正在进行第x次上传,用于提示发送进度。

收文件类似于发文件的逆过程:

  • 首先从socket内读取大小为C_BUFFER_SIZE+1的消息到缓冲区
recv(sock, recv_buf, C_BUFFER_SIZE+1, 0);
  • 然后从缓冲区的第二位开始将缓冲区内容写入文件
fwrite(&recv_buf[1], sizeof(char), recv_len, fp);
  • 然后判断标志位如果是0则终止传输否则继续。
if (recv_buf[0] == '0')
        { //发送完毕
            fclose(fp);
            break;
        }

查找是否存在某个用户或者文件的函数的实现

函数原型

int seek_someone(char *username)
int seek_file(char *username,char *filename)
参数:uesename:待查找的用户名
filename:待查找的文件名
返回值:0表示无此文件/用户
1表示有此文件/用户

这两个函数的实现都比较简单

  • seek_someone:查找网盘是否有某个用户等价于查找网盘主目录下是否有以该用户名命名的文件夹。
    利用opendir函数打开用户对应的主目录,若返回NULL 表示无此文件夹,即不存在此用户return 0,否则存在,关闭文件夹return 1 即可。
  • seek_file:利用fopen()打开用户主目录下的文件,注意打开模式为‘r’,若返回NULL 表示无此文件,这里需要再次判断share文件夹内是否含有该文件,若仍然返回NULL,那么该用户没有此文件return 0,否则存在文件,则关闭文件并return 1;

需要注意的地方

注意pthread_create时,向处理函数传递参数的方法:

不能直接在第三个参数即函数名 的后面直接写参数,
虽然能过编译,但是参数实际没有传进去
错误的写法:pthread_create(&r_thread, NULL, recv_pthread(new_sock), NULL);
因为第三个函数是个函数指针,传的只是地址
应该利用pthread_create的第四个参数
把处理函数的参数改为void *类型,然后把要传的参数强制类型转换为void 传进去
在处理函数内部再把接收到的参数强制类型转换为需要的类型
(注意类型宽度需要<= sizeof(void
) 否则会出错)
正确写法:
pthread_create(&r_thread, NULL, recv_pthread, (void *)&new_sock);

两台机器进行socket通信时,可能在连接时出现错误:

connect error: No route to host(errno:113)

出错原因:server端的防火墙设置了过滤规则
解决办法:使用iptables关闭server端的防火墙

1.暂时打开和关闭

$sudo service iptables stop
$sudo service iptables start

3.永久打开和关闭

$sudo chkconfig iptables on
$sudo chkconfig iptables off

运行结果

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模拟网盘(基于多线程TCP) 的相关文章

  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • ubuntu:升级软件(cmake)-版本消歧(本地编译)[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我的机器上安装了 cmake 2 8 0 来自 ubuntu 软件包 二进制文件放置在 usr bin cmake 中 我需要将 cmake 版本至少
  • 多处理:仅使用物理核心?

    我有一个函数foo它消耗大量内存 我想并行运行多个实例 假设我有一个有 4 个物理核心的 CPU 每个核心有两个逻辑核心 我的系统有足够的内存来容纳 4 个实例foo并行但不是 8 个 此外 由于这 8 个核心中的 4 个是逻辑核心 我也不
  • jq中如何分组?

    这是 json 文档 name bucket1 clusterName cluster1 name bucket2 clusterName cluster1 name bucket3 clusterName cluster2 name bu
  • ftrace:仅打印trace_printk()的输出

    是否可以只转储trace printk 输出于trace文件 我的意思是过滤掉函数跟踪器 或任何其他跟踪器 中的所有函数 一般来说 您可以在选项目录中关闭选项 sys kernel debug tracing options Use ls显
  • 如何使用 GOPATH 的 Samba 服务器位置?

    我正在尝试将 GOPATH 设置为共享网络文件夹 当我进入 export GOPATH smb path to shared folder I get go GOPATH entry is relative must be absolute
  • 内核模式下的线程(和进程)与用户模式下的线程(和进程)有什么区别?

    我的问题 1 书中现代操作系统 它说线程和进程可以处于内核模式或用户模式 但没有明确说明它们之间有什么区别 2 为什么内核态线程和进程的切换比用户态线程和进程的切换花费更多 3 现在 我正在学习Linux 我想知道如何在LINUX系统中分别
  • MySQL 中的创建/写入权限

    我的设备遇到一些权限问题SELECT INTO OUTFILE陈述 当我登录数据库并执行简单的导出命令时 例如 mysql gt select from XYZ into outfile home mropa Photos Desktop
  • 如何在基于 Linux 的系统上的 C 程序中使用 mqueue?

    如何在基于 Linux 的系统上的 C 程序中使用 mqueue 消息队列 我正在寻找一些好的代码示例 可以展示如何以正确且正确的方式完成此操作 也许是一个操作指南 下面是一个服务器的简单示例 该服务器接收来自客户端的消息 直到收到告诉其停
  • Linux - 从第二个选项卡获取文本

    假设我们有这样的文件 一些文本11 一些文本12 一些文本13 一些文本21 一些文本22 一些文本23 文本由制表符分隔 我们知道第 1 列中的一些文本 但希望从第 2 列中获取文本 我知道我可以通过以下方式获取线路 grep somet
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost
  • Windows CE 与嵌入式 Linux [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 现在我确信我们都清楚 Linux 与 Windows 桌面的相对优点 然而 我对嵌入式开发世界的了解却少得多 我主要对行业解决方案感兴
  • 高效的内存屏障

    我有一个多线程应用程序 其中每个线程都有一个整数类型的变量 这些变量在程序执行期间递增 在代码中的某些点 线程将其计数变量与其他线程的计数变量进行比较 现在 我们知道在多核上运行的线程可能会无序执行 一个线程可能无法读取其他线程的预期计数器
  • 如何允许应用程序声明“https”方案 URI? (即如何从 https URL 打开桌面应用程序?)

    目前我正在尝试为 OAuth 2 0 授权流程创建一个客户端 实际上是一个本机应用程序 并且在规范中就在这儿 https www rfc editor org rfc rfc8252 section 7 2据说有 3 种方法来处理重定向 U
  • 为什么C Clock()返回0

    我有这样的事情 clock t start end start clock something else end clock printf nClock cycles are d d n start end 我总是得到输出 时钟周期是 0
  • git 错误:无法处理 https

    当我尝试使用 git clone 时https xxx https xxx我收到以下错误我不处理协议 https 有人可以帮我吗 完整消息 dementrock dementrock A8Se git 克隆https git innosta
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • Linux 上的基准测试程序

    对于一项任务 我们需要使用不同的优化和参数来对我们的实现进行基准测试 有没有一种可行的方法可以在Linux命令行 我知道时间 上使用不同的参数对小程序进行基准测试 从而为我提供CSV或类似内容的时间数据 输出可能类似于 Implementa
  • 当用户按下打印时运行脚本,并且在脚本结束之前不开始假脱机(linux,cups)

    我需要做的是结合用户按下打印来执行 python 程序 脚本 并且在该程序退出之前不要让打印作业假脱机 原因是打印驱动程序不是开源的 我需要更改用户设置 在本例中是部门 ID 和密码 通常是每个用户 但因为这是一个信息亭 具有相同帐户的不同
  • Fedora dnf 更新不起作用?

    当我尝试使用 update 命令更新 Fedora 22 时 sudo dnf update 我收到以下错误 错误 无法同步存储库 更新 的缓存 无法准备内部镜像列表 Curl 错误 6 无法解析主机名 无法解析主机 mirrors fed

随机推荐

  • js DOM

    DOM Document Object Model HTML 和 XML 文档的编程接口 通过 DOM JavaScript 能够访问和改变 HTML 文档的所有元素 1 查找 通过 id 查找 HTML 元素 div div 2 通过标签
  • Paper and Codes Leaderboard

    目录 介绍 模型入选标准 1 目标检测 Paper and Codes for COCO by 2023 3 31 COCO FPS Models by 2023 02 18 Look at Batch Size 2 图像分类 ImageN
  • 【Backbone: MLP-Mixer】MLP-Mixer: An all-MLP Architecture for Vision

    Abstract CNN和attention based结构很棒 但不是必须的 本文提出MLP Mixer 一种基于多层感知机 MLPs 的框架 包含两种layers 1 channel mixing MLPs 应用在image patch
  • C++(30)——lambda表达式原理及应用

    lambda lambda这个词起源于数学上的 在C 中利用lambda表达式 可以方便的定义和创建匿名函数 lambda可以看做函数对象的升级版 改进了函数对象以下的缺点 使用在泛型算法中的参数传递的过程中 比较性质 自定义操作 优先级队
  • J2EE之自定义MVC框架知识(中篇)

    J2EE之自定义MVC框架知识 中篇 文章目录 J2EE之自定义MVC框架知识 中篇 前言 1 优化中央控制器中的action容器 使其加载变成可配置的 1 1 编写 xml文件 config xml 1 2 导入XML建模相关的类 Act
  • INNO setup 制作安装包

    1 获取SQLserver安装路径vardbpath string rtn boolean rtn RegQueryStringValue HKEY LOCAL MACHINE SOFTWAREMicrosoftMSSQLServerSet
  • 95-38-150-Buffer-CompositeByteBuf

    文章目录 1 概述 2 继承关系 1 概述 CompositeByteBuf实际就是个ByteBuf的包装器 它将多个ByteBuf组合成一个集合 然后对外提供统一的ByteBuf接口 2 继承关系
  • 对象,我遇见了你

    对象 什么是对象 对象属性 Object assign Object create Object is Object keys Object values Object entries Object fromEntries Object t
  • Day 21 B. T-primes

    Problem We know that prime numbers are positive integers that have exactly two distinct positive divisors Similarly we l
  • Windows下安装nvm及搭建Angular环境的步骤整理

    环境 操作系统 windows 8 1 安装nvm nvm windows 下载https github com coreybutler nvm windows releases 我把nvm noinstall zip解压到c dev nv
  • PS2汉化1 字库处理

    引语 其实字库处理很难说有一个统一的方法 不同的程序都需要不同方法来处理 关于常见位图字库的详细信息 下面是字库存在于ELF ERX文件时的处理思路 字模替换 最天真朴素 最通用的处理方式 适用于原字库大小能够塞下汉化所用的全部文字的情况
  • XSS、CSRF攻击以及预防手段

    文章目录 XSS 反射型 持久型 DOM型 XSS如何防御 CSRF XSS XSS全程Cross Site Scripting 名为跨站脚本攻击 是一种常见于 Web 应用中的计算机安全漏洞 恶意攻击者往 Web 页面里嵌入恶意的客户端脚
  • 【VBA编程】VBA基础语法(一)

    一 VBA中的数据类型 VBA里的数据类型有 字节型 Byte 整数型 Integer 长整数型 Long 单精度浮点型 Single 双精度浮点型 Double 货币型 Currency 小数型 Decimal 字符串型 String 日
  • 《数据清洗》第五章操作实例

    案例一介绍 通过Kettle工具 消除CSV文件merge csv中完全重复的数据 1 打开Kettle工具 创建转换 通过使用Kettle工具 创建一个转换repeat transform 并添加 CSV文件输入 控件 唯一行 哈希值 控
  • 把多页Word文档缩小打印到同一张纸上

    方法一 首先要确定纸张的大小和方向 系统默认的是 A4 纸 方向纵向 如果要改变 可单击 文件 页面设置 进行相应的更改 单击 插入 文本框 横排 然后在刚才新建的文档中画出一个文本框 大小大约为这页纸的四分之一 然后将鼠标放到该文本框的边
  • 关于某些特殊时候按钮disabled属性失效时的解决办法

    今天遇到了一个BUG 导致下一步按钮中的属性即时有disabled时 点击按钮依然会触发按钮的点击事件 以下为下一步按钮的JS代码 下一步 btn step click function e if this hasClass layui b
  • 常见排序算法--合并排序

    思路 将一个无序的序列分组 直至分为每两个元素一组 如果有单个元素剩余 则可以剩余的单个元素自己一组 小组内排序 然后合并成一个有序的序列 例子 排序过程如图所示 图片摘选自 https blog csdn net ZY cat artic
  • 腾讯云nginx配置ssl证书实现https

    1 申请证书 2 下载证书 签发后 里面包含nginx 1 1 域名 bundle crt 2 2 域名 key 3 配置nginx文件 在配置ssl证书之前 要确保你的nginx已经安装了ssl模块 如果没有请安装 gt sbin ngi
  • rstudio导入txt文件_r语言怎么读取txt文件

    展开全部 1 r语言62616964757a686964616fe59b9ee7ad9431333431376533读取txt文件的方法 首先根据下图图片中的命令代码进行输入 2 然后这样就可以读取txt文件了 结果图如下 3 R读取csv
  • 模拟网盘(基于多线程TCP)

    题目描述 模拟一个网盘实现以下功能 用户可以注册 登录 上传和下载文件 用户可以实现类似 ls的功能查询文件信息 多用户间文件可以共享 要求 基于TCP协议 server端要用多线程实现 在linux下用C语言实现 需要实现的功能 服务器端