模拟网盘(基于多线程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) 的相关文章

  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 在centos中安装sqlite3 dev和其他包

    我正在尝试使用 cpanel 在 centos 机器上安装 sqlite dev 和其他库 以便能够编译应用程序 我对 debian 比 centos 更熟悉 我知道我需要的库是 libsqlite3 dev libkrb5 dev lib
  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • 使用非规范地址检索内存数据会导致 SIGSEGV 而不是 SIGBUS

    我无法使用以下汇编代码产生 总线错误 这里我使用的内存地址不是合法的 规范地址 那么 我怎样才能触发该错误呢 我在带有 NASM 2 14 02 的 Ubuntu 20 04 LTS 下运行这段代码 但它会导致负载出现 SIGSEGV 分段
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 添加文件时运行 shell 命令

    我的 Linux 机器上有一个名为 images 的文件夹 该文件夹连接到一个网站 该网站的管理员可以向该网站添加图片 但是 当添加图片时 我想要一个命令来运行调整目录中所有图片的大小 简而言之 我想知道当新文件添加到特定位置时如何使服务器
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 在生产服务器上使用 Subversion 使文件生效的最佳方法是什么?

    目前我已经设置了 subversion 这样当我在 Eclipse PDT 中进行更改时 我可以提交更改 它们将保存在 home administrator 中项目文件 该文件具有 subversion 推荐的 branches tags
  • linux下如何从文本文件中获取值

    我有一些文本格式的文件 xxx conf 我在这个文件中有一些文本 disablelog 1 当我使用 grep r disablelog oscam conf 输出是 disablelog 1 但我只需要值1 请问你有什么想法吗 一种方法
  • 使用os.execlp时,为什么`python`需要`python`作为argv[0]

    代码是这样的 os execlp python python child py other args this works os execlp python child py other args this doesn t work 我读过
  • 为什么同一个curl命令在windows和linux下输出不同的东西?

    为什么同样的curl o file https www link com 命令输出不同的东西 例如 如果我运行命令curl o source txt https www youtube com playlist list PLIx6Fwnp
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序

随机推荐

  • 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语言实现 需要实现的功能 服务器端