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

news2024/9/24 21:20:29

主要代码

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 9


struct  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通道的 fd
        break;
    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));         向新创建的文件中写入数据,secondBuf
        close(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 --默认 TCP
    s_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (s_fd == -1)
    {
        perror("socket");
        exit(-1);
    }
    // 初始化bind需要的struct

    s_addr.sin_family = AF_INET;
    // htons -- h - host to  ns-net short
    s_addr.sin_port = htons(atoi(argv[2])); // 通过传参指定端口
    // a -- ASCLL  传参方式得到IP
    inet_aton(argv[1], &s_addr.sin_addr);

    // 3.bind
    bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
    // listen
    listen(s_fd, 10);
    // 4.accept
    while (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 --默认 TCP
    c_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (c_fd == -1)
    {
        perror("socket");
        exit(-1);
    }
    // 初始化bind需要的struct

    c_addr.sin_family = AF_INET;
    // htons -- h - host to  ns-net short
    c_addr.sin_port = htons(atoi(argv[2]));
    // a -- ASCLL
    inet_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.coloradmin.cn/o/1615981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

231 基于matlab的北斗信号数据解析

基于matlab的北斗信号数据解析&#xff0c;多通道和单通道接收到的北斗信号数据&#xff0c;利用接收到的北斗数据&#xff08;.dat .txt文件&#xff09;&#xff0c;进行解析&#xff0c;得到初始伪距&#xff0c;平滑伪距&#xff0c;载波相位&#xff0c;并计算其标准差&am…

洛谷 -P1007 独木桥(模拟,思维)

独木桥 题目背景 战争已经进入到紧要时间。你是运输小队长&#xff0c;正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激&#xff0c;于是命令你的士兵们到前方的一座独木桥上欣赏风景&#xff0c;而你留在桥下欣赏士兵们。士兵们十分愤怒&#xf…

双线性插值计算手动实现以及原理

双线性插值计算手动实现以及原理 代码原理 代码 先贴代码吧&#xff0c;原理其实也比较简单&#xff0c;看代码基本也就理解了&#xff0c;时间太晚了&#xff0c;原理后续再补吧。 import torch from torch.nn import functional as F import numpy as np from itertools im…

苍穹外卖开发笔记(6.缓存商品,购物车)

目录 一、缓存商品2、缓存菜品&#xff08;redis&#xff09;1.问题说明2.实现思路3.代码开发 2、缓存套餐&#xff08;spring cache&#xff09;1.实现思路2.代码实现 3、测试 二、购物车功能1、添加购物车1.需求分析设计2.代码开发3.测试 2、查看购物车1.需求分析设计2.代码开…

基于TSM模块的打架斗殴识别技术

目 录 1 引言.... 4 1.1 研究背景与意义.... 4 1.2 研究现状综述.... 5 1.3 研究内容.... 6 1.3.1 图像预处理的优化.... 6 1.3.2 TSM模块的应用.... 6 1.3.3 视频分类的设计与实现.... 6 2 关键技术与方法.... 8 2.1 TSM算法与模型选择.... 8 2.1.1 TSM算法原理.... 8 2.1.2 …

用python做傅里叶变换和系统辨识

一、原始信号 1、理想数据 &#xff08;1&#xff09;系统参数 参数类型数值J0.5 k g ∗ m 2 kg*m^2 kg∗m2K0.2b5 &#xff08;2&#xff09;激励曲线 import matplotlib.pyplot as plt import numpy as np# 生成数据 x np.linspace(0, 10, 1000) # 生成0到10之间的100…

下列程序定义了NxN的二维数组,并在主函数中自动赋值。请编写函数fun(int a[][N],int n),该函数的功能是:使数组右上半三角元素中的值乘以m。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 下列…

从0到1:社区论坛小程序开发笔记

背景 论坛小程序&#xff1a;为用户提供了一个社交互动的平台&#xff0c;使用户可以分享经验、交流观点、解决问题&#xff0c;促进社区成员之间的互动和交流。 用户可以在论坛小程序上发布有关各种话题的帖子&#xff0c;分享自己的知识、经验和见解&#xff0c;帮助其他用户…

mysql基础14——视图

视图 视图是一种虚拟表 可以把一段查询语句作为视图存储在数据库中 需要的时候把视图看作一个表&#xff0c;对里面的数据进行查询 视图并没有真正存储数据 避免了数据存储过程中可能产生的冗余 提高了存储的效率 子查询 嵌套在另一个查询中的查询 派生表 如果在查询中…

【MySQL 数据宝典】【内存结构】- 003 Change Buffer 详解

一、 Change Buffer基本概念 Change Buffer&#xff1a;写缓冲区,是针对二级索引(辅助索引) 页的更新优化措施。 作用: 在进行DML操作时&#xff0c;如果请求的是 辅助索引&#xff08;非唯一键索引&#xff09;没有在缓冲池 中时&#xff0c;并不会立刻将磁盘页加载到缓冲池…

游戏AI智能体模仿学习技术方案揭秘(二)(附方案详情),沉浸式玩家体验秘诀,看《梦三国2》游戏AI智能体!

接上篇内容&#xff0c;小智发现内容非常受游戏开发者们的欢迎&#xff0c;今天给大家带来方案(二&#xff09;内容&#xff0c;没看过第一篇的伙伴可以戳以下链接查看~~码住&#xff01; 游戏AI智能体模仿学习技术方案&#xff08;附方案详情&#xff09;&#xff0c;沉浸式玩…

AQS(AbstractQueuedSynchronizer)队列同步器源码解读

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. AOS、AQS、AQLS的区别 3. AQS的底层原理 3.1. 核心思想 3.2. 数…

PyQt介绍——动画使用详解之QPropertyAnimation

一、继承关系 PyQt5的动画框架是QAbstractAnimation&#xff0c;它是一个抽象类&#xff0c;不能直接使用&#xff0c;需要使用它的子类。它的类结构如下&#xff1a; QAbstractAnimation&#xff1a;抽象动画&#xff0c;是所有动画的基类&#xff0c;不能直接使用。 QVariant…

基于postCSS手写postcss-px-to-vewiport插件实现移动端适配

&#x1f31f;前言 目前前端实现移动端适配方案千千万&#xff0c;眼花缭乱各有有缺&#xff0c;但目前来说postcss-px-to-vewiport是一种非常合适的实现方案&#xff0c;postcss-px-to-vewiport是一个基于postCss开发的插件&#xff0c;其原理就是将项目中的px单位转换为vw(视…

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

配置nodejs的俩小脚本

介绍&#xff1a;共两个脚本。 脚本1&#xff0c;用来配置环境变量&#xff0c;生成环境变量所需的配置信息&#xff0c;然后自己添加到系统环境变量里去 特别注意&#xff1a;该脚本需要放到nodejs目录下面&#xff0c;如果不是&#xff0c;则无法生成环境变量配置文本内容 另…

【STL概念】

STL STL&#xff08;Standard Template Library),即标准模板库从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。STL的目的是标准化组件&#xff0…

找不到msvcp140dll,无法继续执行代码的详细解决方法

在我们日常使用计算机进行各类工作任务的过程中&#xff0c;时常会遭遇一些突发的技术问题。比如&#xff0c;有时在运行某个重要程序或应用软件时&#xff0c;系统会突然弹出一个令人困扰的错误提示&#xff1a;“电脑提示找不到msvcp140.dll文件&#xff0c;因此无法继续执行…

Linux CPU 占用率 100% 排查

其他层面要考虑到的地方 mysql&#xff0c;有执行时间特别长的sql、死锁redis雪崩等相关问题并发导出数据量大Java定时器服务业务复杂&#xff0c;比如像每天要更新电商的统计表&#xff0c;每天发送优惠券等业务需要提前计算才能保证业务使用时的流畅性&#xff0c;我这个原因…

【快速上手ESP32(基于ESP-IDFVSCode)】09-Flash存储

ESP32中的Flash 关于ESP32中的Flash&#xff0c;我们需要再回顾一下命名规则。 我用的是立创开发板设计的板子&#xff0c;芯片型号是ESP32S3R8N8&#xff0c;因此可以知道我这块板子有8MB的Flash&#xff0c;大家可以参照着命名规则看看自己有多大的Flash容量。 操作Flash …