项目——服务器与客户端

头文件

#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/select.h>
#include <poll.h>
#include <errno.h>
#define PRINT_ERR(msg) \
        do{\
        fprintf(stderr,"line:%d ",__LINE__);\
        perror(msg);\
        return -1;\
    } while(0)
#define IF(i) if (argc != i) {\
        printf("input error,try again\n");\
        printf("usage: ./a.out srcfile destfile\n");\
        return -1;\
}
#endif

服务器

#include <head.h>
#define PORT 8888
#define IP "192.168.31.159"

struct td
{
    int newfd;
    struct sockaddr_in cin_t;
} td_t;

void *callback(void *arg);
int do_download(int sfd);
int Upload(int sfd);
int View_Directory(int sfd);
int Login(int sfd);
int Register(int sfd);

int main(int argc, const char *argv[])
{
    //创建流式套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0)
        PRINT_ERR("socket");

    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
        PRINT_ERR("setsockopt");

    //填充地址信息的结构体,将IP和端口号绑定到套接字上
    struct sockaddr_in sin_t;
    sin_t.sin_family = AF_INET;
    sin_t.sin_port = htons(PORT);
    sin_t.sin_addr.s_addr = inet_addr(IP);
    //绑定服务器的ip和端口
    if (bind(sfd, (struct sockaddr *)&sin_t, sizeof(sin_t)) < 0)
        PRINT_ERR("bind");

    //将套接字设置为监听状态
    if (listen(sfd, 128) < 0)
        PRINT_ERR("listen");
    printf("服务器监听成功\n");
    socklen_t addrlen = sizeof(td_t.cin_t);
    pthread_t tid;
    while (1)
    {
        td_t.newfd = accept(sfd, (struct sockaddr *)&td_t.cin_t, &addrlen);
        if (td_t.newfd < 0)
            PRINT_ERR("accept");
        printf("[%s:%d] newfd:%d 成功连接\n", inet_ntoa(td_t.cin_t.sin_addr), ntohs(td_t.cin_t.sin_port), td_t.newfd);
        pthread_create(&tid, NULL, callback, (void *)&td_t);
        pthread_detach(tid);
    }
    close(sfd);

    return 0;
}

void *callback(void *arg)
{
    struct td *p = (struct td *)arg;
    int newfd = p->newfd;
    struct sockaddr_in cin_t = p->cin_t;
    ssize_t res;
    char buf[2];
    while (1)
    {
        bzero(buf, sizeof(buf));
        //先只读取前两个字节判断其功能,文件名在功能函数读取
        res = recv(newfd, buf, 2, 0);
        if (res == 0)
        {
            printf("客户端断开,关闭newfd:%d\n", newfd);
            close(newfd);
            return NULL;
        }
        switch (buf[1])
        {
        case 1:
            printf("[%s:%d] newfd:%d请求下载", inet_ntoa(cin_t.sin_addr),
                   ntohs(cin_t.sin_port), newfd);
            do_download(newfd);
            break;
        case 2:
            printf("[%s:%d] newfd:%d请求上传", inet_ntoa(cin_t.sin_addr),
                   ntohs(cin_t.sin_port), newfd);
            Upload(newfd);
            break;
        case 3:
            printf("[%s:%d] newfd:%d请求查看目录\n", inet_ntoa(cin_t.sin_addr),
                   ntohs(cin_t.sin_port), newfd);
            View_Directory(newfd);
            break;
        case 4:
            printf("[%s:%d] newfd:%d请求登录\n", inet_ntoa(cin_t.sin_addr),
                   ntohs(cin_t.sin_port), newfd);
            Login(newfd);
            break;
        case 5:
            printf("[%s:%d] newfd:%d请求注册\n", inet_ntoa(cin_t.sin_addr),
                   ntohs(cin_t.sin_port), newfd);
            Register(newfd);
            break;
        default:
            break;
        }
    }
}
//客户端下载
int do_download(int newfd)
{
    char buf[514];
    ssize_t res;
    //读取文件名
    res = recv(newfd, buf, 20, 0);
    if (res < 0)
        PRINT_ERR("recv");
    puts(buf);
    //打开文件,失败则向客户端发送0,表示出错
    int fd = open(buf, O_RDONLY);
    if (fd < 0)
    {
        printf("发送失败,无该文件\n");
        *(unsigned short *)buf = htons(0);
        if (send(newfd, buf, 2, 0) < 0)
            PRINT_ERR("send");
        return -1;
    }
    int num = 1;
    int size = 0;
    while (1)
    {
        //封装数据包,前两个字节表示编号,后面是数据
        bzero(buf, sizeof(buf));
        *(unsigned short *)buf = htons(num++);
        res = read(fd, buf + 2, 512);
        if (res < 0)
            PRINT_ERR("read");
        if (res == 0)
        {
            printf("发送完成,大小为%d\n", size);
            break;
        }
        if (send(newfd, buf, res + 2, 0) < 0)
            PRINT_ERR("send");
        size += res;
        //等待客户端读取完上一个数据包然后在给客户端发送下一个
        res = recv(newfd, buf, 1, 0);
        if (res < 0)
            PRINT_ERR("recv");
    }
}
//客户端上传
int Upload(int sfd)
{
    char filename[20];
    ssize_t res;
    char buf[514];
    //读取文件名
    res = recv(sfd, filename, sizeof(filename), 0);
    if (res < 0)
        PRINT_ERR("recv");
    puts(filename);
    int size = 0;
    int fd, num = 1;
    while (1)
    {
        //给客户端发送我需要的数据包编号
        *(unsigned short *)buf = htons(num);
        if (send(sfd, buf, 2, 0) < 0)
            PRINT_ERR("send");
        bzero(buf, sizeof(buf));
        //读取数据
        res = recv(sfd, buf, sizeof(buf), 0);
        if (res < 0)
            PRINT_ERR("recv");
        //判断客户端是否发送正确的数据
        if (htons(num) == *(unsigned short *)buf)
        {
            num++;
            //如果是第一次接收数据则创建文件
            if (size == 0)
            {
                fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0777);
                if (fd < 0)
                    PRINT_ERR("open");
            }
            //向文件里写数据并计数
            if (write(fd, buf + 2, res - 2) < 0)
                PRINT_ERR("write");
            size += (res - 2);
            if (res < sizeof(buf))
            {
                printf("接收完成,大小为%d\n", size);
                close(fd);
                break;
            }
        }
        else
        {
            printf("客户端无该文件\n");
            break;
        }
    }
    return 0;
}
//客户端查看
int View_Directory(int sfd)
{
    char buf[300];
    DIR *dir;
    struct dirent *dt;
    struct stat st;
    if ((dir = opendir("./")) == NULL)
        PRINT_ERR("open dir error");
    while ((dt = readdir(dir)) != NULL)
    {
        //是隐藏文件则忽略
        if (dt->d_name[0] == '.')
            continue;
        stat(dt->d_name, &st);
        bzero(buf, sizeof(buf));
        //封装数据包,包括文件名和文件大小
        snprintf(buf, sizeof(buf), "%-9s   %ld\n", dt->d_name, st.st_size);
        if (send(sfd, buf, sizeof(buf), 0) < 0)
            PRINT_ERR("send");
        //等待客户端读取完上一个数据包然后在给客户端发送下一个
        if (recv(sfd, buf, 1, 0) < 0)
            PRINT_ERR("recv");
    }
    bzero(buf, sizeof(buf));
    *(unsigned short *)buf = htons(1);
    //给客户端发送1,表示发送完毕
    if (send(sfd, buf, 2, 0) < 0)
        PRINT_ERR("send");
    closedir(dir);
}
//客户端登录
int Login(int sfd)
{
    ssize_t res;
    char buf[50];
    //读取用户名和密码
    res = recv(sfd, buf, sizeof(buf), 0);
    if (res < 0)
        PRINT_ERR("recv");
    printf("用户名:%s 密码:%s\n", buf, buf + strlen(buf) + 1);
    FILE *fp;
    if ((fp = fopen("usr.txt", "r")) == NULL)
        PRINT_ERR("usr.txt");
    typedef struct
    {
        char name[20];
        char pass[20];
    } usr;
    usr *p[50];
    int i = 0;
    while (1)
    {
        if ((p[i] = (usr *)malloc(sizeof(usr))) == NULL)
        {
            printf("内存申请失败\n");
            return -1;
        }
        if (fscanf(fp, "%s%s", p[i]->name, p[i]->pass) < 0)
        {
            if (errno == 0)
            {
                break;
            }
            else
            {
                PRINT_ERR("fscanf");
            }
        }
        i++;
    }
    int flag = 0;
    for (int j = 0; j < i; j++)
    {
        if (strcmp(buf, p[j]->name) == 0)
        {
            flag = 1;
            if (strcmp(buf + strlen(buf) + 1, p[j]->pass) == 0)
            {
                //登录成功发送1
                printf("登录成功\n");
                buf[0] = 1;
                if (send(sfd, buf, 1, 0) < 0)
                    PRINT_ERR("send");
                break;
            }
            //密码不对发送2
            printf("密码不正确\n");
            buf[0] = 2;
            if (send(sfd, buf, 1, 0) < 0)
                PRINT_ERR("send");
            break;
        }
    }
    if (flag == 0)
    {
        //不存在该用户发送0
        printf("不存在该用户\n");
        buf[0] = 0;
        if (send(sfd, buf, 1, 0) < 0)
            PRINT_ERR("send");
    }
    for (int j = 0; j < i; j++)
    {
        free(p[j]);
        p[j] = NULL;
    }
    fclose(fp);
    return 0;
}
//客户端注册
int Register(int sfd)
{
    ssize_t res;
    char buf[50];
    //读取用户名和密码
    res = recv(sfd, buf, sizeof(buf), 0);
    if (res < 0)
        PRINT_ERR("recv");
    printf("用户名:%s 密码:%s\n", buf, buf + strlen(buf) + 1);
    FILE *fp;
    if ((fp = fopen("usr.txt", "a+")) == NULL)
        PRINT_ERR("usr.txt");
    typedef struct
    {
        char name[20];
        char pass[20];
    } usr;
    usr *p[50];
    int i = 0;
    while (1)
    {
        if ((p[i] = (usr *)malloc(sizeof(usr))) == NULL)
        {
            printf("内存申请失败\n");
            return -1;
        }
        if (fscanf(fp, "%s%s", p[i]->name, p[i]->pass) < 0)
        {
            if (errno == 0)
            {
                break;
            }
            else
            {
                PRINT_ERR("fscanf");
            }
        }
        i++;
    }
    int flag = 0;
    for (int j = 0; j < i; j++)
    {
        if (strcmp(buf, p[j]->name) == 0)
        {
            printf("注册失败,已有该用户\n");
            buf[0] = 0;
            //注册失败向客户端发送0
            if (send(sfd, buf, 1, 0) < 0)
                PRINT_ERR("send");
            flag = 1;
            break;
        }
    }
    if (flag == 0)
    {
        fprintf(fp, "%s %s\n", buf, buf + strlen(buf) + 1);
        fflush(fp);
        printf("注册成功\n");
        buf[0] = 1;
        //注册成功向客户端发送1
        if (send(sfd, buf, 1, 0) < 0)
            PRINT_ERR("send");
    }
    for (int j = 0; j < i; j++)
    {
        free(p[j]);
        p[j] = NULL;
    }
    fclose(fp);
    return 0;
}

客户端

#include <head.h>
#define PORT 8888
#define IP "192.168.31.159"

int do_download(int sfd);
int Upload(int sfd);
int View_Directory(int sfd);
int Login(int sfd);
int Register(int sfd);

int main(int argc, const char *argv[])
{
    //创建流式套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0)
        PRINT_ERR("socket");
    struct sockaddr_in sin_t;
    sin_t.sin_family = AF_INET;
    sin_t.sin_port = htons(PORT);
    sin_t.sin_addr.s_addr = inet_addr(IP);
    if (connect(sfd, (struct sockaddr *)&sin_t, sizeof(sin_t)) < 0)
        PRINT_ERR("connect");
    printf("成功连接服务器[%s:%d]\n", inet_ntoa(sin_t.sin_addr), ntohs(sin_t.sin_port));
    char c;
    while (1)
    {
        system("clear");
        printf("*******************\n");
        printf("*******1.登录******\n");
        printf("*******2.注册******\n");
        printf("*******3.退出******\n");
        printf("*******************\n");
        printf("请输入>>>");
        c = getchar();
        while (getchar() != 10)
            ;
        switch (c)
        {
        case '1':
            c = Login(sfd);
            break;
        case '2':
            Register(sfd);
            break;
        case '3':
            goto END;
            break;
        default:
            printf("请输入正确的选项\n");
            break;
        }
        if (c == 1)
        {
            printf("输入任意字符跳转>>>");
            while (getchar() != 10)
                ;
            break;
        }
        printf("输入任意字符清屏>>>");
        while (getchar() != 10)
            ;
    }
    while (1)
    {
        system("clear");
        printf("*******************\n");
        printf("*******1.下载******\n");
        printf("*******2.上传******\n");
        printf("*******3.查看******\n");
        printf("*******4.退出******\n");
        printf("*******************\n");
        printf("请输入>>>");
        c = getchar();
        while (getchar() != 10)
            ;
        switch (c)
        {
        case '1':
            do_download(sfd);
            break;
        case '2':
            Upload(sfd);
            break;
        case '3':;
            View_Directory(sfd);
            break;
        case '4':
            goto END;
            break;

        default:
            printf("请输入正确的选项\n");
            break;
        }
        printf("输入任意字符清屏>>>");
        while (getchar() != 10)
            ;
    }
END:
    close(sfd);
    return 0;
}
//下载
int do_download(int sfd)
{
    printf("请输入文件名\n");
    char filename[20];
    fgets(filename, sizeof(filename), stdin);
    filename[strlen(filename) - 1] = 0;
    char buf[514];
    //封装第一个数据包,包括功能编号和文件名
    int size = sprintf(buf, "%c%c%s%c", 0, 1, filename, 0);
    if (send(sfd, buf, size, 0) < 0)
        PRINT_ERR("send");
    size = 0;
    int fd, num = 1;
    ssize_t res;
    while (1)
    {
        //接收服务器数据
        bzero(buf, sizeof(buf));
        res = recv(sfd, buf, sizeof(buf), 0);
        if (res < 0)
            PRINT_ERR("recv");
        //判断编号是否正确
        if (htons(num) == *(unsigned short *)buf)
        {
            num++;
            //如果是第一次接收数据则创建文件
            if (size == 0)
            {
                fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0777);
                if (fd < 0)
                    PRINT_ERR("open");
            }
            if (write(fd, buf + 2, res - 2) < 0)
                PRINT_ERR("write");
            size += (res - 2);
            //给服务器发送一个字节表示客户端已读取完数据,请求发送下一个数据包
            if (send(sfd, buf, 1, 0) < 0)
                PRINT_ERR("send");
            if (res < 514)
                break;
        }
        else
        {
            printf("下载失败无该文件\n");
            break;
        }
    }
    if (size != 0)
    {
        printf("下载完成,大小为%d\n", size);
        close(fd);
    }
    return 0;
}
//上传
int Upload(int sfd)
{
    printf("请输入文件名\n");
    char filename[50];
    fgets(filename, sizeof(filename), stdin);
    filename[strlen(filename) - 1] = 0;
    int t = -1;
    for (int i = 0; i < strlen(filename); i++)
    {
        if (filename[i] == '/')
            t = i;
    }
    char buf[514];
    //封装第一个数据包,包括功能编号和文件名
    int size = sprintf(buf, "%c%c%s%c", 0, 2, filename + t + 1, 0);
    if (send(sfd, buf, size, 0) < 0)
        PRINT_ERR("send");
    size = 0;
    int fd, num = 1;
    ssize_t res;
    while (1)
    {
        bzero(buf, sizeof(buf));
        res = recv(sfd, buf, 2, 0);
        if (res < 0)
            PRINT_ERR("recv");
        //判断服务器是否发送正确的数据
        if (htons(num) == *(unsigned short *)buf)
        {
            //如果是第一次发送数据则打开文件
            if (size == 0)
            {
                fd = open(filename, O_RDONLY);
                //打开文件失败给服务器发送0
                if (fd < 0)
                {
                    *(unsigned short *)buf = htons(0);
                    if (send(sfd, buf, 2, 0) < 0)
                        PRINT_ERR("send");
                    PRINT_ERR("open");
                }
            }
            //给每个数据包的头两个字节是编号
            bzero(buf, sizeof(buf));
            *(unsigned short *)buf = htons(num);
            num++;
            res = read(fd, buf + 2, sizeof(buf) - 2);
            if (res < 0)
                PRINT_ERR("read");
            if (send(sfd, buf, res + 2, 0) < 0)
                PRINT_ERR("send");
            size += res;
            if (res < sizeof(buf) - 2)
            {
                printf("上传完毕\n");
                break;
            }
        }
        else
        {
            printf("服务器创建文件出错,上传失败\n");
            break;
        }
    }
    if (size != 0)
    {
        printf("上传完成,大小为%d\n", size);
        close(fd);
    }
    return 0;
}
//查看
int View_Directory(int sfd)
{
    char buf[50];
    buf[1] = 3;
    //发送第一个数据包只有两个字节表示功能
    if (send(sfd, buf, 2, 0) < 0)
        PRINT_ERR("send");
    while (1)
    {
        bzero(buf, sizeof(buf));
        if (recv(sfd, buf, sizeof(buf), 0) < 0)
            PRINT_ERR("recv");
        //服务器端发送1则表示发送完毕
        if (*(unsigned short *)buf == htons(1))
            break;
        printf("%s", buf);
        //给服务器发送一个字节表示客户端已读取完数据,请求发送下一个数据包
        if (send(sfd, buf, 1, 0) < 0)
            PRINT_ERR("send");
    }
}
//登录
int Login(int sfd)
{
    char name[20];
    char password[30];
    char buf[50];
    printf("请输入用户名和密码\n");
    printf("(用户名和密码用空格隔开)\n");
    scanf("%s %s", name, password);
    while (getchar() != 10)
        ;
    int size = sprintf(buf, "%c%c%s%c%s%c", 0, 4, name, 0, password, 0);
    if (send(sfd, buf, size, 0) < 0)
        PRINT_ERR("send");
    if (recv(sfd, buf, 1, 0) < 0)
        PRINT_ERR("recv");
    if (buf[0] == 1)
    {
        printf("登录成功\n");
        return 1;
    }
    else if (buf[0] == 0)
        printf("不存在该用户\n");
    else if (buf[0] == 2)
        printf("密码不正确\n");
    return 0;
}
//注册
int Register(int sfd)
{
    char name[20];
    char password[30];
    char buf[50];
    printf("请输入要注册的用户名和密码\n");
    printf("(用户名和密码用空格隔开)\n");
    scanf("%s %s", name, password);
    while (getchar() != 10)
        ;
    int size = sprintf(buf, "%c%c%s%c%s%c", 0, 5, name, 0, password, 0);
    if (send(sfd, buf, size, 0) < 0)
        PRINT_ERR("send");
    if (recv(sfd, buf, 1, 0) < 0)
        PRINT_ERR("recv");
    if (buf[0] == 1)
        printf("注册成功\n");
    else
        printf("注册失败,已存在该用户\n");
    return 0;
}

注册成功
在这里插入图片描述
注册重复用户
在这里插入图片描述

密码不正确登录
在这里插入图片描述
不存在用户名登录
在这里插入图片描述
登录成功
在这里插入图片描述

下载正确
在这里插入图片描述
下载失败
在这里插入图片描述
上传本目录文件正确
在这里插入图片描述
上传其他目录文件正确
在这里插入图片描述
上传错误文件
在这里插入图片描述
查询服务器目录文件
在这里插入图片描述
退出
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值