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

主要代码

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.mfbz.cn/a/556046.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

传统零售行业如何做数字化转型?

传统零售行业的数字化转型是一个系统性的过程&#xff0c;涉及到企业的多个方面。以下是一些关键步骤和策略&#xff0c;帮助传统零售企业实现数字化转型&#xff1a; 1、明确转型目标和战略 首先&#xff0c;企业需要明确数字化转型的目标和战略。包括确定企业的核心竞争力、…

Java内存模型和 JVM 内存运行时

文章目录 前言一、什么是Java 的内存模型&#xff1f;二、什么是 JVM 的运行时数据区Java8 之前和之后的区别JVM 内存模型JVM 内存区域JVM 内存垃圾回收JVM如何判断哪些对象不在存活&#xff1f;JVM运行过程中如何判断哪些对象是垃圾&#xff1f; JVM 垃圾回收Java8 中的 jvm如…

.rmallox勒索病毒来袭:如何守护您的数据安全?

引言&#xff1a; 随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中勒索病毒更是成为了网络安全领域的一大难题。.rmallox勒索病毒作为一种典型的恶意软件&#xff0c;通过加密受害者文件并勒索赎金的方式&#xff0c;给企业和个人带来了巨大的经济损…

指纹浏览器如何高效帮助TikTok账号矩阵搭建?

TikTok的账号矩阵&#xff0c;可能听起来还比较陌生&#xff0c;但随着TikTok业务已经成为吃手可热的跨境业务&#xff0c;TikTok多账号矩阵已成为流行策略。但它有什么优点呢&#xff1f;操作多个帐户会导致被禁止吗&#xff1f;如何有效地建立账户矩阵开展业务&#xff1f;这…

爬虫 | 基于 requests 实现加密 POST 请求发送与身份验证

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目旨在实现一个简单的 Python 脚本&#xff0c;用于向指定的 URL 发送 POST 请求&#xff0c;并通过特定的加密算法生成请求头中的签名信息。这个脚本的背后是与某个特定的网络服务交互&#xff0c;发送特定格式的 JSON 数据…

深入理解MySQL中的UPDATE JOIN语句

在MySQL数据库中&#xff0c;UPDATE语句用于修改表中现有的记录。有时&#xff0c;我们需要根据另一个相关联表中的条件来更新表中的数据。这时就需要使用UPDATE JOIN语句。最近我们遇到了这样的需求&#xff1a;我们有一张历史记录表&#xff0c;其中一个字段记录了用,连接的多…

网络爬虫软件学习

1 什么是爬虫软件 爬虫软件&#xff0c;也称为网络爬虫或网络蜘蛛&#xff0c;是一种自动抓取万维网信息的程序或脚本。它基于一定的规则&#xff0c;自动地访问网页并抓取需要的信息。爬虫软件可以应用于大规模数据采集和分析&#xff0c;广泛应用于舆情监测、品牌竞争分析、…

在 Linux 终端中创建目录

目录 ⛳️推荐 前言 在 Linux 中创建一个新目录 创建多个新目录 创建多个嵌套的子目录 测试你的知识 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 前言 在本系列的这一部…

Day09 React———— 第九天

ReactRoter 一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候&#xff0c;path 对应的组件会在页面中进行渲染 基础用法 import { createBrowserRouter, RouterProvider } from "react-router-dom"; const router createBrowserRoute…

解决Mac使用Vscode无法调用外部终端

前言 今天遇到一个很奇怪的问题&#xff0c;之前好好的用Vscode还能调用外部终端&#xff0c;怎么今天不行了&#xff1f;问题出在哪里呢&#xff1f;请听我娓娓道来。 检查配置文件 我查看了一下配置文件&#xff0c;发现配置文件都是调用外部控制台&#xff0c;没毛病啊。 …

linux启动minicom、u-boot的常用命令、网络命令tftp、nfs/根文件系统、u-boot的bootargs环境变量

linux启动minicom sudo minicom -con进入minicom界面&#xff1a; 打开单片机 在打开之后&#xff0c;我们通过 printenv查看环境配置 在修改配置之前&#xff0c;我们最好先将环境初始化一下&#xff0c;初始化代码为 nand erase.chipu-boot的常用命令 尽管u-boot是一个…

Torch 模型 感受野可视化

前言&#xff1a;感受野是卷积神经网络 (CNN) 中一个重要的概念&#xff0c;它表示 CNN 每一层输出的特征图上的像素点在输入图像上映射的区域。感受野的大小和形状直接影响到网络对输入图像的感知范围和精度&#xff0c;进而调整网络结构、卷积核大小和步长等参数&#xff0c;…

后端-MySQL-week11 多表查询

tips: distinct————紧跟“select”之后&#xff0c;用于去重 多表查询 概述 一对多&#xff08;多对一&#xff09; 多对多 一对一 多表查询概述 分类 连接查询 内连接 外连接 自连接 必须起别名&#xff01; 联合查询-union&#xff0c;union all 子查询 概念 分类 …

OpenMesh 极小曲面(局部迭代法)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 我们的目标是想得到一个曲率处处为0的曲面,具体操作如下所述: 二、实现代码 #define _USE_MATH_DEFINES #include

量子时代加密安全与区块链应用的未来

量子时代加密安全与区块链应用的未来 现代密码学仍然是一门相对年轻的学科&#xff0c;但其历史却显示了一种重要的模式。大多数的发展都是基于几年甚至几十年前的研究。而这种缓慢的发展速度也是有原因的&#xff0c;就像药物和疫苗在进入市场之前需要经过多年的严格测试一样&…

【Web】2022DASCTF X SU 三月春季挑战赛 题解(全)

目录 ezpop calc upgdstore ezpop 瞪眼看链子 fin#__destruct -> what#__toString -> fin.run() -> crow#__invoke -> fin#__call -> mix.get_flag() exp <?php class crow {public $v1;public $v2;}class fin {public $f1; }class what {public $a; }…

MATLAB中gurobi 运行报错与调试

问题背景如下&#xff1a;刚拿到一份MATLAB的代码&#xff0c;但是电脑第一次安装gurobi&#xff0c;在运行过程中发生了报错&#xff0c;使用断点进行调试和步进调试方法&#xff0c;最终发现&#xff0c;这个问题出在了哪一步&#xff0c;然后向了人工智能和CSDN、百度寻求答…

VScode远程连接虚拟机提示: 无法建立连接:XHR failed.问题解决方案

一问题描述 在vscode下载插件Remote-SSH远程连接虚拟机时提示无法建立连接 二.最大嫌疑原因&#xff1a; 我也是在网上找了许久&#xff0c;发现就是网络原因&#xff0c;具体不知&#xff0c;明明访问别的网页没问题&#xff0c;就是连不上&#xff0c;然后发现下载vscode的…

数据赋能(61)——要求:数据管理部门职责

“要求&#xff1a;数据管理部门职责”是作为标准的参考内容编写的。 数据管理部门职责在于以数据资源为核心&#xff0c;将原始数据转化为可被业务部门与数据服务部门有效利用的数据资源&#xff0c;以支持业务赋能的实现。 数据管理要确保数据的完整性、准确性与一致性&…

Debian12 中重新安装MSSQL 并指定服务器、数据库、数据表字段的字符排序规则和默认语言等参数

在 Linux 上配置 SQL Server 设置 - SQL Server | Microsoft Learn 零、查看sql server 服务器支持的字符排序规则 SELECT Name from sys.fn_helpcollations() where name Like Chinese% go------ Chinese_PRC_CI_AI Chinese_PRC_CI_AI_WS Chinese_PRC_CI_AI_KS Chinese_PRC_…
最新文章