Linux系统编程,使用C语言实现简单的FTP(服务器/客户端)

前言

跟着上官社长 陈哥花了一个月的时间终于把Linux系统编程学的差不多了,这一个月真的是头疼啊,各种bug,调的真心心累,不过好在问题都解决掉了,在此也感谢一下答疑老师,给我提供了很多的思路,本文章是对前段时间学习Linux,做一个小小的总结,才疏学浅,只学到这个地步,下来继续努力,加油!。

一.基本功能

  1. get + xxx 从服务器获取某个文件到客户端。

  1. put + xxx 把客户端的某个文件上传到服务器。

  1. cd + 路径 切换目录

  1. lls 列出本地文件列表

  1. ls 列出服务端文件列表

  1. lcd +路径 切换本地目录

二.实现思路

1.使用socket先把服务器和客户端建立连接

2.建立连接后开始信息的交互

客户端输入指令服务器处理指令

三.具体代码如下

先建立socket连接,如下是服务器的代码。

int main(int argc,char**argv)
{
    if(argc !=3 )//判断参数是否输入正确
    {
        printf("input errror\n");    
        exit(-1);
    }
    int s_fd = socket(AF_INET,SOCK_STREAM,0);//创建网络套接字

    struct sockaddr_in addr;
    struct sockaddr_in addr2;
    
     memset(&addr,0,sizeof(struct sockaddr_in));
     memset(&addr2,0,sizeof(struct sockaddr_in));
    int n_read;
    addr.sin_family = AF_INET;//地址协议族
    addr.sin_prot = htons(atoi(argv[2]));//端口号,先使用atoi函数把参数转换成整型,在使用htons转为网络字节序。
    inet_aton(argv[1], &s_addr.sin_addr);//IP地址,使用inet_aton函数把点分十进制的IP地址,转换为网络字节序。例如,192.168.147.155.
    bind(s_fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));

    listen(s_fd,10);//监听

    int sz = sizeof(struct sockaddr_in);
    while(1)//循环卡住不让退出去。
    {
        int c_fd = accept(s_fd,(struct sockaddr *)&addr2,&sz);//尝试连接
        if(c_fd == -1)
        {
            perror("accept");
            exit(-1);    
        }
        printf("get connect : %s\n",inet_nota(addr2.sin_addr));//连接成功打印对方的IP地址。
      
    }
        return 0;
    }

客服端代码,如下

int main(int argc,char **argv)
{
    int c_fd;
    struct Msg msg;

    struct sockaddr_in c_addr;
    memset(&c_addr,0,sizeof(struct sockaddr));

    // 1. socket
    c_fd = socket(AF_INET,SOCK_STREAM,0);
    if(c_fd==-1)
    {
        perror("socket:");
        exit(-1);
    }
    c_addr.sin_family = AF_INET;
    c_addr.sin_port =htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);
    connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr_in));//请求连接
    printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//连接成功打印IP
    while(1)
    {
    }
    return 0;
}

上面只是服务器和客户端,建立连接的代码,还不携带参数,下面开始客户端和服务器进行通讯。

四.服务器/客户端通讯

1.服务器和客户端共有的文件

#include<stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#include <fcntl.h>

#define LS   0
#define PWD  1
#define GET  2
#define IFGO 3
#define CD   4
#define PUT  5
#define LLS  6
#define LCD  7
#define LPWD 8
#define QUIT 9
#define DOFILE 10

struct Msg
{
        int type;
        char data[1024];
        char buf[128];
};

2.客户端


int get_cmd_type(char *cmd)
{
     //比较输入的指令,找到对应的就返回相对应的指令。
     if(!strcmp("ls",cmd))      return LS;
     if(!strcmp("lls",cmd))     return LLS;
     if(!strcmp("pwd",cmd))     return PWD;
     if(!strcmp("quit",cmd))    return QUIE;
      //在输入的指令字符串里面找,对应的指令
      if(strstr(cmd."cd"))       return CD;
      if(strstr(cmd,"get"))      return GET;
      if(strstr(cmd,"put"))      return PUT;

       return -1;//未找到返回错误
}




char *getdir(char *cms)//分割指令,来获取参数
{
    char *p = NULL;
     p = strtok(cms," ");
     p = strtok(NULL," ");
    return p;
}



int cmd_handler(struct Msg msg,int fd)
{
    int ret;
    char buf[100];//临时空间
    char *dir = NULL;
    int filefd; //文件表示符
    ret = get_cmd_type(msg.data);//输入的指令转换成整数
    switch(ret)//根据ret的值来选择
    {
        case PWD:
        case LS:
        case CD:
                msg.type = 0;//标记符
                write(fd,msg.data,sizeof(msg));//发送命令
                break;
       
        case GET:
                msg.type = 2;
                write(fd,msg.data,sizeof(msg));//发送命令
                break;
        case PUT:
                strcpy(buf,msg.data);
                dir = getdir(buf);
                if(access(dir,F_OK) == -1)//判断参数文件是否存在
                 {    
                        printf("%s not exsit\n",dir);
                  }
                  else//如果存在
                  {
                        filefd = open(dir,O_RDWR);//打开这个文件
                        read(filefd,msg.buf,sizeof(msg.buf));//读取这个文件
                        close(filefd);//关闭这个文件
        
                        write(fd,msg,data,sizeof(msg));//向服务器发送命令
                   }
                
         case QUIT:
                   strcpy(msg.data,"quit");
                   write(fd,msg.data,sizeof(msg));
                   close(fd);//关闭文件
                   exit(-1);//退出程序
         case LLS:
                   system("ls");
                   break;
         case LCD:
                    dir = getdir(msg.data);
                    chdir(dir);
                    break;
    }
    return ret;
}


void hand_sever_message(int c_fd,struct Msg msg)
{
    struct Msg msgget;
    char *dir = NULL;
    int n_read;
    n_read = read(c_fd,&msgget,sizeof(msgget));//客户端执行到这里时,会阻塞在这,等待服务器写入数据
    if(n_read == -1)
    {
        printf("server out \n");
        exit(-1);    
    }
    else if(msg.type == DOFILE)
     {
        dir = getdir(msg.data);
        int filefd = open(dir,O_RDWR|O_CREAT,0666);
         write(filefd,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;
    int ret;
    struct Msg msg;//定义结构体
    struct sockaddr_in c_addr;
    memset(&c_addr,0,sizeof(struct sockaddr));//初始化结构体里的内容

    
    c_fd = socket(AF_INET,SOCK_STREAM,0);//创建socket
    if(c_fd==-1)
    {
        perror("socket:");
        exit(-1);
    }
    c_addr.sin_family = AF_INET;
    c_addr.sin_port =htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);

    connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr_in));//请求连接
    printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//连接成功打印IP
    while(1)
    {
        memset(msg.data,0,sizeof(msg.data));
        scanf("%[\n]",msg.data);//获取客户端指令
        if(strlen(msg.data) == 0)
        {
            if(mark == 1)
            {
                printf(">");
            }
        }
        ret = cmd_handler(msg,c_fd);//封装一个函数,处理指令
        
        if(ret >IFGO)
        {
            putchar('>');
            fflush(stdout);
            continue;
        }
        if(ret == -1)
        {
            printf("commend out\n");
            printf(">");
            fflush(stdout);
        }
        handler_sever_message(c_fd,msg);//处理客户端返回内容 
    }
    close(c_fd);//关闭文件
    return 0;
}

3.服务器


int get_cmd_type(char *cmd)//把指令转换成整型
{
    if(!strcmp("ls",cmd))  return LS;
    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;
    return -1;
}

char *getdir(char cmd)//分割指令,获取第二个参数
{
    char *p = NULL;
    p = strtok(cmd," ");
    p = strtok(NULL," ");
    return p;
}


void msg_handler(struct Msg msg,int fd)
{
    int ret;
     char *dir;
    char *databuf[1024] = {0};//临时空间
    int fdfile;//文件描述符
    ret = get_cmd_type(msg.data);

    switch(ret)
            case LS:
            case PWD:
                    msg.type = 0
                    FILE *r = popen(msg.data,"r");执行该参数,并读取,返回读取文件的指针
                    fread(msg.data,sizeof(msg.data),1,r);读取到data空间里面,读取一次,读取的文件是上面r指向的文件。
                    write(fd,msg.data,sizeof(msg));读取完成后写入。
                    break;
            case CD:
                   dir = getdir(msg.data);//先把指令分割出来,获取参数
                    printf("dir :%s\n",dir);//打印参数
                    chdir(dir); 将当前目录改向参数指向的目录。
                    break;
            case GET:
                    dir = getdir(msg.data);//获取参数文件
                    if(access(dir,F_OK)== -1)//判断文件存在吗
                     {
                            strcpy(msg.data,"NO this file");
                      }
                      else//如果存在
                       {
                            msg.type = DOFILE; //标记
                            fdfile = open(dir,O_RDWR);//打开此文件
                            read(fdfile,databuf,sizeof(databuf));//读取此文件到临时空间databuf里面
                            close(fdfile);//读取完成,关闭此文件
                            strcpy(msg.data,databuf);//将databuf拷贝
                            write(fd,msg.data,sizeof(msg));//写入
                       }
                    break;

            case PUT:
                    dir = getdir(msg.data);//分割获取第二个参数
                   fdfile =  open(dir,O_RDWR|O_CREAT,0666);//打开该参数的文件
                    write(fd,msg.buf,strlen(msg.buf));//将客户端读取的内容写入服务器
                    close(fdfile);//写完关闭文件
                    break;
            case QUIT:
                    printf("client quit\n");//打印退出信心
                    exit(-1);//退出程序

}
int main(int argc,char**argv)
{
    if(argc !=3 )//判断参数是否输入正确
    {
        printf("input errror\n");    
        exit(-1);
    }
    int s_fd = socket(AF_INET,SOCK_STREAM,0);//创建网络套接字
    struct Msg msg;
    struct sockaddr_in addr;
    struct sockaddr_in addr2;
    
     memset(&addr,0,sizeof(struct sockaddr_in));
     memset(&addr2,0,sizeof(struct sockaddr_in));
    int n_read;
    addr.sin_family = AF_INET;//地址协议族
    addr.sin_prot = htons(atoi(argv[2]));//端口号,先使用atoi函数把参数转换成整型,在使用htons转为网络字节序。
    inet_aton(argv[1], &s_addr.sin_addr);//IP地址,使用inet_aton函数把点分十进制的IP地址,转换为网络字节序。例如,192.168.147.155.
    bind(s_fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));

    listen(s_fd,10);//监听

    int sz = sizeof(struct sockaddr_in);
    while(1)//循环卡住不让退出去。
    {
        int c_fd = accept(s_fd,(struct sockaddr *)&addr2,&sz);//尝试连接
        if(c_fd == -1)
        {
            perror("accept");
            exit(-1);    
        }
        printf("get connect : %s\n",inet_nota(addr2.sin_addr));//连接成功打印对方的IP地址。
        if(fork()== 0)//创建子进程来对接,进行通讯
        {
                memset(msg.data,0,sizeof(msg.date));
                n_read =read(c_fd,&msg,sizeof(msg));//读取客户端发过来的消息
                if(n_read == 0)//判断读取信息
                {
                    printf("client out\n");
                    break;
                }else (if n_read>0)//如果读取到了
                {
                msg_hanfler(msg,c_fd);//处理函数
                }
        }
      
    }
        return 0;
}

以上就是我对这段时间学习Linux的理解,希望可以帮助到大家谢谢

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明 基于C语言 C++开发的系统项目 课程设计 毕业设计 供参考 源代码+说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

走下去-别回头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值