模仿tftp 服务器 客户端

模仿tftp上传、下载文件,
约定格式:
上传 put filename# //中间空格、末尾#号表示完成输入
下载 get filename# //同上,
停止 quit
参考:https://blog.csdn.net/qq_65687767/article/details/126830833
TFTP就是一个传输文件的简单协议,没有FTP功能那么齐全

客户端代码
执行时 ./client ip地址 port

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

void parsestr( char * str1,char cmd[],char filename[]);
int tcp_connect(const char *server_ip,int server_port);
void upload(int sockfd,char *filename);
void download(int sockfd,char *filename);
void Datahandler(int confd,char *cmd,char *filename);

int main(int argc, const char *argv[])
{
	char cmdbuf[1024] = {0};
    char cmd[100] = {0};
    char filename[100] = {0};
    int con_fd = 0;
//输入参数个数判断
    if(argc != 3)
    {
        perror("输入错误,请输入ip地址和端口号");
        return -1;
    }
//链接服务器,返回后的socket对象
    con_fd = tcp_connect(argv[1],atoi(argv[2])); 
    if(con_fd > 0)
    {
        printf("server connected\n");
    }
    else
    {
        printf("server connect failed\n");
    }
    while(1)
    {
        printf("请输入如下操作命令:\n");
        printf("下载:  get 文件名#\n");
        printf("上传:  put 文件名#\n");
        printf("停止:  quit      \n");
        memset(cmdbuf,0,sizeof(cmdbuf));
        fgets(cmdbuf,sizeof(cmdbuf),stdin);//键盘输入操作命令
        int num = send(con_fd,cmdbuf,strlen(cmdbuf),0);//发送命令到服务器
        if(num == -1)
        {
            perror("send error:");
            exit(-1);
        }
        if(strncmp(cmdbuf,"quit",4) == 0)
            exit(-1);
        parsestr(cmdbuf,cmd,filename);//解析输入字符串
        Datahandler(con_fd,cmd,filename);//处理数据
    } 
    close(con_fd);
    return 0;
}
//3、处理数据函数
void Datahandler(int confd,char *cmd,char *filename)
{
    if(strncmp(cmd,"get",3) == 0)
    {
        //下载文件
        download(confd,filename);
    }
    else if(strncmp(cmd,"put",3) == 0)
    {
        /* 上传文件 */
        upload(confd,filename);
    }
    else
    {
        printf("cmd is error\n");
    }
}

//3.1 下载文件
void download(int sockfd,char *filename)
{
    char text[1024] = {0};
    int bytes = 0;
    int total = 0;
    struct stat1
    {
        int errnum;
        struct stat filestat;
    };
    struct stat1 filestat1;
    memset(&filestat1,0,sizeof(filestat1));
    recv(sockfd,&filestat1,sizeof(filestat1),0);
    if(filestat1.errnum == 1)
    {
        printf("\n");
        printf("************************\n");
        printf("%s文件不存在服务器中.........\n",filename);
        printf("************************\n");
        printf("\n");
        return;
    }
    int fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,777);
    if(fd < 0)
    {
        perror("open error");
        return;
    }
    while(1)
    {
        if(total >= filestat1.filestat.st_size)
        {
            printf("大小为 %ldkb 的 %s 文件下载成功!\n",filestat1.filestat.st_size,filename);
            close(fd);
            break;
        }
        bytes = recv(sockfd,text,sizeof(text),0);
        write(fd,text,bytes);
        total += bytes;
    }
}
//3.2 上传文件
void upload(int sockfd,char *filename)
{
    struct stat1
    {
        int errnum;
        struct stat filestat;
    };
    struct  stat1 filestat1;
    char text[1024] = {0};
    int bytes = 0;
    memset(&filestat1,0,sizeof(filestat1));
    errno = 0;
    if(stat(filename,&filestat1.filestat) < 0)
    {
        if(errno == ENOENT)
        {
            filestat1.errnum = 1;
            send(sockfd,&(filestat1),sizeof(filestat1),0);//发送文件大小
            printf("error:该文件不存在此目录。\n");
            return;
        }
        else
        {
            perror("stat file error:");
            close(sockfd);
            exit(-1);
        }
    }
    else
    {
        send(sockfd,&(filestat1),sizeof(filestat1),0);//发送文件大小
    }
    int fd = open(filename,O_RDONLY);
    if(fd < 0)
    {
        perror("open file error");
        exit(-1);
    }
    while(1)
    {
        memset(text,0,sizeof(text));
        bytes = read(fd,text,sizeof(text));
        if(0 == bytes)
        {
            printf("大小为 %ldkb 的 %s 文件上传成功\n",filestat1.filestat.st_size,filename);
            close(fd);
            break;
        }
        send(sockfd,text,bytes,0);
    }
}
//1、请求链接服务器
int tcp_connect(const char *server_ip,int server_port)
{
    char *serverip;
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd == -1)
    {
        perror("socket error");
        return -1;
    }
    struct sockaddr_in server_para;
    memset(&server_para,0,sizeof(server_para));
    server_para.sin_family = AF_INET;
    server_para.sin_addr.s_addr = inet_addr(server_ip);
    server_para.sin_port = htons(server_port);

    if((connect(sock_fd,(struct sockaddr *)&server_para,sizeof(server_para))) < 0)
    {
        perror("connect errror");
        return -1;
    }
    
    return sock_fd;
}
//2、解析输入字符串
void parsestr(char * str1,char cmd[],char filename[])
{
    
    memset(filename,0,sizeof(filename));
    memset(cmd,0,sizeof(cmd));
    int i = 0;
    while(str1[i] == ' ') //跳过cmd前面多余的空格
    {
        i++;
        continue;
    }
    int k = 0;
    while(str1[i+k] != ' ')
    {
        cmd[k] = str1[i+k]; 
        k++;
    }
    cmd[k] = '\0';
    k++;//把空格跳过
    while(str1[i+k] == ' ') //跳过cmd后面多余的空格
    {
        k++;
        continue;
    }
    int j = 0;
    while(str1[i+k+j] != '#')
    {
        filename[j] = str1[i+k+j];
        j++;
    }
    filename[i+k+j] = '\0';
}

服务器代码
执行时 ./server ip地址 port

#include<stdio.h>
#include <sys/socket.h>
#include <errno.h>
#include<netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>

int tcp_tocket(char *server_ip,int port1);
void parsestr(char cmdchar[],char cmd[],char filename[]);
void upload(int newfd,char *filename);
void download(int newfd, char *filename);
void Datahandler(int newfd,char *cmd,char *filename);

int main(int argc,char *argv[])
{
    char cmdchar[1024];
    char cmd[20] = {0};
    char filename[500] = {0};
    int newfd = 0;
    int byte = 0;

    newfd = tcp_tocket(argv[1],atoi(argv[2]));
    if(newfd > 0)
        printf("client connect on\n"); 
    else
        printf("client connect failed\n"); 
    while(1)
    {
        memset(cmdchar,0,sizeof(cmdchar));
        byte = recv(newfd,cmdchar,sizeof(cmdchar),0);
        if(byte < 0)
        {
            perror("recv error:");
            exit(-1);
        }
        else if(byte == 0)
        {
            printf("client connect off\n");
            exit(-1);
        }
        if(strncmp(cmdchar,"quit",4) == 0)
        exit(-1);
        parsestr(cmdchar,cmd,filename);  // 接受字符解析
        Datahandler(newfd,cmd,filename);
    }
    close(newfd);
    return 0;
}
void Datahandler(int newfd,char *cmd,char *filename)
{
    if(0 == strncmp(cmd,"get",3))
    download(newfd,filename);
    else if(0 == strncmp(cmd,"put",3))
    upload(newfd,filename);
    else
    {
        printf("cmd error\n");
        // close(newfd);
        // exit(-1);
        return;
    }
}
void download(int newfd, char *filename) //客户端请求下载文件
{
    struct stat1
    {
        int errnum;
        struct stat filestat;
    };
    struct stat1 filestat1;
    char text[1024];
    int num = 0;
    memset(&filestat1,0,sizeof(filestat1));
    errno  = 0;
    if(stat(filename,&filestat1.filestat) < 0)
    {
        if(errno == ENOENT)
        {
            filestat1.errnum = 1;
            send(newfd,&filestat1,sizeof(filestat1),0);
            printf("客户端需求的文件不存在,等待其重新发送\n");//主要发送文件大小
            return;
        }
        else
        {
            perror("stat file error:");
            close(newfd);
            exit(-1);
        }
    }
    else
    {
        send(newfd,&filestat1,sizeof(filestat1),0);//主要发送文件大小
    }
    int fd = open(filename,O_RDONLY);
    if(fd < 0)
    {
        perror("open file error");
        exit(-1);
    }
    while (1)
    {
        memset(text,0,sizeof(text));
        num = read(fd,text,sizeof(text));
        if(num == 0)
        {
            printf("大小为%ld 的 %s 文件 发送完毕!!\n",filestat1.filestat.st_size,filename);
            close(fd);
            break;
        }
        send(newfd,text,num,0);
    }
}
void upload(int newfd,char *filename)  //客户端请求上传文件
{
    struct stat1
    {
        int errnum;
        struct stat filestat;
    };
    struct stat1 filestat1;
    char text[1024] = {0};
    int total = 0;
    int bytes = 0;
    recv(newfd,&filestat1,sizeof(filestat1),0);  //文件大小
    if(filestat1.errnum == 1)
        return;
    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,777);
    if(fd < 0)
    {
        perror("open file error");
        exit(-1);
    }
    while(1)
    {
        memset(&text,0,sizeof(text));
        bytes = recv(newfd,text,sizeof(text),0);
        total += bytes;
        if(total >= filestat1.filestat.st_size)
        {
            write(fd,text,bytes);
            printf("大小为 %ldkb 的 %s 文件 接受完毕\n",filestat1.filestat.st_size,filename);
            close(fd);
            break; 
        }
        write(fd,text,bytes);
    }
}
void parsestr(char cmdchar[],char cmd[],char filename[])
{
    memset(cmd,0,sizeof(cmd));
    memset(filename,0,sizeof(filename));
    int i = 0;
    while(cmdchar[i] == ' ')  //跳过cmd前面多余的空格
    {
        i++;
        continue;
    }
    int k = 0;
    while (cmdchar[i+k] != ' ')
    {
        cmd[k] = cmdchar[i+k];
        k++;
    }
    if(cmdchar[i+k] == ' ')
        cmd[k] = '\0';
    k++;
    while(cmdchar[i+k] == ' ')  //跳过cmd后面多余的空格
    {
        k++;
        continue;
    }
    int j = 0;
    while (cmdchar[i+k+j] != '#')
    {
        filename[j] = cmdchar[i+k+j];
        j++;
    }
        filename[i+k+j] = '\0';
}
//tcp连接

int tcp_tocket(char *server_ip,int port1)
{
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd < 0)
    {
        perror("connect error");
        return -1;
    }
    struct sockaddr_in sockaddrin;

    memset(&sockaddrin,0,sizeof(sockaddrin));
    sockaddrin.sin_family = AF_INET;
    sockaddrin.sin_addr.s_addr = inet_addr(server_ip);
    sockaddrin.sin_port = htons(port1);

    int isockoptval = 1;
    if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&isockoptval,sizeof(isockoptval)) == -1)
    {  //解决重新连接服务器时出现bind:address already in use问题
        perror("setsockopt fail");
        close(sock_fd);
        // exit(-1);
    }
    if(bind(sock_fd, (struct sockaddr*)&sockaddrin, sizeof(sockaddrin)) < 0)
    {
        perror("bind failed");
        return -1;
    }
    if(listen(sock_fd,10) < 0)
    {
        perror("listen failed");
        return -1;
    }
    int accept_fd = accept(sock_fd, NULL,NULL);
    return accept_fd;
}

以上程序均在ubuntu系统中调试成功

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值