Linux 网络编程项目--简易ftp

news/2024/5/3 19:32:06

主要代码

config.h


#define LS     0
#define GET    1
#define PWD    2#define IFGO   3#define LCD    4
#define LLS    5
#define CD     6
#define PUT    7#define QUIT   8
#define DOFILE 9struct  Msg
{int type;char data[1024];char secondBuf[128];
};

服务器:

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// #include<linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>int get_cmd_type(char *cmd)
{if (!strcmp("ls", cmd))return LS; // 这里类命令没有参数直接strcmp 比较即可if (!strcmp("quit", cmd))return QUIT;if (!strcmp("pwd", cmd))return PWD;if (strstr(cmd, "cd") != NULL)return CD;  对于有参数的命令,则去 寻找他的子串是否包含这个命令if (strstr(cmd, "get") != NULL)return GET;if (strstr(cmd, "put") != NULL)return PUT;
}char *getDesDir(char *cmsg) // 获取后段字符串 --- 参数
{char *p = strtok(cmsg, " ");p = strtok(NULL, " ");return p;
}void msg_handler(struct Msg msg, int fd)
{char dataBuf[1024] = {0};char *file = NULL;int fdfile;printf("cmd: %s\n", msg.data);int ret = get_cmd_type(msg.data);switch (ret){ // 根据返回的指令选择,处理的方式case LS:case PWD:// popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。FILE *r = popen(msg.data, "r");fread(msg.data, sizeof(msg.data), 1, r);write(fd, &msg, sizeof(msg)); // 写入socket通道的 fdbreak;case CD:char *dir = getDesDir(msg.data);printf("dir: %s\n", dir);chdir(dir);break;case GET:msg.type=DOFILE;file = getDesDir(msg.data);  获取后段字符串 --- 参数 -文件名if (access(file, F_OK) == -1){                                       // access 判断文件是否存在strcpy(msg.data, "No this  file!"); 将 "no this file"的信息写入data,并且write返回给客户端write(fd, &msg, sizeof(msg));}else{// 存在,打开服务器目录下的文件,读取里面的内容,写入到客户端文件中fdfile = open(file, O_RDWR);            // 打开服务器目录下的文件read(fdfile, dataBuf, sizeof(dataBuf)); // 读取里面的内容close(fdfile);                          // 用完记得关闭文件strcpy(msg.data, dataBuf);    // 写入数据write(fd, &msg, sizeof(msg)); // 写入到客户端文件中}break;case PUT:                                                       // 上传文件到服务器fdfile = open(getDesDir(msg.data), O_RDWR | O_CREAT, 0666); // 以可读可写的形式创建 需要put的文件,给0666的权限 -- 可读可写write(fdfile, msg.secondBuf, strlen(msg.secondBuf));         向新创建的文件中写入数据,secondBufclose(fdfile);break;case QUIT:puts("Client Quit!");exit(-1);}
}int main(int argc, char **argv)
{int s_fd;int c_fd;struct sockaddr_in s_addr;struct sockaddr_in c_addr;int nread;// char msg[128]={0};struct Msg msg;char readBuf[128] = {0};if (argc != 3){ // 判断传参是否正确puts("Improper parameters");exit(-1);}memset(&s_addr, 0, sizeof(struct sockaddr_in));memset(&c_addr, 0, sizeof(struct sockaddr_in));int clen = sizeof(struct sockaddr_in);// 1. socket -- IPv4  流形式i 0 --默认 TCPs_fd = socket(AF_INET, SOCK_STREAM, 0);if (s_fd == -1){perror("socket");exit(-1);}// 初始化bind需要的structs_addr.sin_family = AF_INET;// htons -- h - host to  ns-net shorts_addr.sin_port = htons(atoi(argv[2])); // 通过传参指定端口// a -- ASCLL  传参方式得到IPinet_aton(argv[1], &s_addr.sin_addr);// 3.bindbind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));// listenlisten(s_fd, 10);// 4.acceptwhile (1){c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); // 获得新的客户端 描述符if (c_fd == -1){perror("accept");}printf("get connect. %s\n", inet_ntoa(c_addr.sin_addr));if (fork() == 0){ // 接入新的客户端 创建子进程来处理读while (1){// read()memset(msg.data, 0, sizeof(msg.data));nread = read(c_fd, &msg, sizeof(msg));if (nread == 0){printf("client  out\n"); // 读取时已经到达文件的末尾,则返回0。表示客户端退出break;}else if (nread > 0) // 读取成功{msg_handler(msg, c_fd);}}}}close(c_fd);close(s_fd);return 0;
}

客户端:

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// #include<linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>int get_cmd_type(char *cmd)
{if (strstr(cmd, "lcd"))return LCD;if (strstr(cmd, "cd"))return CD;if (strstr(cmd, "get"))return GET;if (strstr(cmd, "put"))return PUT;if (!strcmp("quit", cmd))return QUIT;if (!strcmp("ls", cmd))return LS;if (!strcmp("lls", cmd))return LLS;if (!strcmp("pwd", cmd))return PWD;return -1; //  找不到这个指令就放回-1
}char *getdir(char *cmsg) // 获取后段字符串 --- 参数
{char *p = strtok(cmsg, " ");p = strtok(NULL, " ");return p;
}int cmdHanler(struct Msg msg, int fd)
{char buf[32];char *dir = NULL;int ret;int fdfile;ret = get_cmd_type(msg.data);switch (ret){case LS:case CD:case PWD:write(fd, &msg, sizeof(msg));break;case GET:write(fd, &msg, sizeof(msg));break;case PUT:strcpy(buf, msg.data);dir = getdir(buf);if (access(dir, F_OK) == -1){printf("%s not exit\n", dir);}else{fdfile = open(dir, O_RDWR);read(fdfile, msg.secondBuf, sizeof(msg.secondBuf));  客户端把数据读到secondBuf里面close(fdfile);write(fd, &msg, sizeof(msg)); // 将内容写给服务器fd}break;case LLS:system("ls");break;case LCD:dir = getdir(msg.data);chdir(dir);break;case QUIT:strcpy(msg.data, "quit");write(fd, &msg, sizeof(msg));close(fd);exit(-1);}
return ret;
}void  handlerServerMessage(int c_fd,struct Msg msg){int nread;struct Msg msgget;int newfdfile;nread=read(c_fd,&msgget,sizeof(msgget));if(nread==0){printf("server is out,quit\n");exit(-1);}else if(msgget.type == DOFILE){char *p=getdir(msg.data);newfdfile=open(p,O_RDWR|O_CREAT,0600); write(newfdfile,msgget.data,strlen(msgget.data));putchar('>');fflush(stdout);}
else{
printf("----------------------------------\n");
printf("\n%s\n",msgget.data);
printf("----------------------------------\n");putchar('>');fflush(stdout);}}int main(int argc, char **argv)
{int c_fd;struct sockaddr_in c_addr;struct Msg msg;int nread;if (argc != 3){ // 判断传参是否正确puts("Improper parameters");exit(-1);}// char msg[128] = {0};char readBuf[128];memset(&c_addr, 0, sizeof(struct sockaddr_in));int clen = sizeof(struct sockaddr_in);// 1. socket -- IPv4  流形式i 0 --默认 TCPc_fd = socket(AF_INET, SOCK_STREAM, 0);if (c_fd == -1){perror("socket");exit(-1);}// 初始化bind需要的structc_addr.sin_family = AF_INET;// htons -- h - host to  ns-net shortc_addr.sin_port = htons(atoi(argv[2]));// a -- ASCLLinet_aton(argv[1], &c_addr.sin_addr);// 2.connect -- 阻塞等待连接int p_c = connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in));if (p_c == -1){perror("connect");exit(-1);}printf("get connect. %s\n", inet_ntoa(c_addr.sin_addr));while (1){ // 我们希望能一直读写 -- 实现聊天功能memset(msg.data, 0, sizeof(msg.data));gets(msg.data); // 一直 获取 命令输入int ret = cmdHanler(msg, c_fd);if (ret > IFGO){putchar('>');fflush(stdout);continue;}if (ret == -1){puts("command not");putchar('>');fflush(stdout);continue;}handlerServerMessage(c_fd, msg);}return 0;
}

代码解析:

基本命令

如下图: 

我们在客户端中将LS PWD CD命令写入msg,到服务器这边就会读取他们的值

msg.data 的第一段子串是命令,第二串是参数 -- 比如 cd .. 

ls,pwd只有命令,调用popen来调用

popen()函数补充



  popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。

  这个进程必须由 pclose() 函数关闭,而不是 fclose() 函数。pclose() 函数关闭标准 I/O 流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell 不能被执行,则 pclose() 返回的终止状态与 shell 已执行 exit 一样。

也就是,popen创建管道,执行shell命令将文件流中的某些数据读出

get

服务器先将文件写入msg

客户端再通过msg获取

 //这个函数用来读取我们的文件指令

 if 类型是文件操作 FOFILE,就创建创建文件向其中写入数据,这样就得到了服务器的文件 --GET

else 直接将操作打印出来 


 

put:

如下图,先从客户端开始说,客户端先确保,文件存在,然后将文件打开,将文件内容读取到secondBuf里面,关文件,写入fd(服务器)的msg中。

服务器,直接创建一个文件来接收我们刚刚传过来的数据,写入文件,关文件。

_access()函数补充


头文件:#include<io.h>
函数原型:int _access(const char *pathname, int mode);
参数:pathname 为文件路径或目录路径,mode 为访问权限
返回值:如果文件具有指定的访问权限,则函数返回0;如果文件不存在或者不能访问指定的权限,则返回-1

lcd:

读取到dir 为第二个子串 -- 参数

调用chdir 跳转即可

chdir()函数 补充


系统调用 chdir()和 fchdir()可以用于更改进程的当前工作目录,函数原型如下所示:

#include <unistd.h>

int chdir(const char *path);
int fchdir(int fd);

首先,使用这两个函数之一需要包含头文件<unistd.h>
函数参数和返回值含义如下:
path:将进程的当前工作目录更改为 path 参数指定的目录,可以是绝对路径、也可以是相对路径,指定的目录必须要存在,否则会报错。
fd:将进程的当前工作目录更改为 fd 文件描述符所指定的目录(譬如使用 open 函数打开一个目录)。
返回值:成功均返回 0;失败均返回-1,并设置 errno。

// 其余细节,还请读者看代码注释


http://www.mrgr.cn/p/46300287

相关文章

GIS融合之路(六)-Cesium的雨雪风雷电效果

终于来到系列第六篇了,也来到大家最喜闻乐见天气效果 系列传送门: 山海鲸可视化:GIS融合之路(一)技术选型CesiumJS/loaders.gl/iTowns? 山海鲸可视化:GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合 山海鲸可视化:GIS融合之路(三)CesiumJS和ThreeJS相机同步 山海…

WPF项目使用日志

提问 WPF项目如何使用日志 回答引入nuget log4net加入配置特性[assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionarie…

编程入门(四)【计算机网络基础(由一根网线连接两个电脑开始)】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 前言两个电脑如何互连呢&#xff1f;集线器、交换机与路由器总结 前言 当你有…

实验一——椰子视频app原型设计

一、对比分析墨刀、Axure、Mockplus等原型设计工具的各自的适用领域及优缺点。 (1)Axure的优缺点 1.主要优点 Axure作为老牌的原型图工具,功能最齐全,交互最多样,基本任何想要的效果都可以实现,尤其在制作PC端原型图上有优势。 2.主要缺点 Axure缺点同样也相当明显,Axure的…

Docker操作容器打包(commit),压缩(save),挂载(load)

文章目录 前言一、容器打包二、将镜像压缩成tar包三、将tar包挂载为镜像结束 前言 将容器打包成镜像时&#xff0c;你正在将应用程序及其所有依赖项、文件和配置文件捆绑到一个可移植的、独立的单元中。这样做可以确保您的应用程序在不同环境中具有一致的运行方式&#xff0c;…

SQL server跨库链接服务器

SQL server进阶技能篇:SQL的跨库查询与链接服务器 - 知乎 (zhihu.com)各位小伙伴们,关于MSSQL的基本技能篇前面一共写了10篇,也基本上算是告一段落,接下来将开始介绍进阶技能篇。在构思这个进阶技能篇的时候,一直在考虑先写哪个,其实到看到这部分内容能理解的人,基本上对…

cesium 解决带高度的polygon 材质不能透明的问题

问题描述&#xff1a;创建一个带高度的polygon&#xff0c;用一个带透明度的图片做材质&#xff0c;画出来的多边形没有透明效果&#xff0c;图片的透明通道没有用上。 一、创建不带高度polygon 不带高度的polygon&#xff0c;使用带透明度的图片是有效果的&#xff0c;但是不…

8-01. 逻辑调整及补充内容

实现音量修改 修改 AudioManager修改 UIManager实现结束游戏 修改 EventHandler修改 UIManager修改 Player修改 NPCMovement修改 TimeManager修改 AudioManager修改 SaveLoadManager修改 TransitionManager解决游戏刚开始时NPC有移动的问题 修改 NPCMovement增加初始箱子解决箱…

年龄与疾病c++

题目描述 某医院想统计一下某项疾病的获得与否与年龄是否有关&#xff0c;需要对以前的诊断记录进行整理&#xff0c;按照0-18岁、19-35岁、36-60岁、61以上&#xff08;含61&#xff09;四个年龄段统计的患病人数以及占总患病人数的比例。 输入 共2行&#xff0c;第一行为过…

(学习)godot4.2 Android调试

1 勾选远程部署 2 安装 Android 构建模板 3 cmd 运行以下命令获取 debug.keystorekeytool -keyalg RSA -genkeypair -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 99…

NLP问答系统:使用 Deepset SQUAD 和 SQuAD v2 度量评估

目录 一、说明 二、Deepset SQUAD是个啥&#xff1f; 三、问答系统&#xff08;QA系统&#xff09;&#xff0c;QA系统在各行业的应用及基本原理 3.1 医疗 3.2 金融 3.3 顾客服务 3.4 教育 3.5 制造业 3.6 法律 3.7 媒体 3.8 政府 四、在不同行业使用QA系统的基本原理 五、关于…

visual studio连接ubuntu不成功原因(SSH问题)及解决办法

原因1&#xff1a; 网络没有互通&#xff08;一般VMware&#xff09; 使用ping来看网络是不是可以互通&#xff0c;例如&#xff1a; //这里的ip是ubuntu的ip&#xff0c;也可以从ubuntu的客户端ping一下当前主机 ping 192.168.1.101原因2&#xff1a; SSH没有密钥&#xf…

机器学习在安全领域的应用:从大数据中识别潜在安全威胁

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

爬虫 | 垃圾处理设施数据的获取与保存

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目通过发送网络请求&#xff08;requests&#xff09;&#xff0c;从指定的 URL 获取垃圾处理设施的相关数据&#xff0c;并将数据保存到 CSV 文件中&#xff0c;以供后续分析和利用。 目录 一、项目结构 二、详细说明 三…

[8] UE C++ Mario

创建了盒子 , 定义了盒子的碰撞位置能在if里面直接声明赋值局部变量 但不能赋值成员变量 friend关键词应用 导入类的时候如果是灰色就删掉 , 并且查看头文件

obsidian和typora图片兼容问题

1.打开obsidian的第三方插件-->插件市场 找到Custom Attachment Location下载(当然这个也是需要一点魔法的)2.配置obsidian的文件与链接3.配置obsidian的Custom Attachment Location插件4.typora的文件路径 配置一样即可最后对比,两个程序之间可以相互查看文件了

VS2015 查找结果明细没显示问题

今天使用VS2015查找数据的时候,只显示了统计结果,没显示明细 找了下解决方案,需要修改注册表配置 1.打开注册表以下路径 HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{73B7DC00-F498-4ABD-AB79-D07AFD52F395}\InProcServer32 2.将默认值修改为 TextMgrP.dll 文件所在路径 例如: C…

Python教学入门:函数

在 Python 中&#xff0c;def 关键字用于定义函数。函数是一段可重用的代码块&#xff0c;用于执行特定的任务或操作。通过定义函数&#xff0c;可以将一段代码封装起来&#xff0c;使其可以在程序中被多次调用&#xff0c;提高代码的复用性和可维护性。 下面是 def 函数定义的…

2024.4.13 Python 爬虫复习day01

目录 day01_HTTP协议HTML页面web服务器 各类名词解释 URL统一资源定位符 HTTP协议 HTML页面 知识点: 第一个页面 标题标签和图片标签 注册页面 登录页面 WEB服务器 安装fastapi和uvicorn 原始命令方式 镜像源命令方式 工具方式 快速搭建web服务器 知识点: 示例…

设计模式———单例模式

单例也就是只能有一个实例&#xff0c;即只创建一个实例对象&#xff0c;不能有多个。 可能会疑惑&#xff0c;那我写代码的时候注意点&#xff0c;只new一次不就得了。理论上是可以的&#xff0c;但在实际中很难实现&#xff0c;因为你无法预料到后面是否会脑抽一下~~因此我们…