TCP客户端与服务器+sqlite3作业

【server.c】

/* 服务器.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sqlite3.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;

enum Type {
    TYPE_REGIST,
    TYPE_LOGIN,
    TYPE_CHAT,
    TYPE_FILE_UPLOAD_REQUEST
};

typedef struct Pack {
    enum Type type;
    char name[20];
    char pswd[20];
    char filename[20];
    long filesize;
    char tarname[20];
    char text[1024];
} pack_t;

typedef struct User {
    char name[20];
    int sock;
    int hasMsg;
    char msg[1024];
} user_t;

user_t user_arr[50] = {0};
int user_len = 0;
sqlite3 *db;

void read_data(int client);
void insert_user(user_t user);
int find_user(const char* username);

int main(int argc, const char *argv[]) {
    if (argc != 2) {
        printf("请输入端口号\n");
        return 1;
    }

    // 初始化数据库
    int rc = sqlite3_open("users.db", &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
        return 1;
    }

    // 创建用户表
    const char *sql = "CREATE TABLE IF NOT EXISTS users ("
                      "username TEXT PRIMARY KEY, "
                      "password TEXT NOT NULL);";
    char *err_msg = NULL;
    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "数据库错误: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
    }

    int port = atoi(argv[1]);
    int server = socket(AF_INET, SOCK_STREAM, 0);
    addr_in_t addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    if (bind(server, (addr_t*)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }

    listen(server, 10);

    int epfd = epoll_create1(EPOLL_CLOEXEC);
    struct epoll_event epoll_stdin = { .events = EPOLLIN, .data.fd = 0 };
    struct epoll_event epoll_server = { .events = EPOLLIN, .data.fd = server };

    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &epoll_stdin);
    epoll_ctl(epfd, EPOLL_CTL_ADD, server, &epoll_server);

    struct epoll_event arr[50] = {0};

    while (1) {
        int len = epoll_wait(epfd, arr, 50, -1);
        for (int i = 0; i < len; i++) {
            int fd = arr[i].data.fd;
            if (fd == server) {
                int client = accept(server, 0, 0);
                struct epoll_event epoll_client = { .events = EPOLLIN, .data.fd = client };
                epoll_ctl(epfd, EPOLL_CTL_ADD, client, &epoll_client);
            } else if (fd == 0) {
                char buf[64] = "";
                scanf("%63s", buf);
                while (getchar() != '\n');
                printf("键盘输入数据: %s\n", buf);
            } else {
                read_data(fd);
            }
        }
    }
    sqlite3_close(db);
    return 0;
}

void insert_user(user_t user) {
    user_arr[user_len] = user;
    user_len++;
}

int find_user(const char* username) {
    for (int i = 0; i < user_len; i++) {
        if (strcmp(username, user_arr[i].name) == 0) {
            return i;
        }
    }
    return -1;
}

void read_data(int client) {
    pack_t pack = {0};
    int res = read(client, &pack, sizeof(pack));
    if (res <= 0) {
        close(client);
        // 从在线列表中移除用户
        for (int i = 0; i < user_len; i++) {
            if (user_arr[i].sock == client) {
                user_arr[i] = user_arr[user_len - 1];
                user_len--;
                break;
            }
        }
        return;
    }

    switch (pack.type) {
        case TYPE_REGIST: {
            sqlite3_stmt *stmt;
            const char *sql = "INSERT INTO users (username, password) VALUES (?, ?);";
            int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
            if (rc != SQLITE_OK) {
                strcpy(pack.text, "注册失败");
            } else {
                sqlite3_bind_text(stmt, 1, pack.name, -1, SQLITE_STATIC);
                sqlite3_bind_text(stmt, 2, pack.pswd, -1, SQLITE_STATIC);
                rc = sqlite3_step(stmt);
                if (rc == SQLITE_DONE) {
                    strcpy(pack.text, "注册成功");
                } else {
                    strcpy(pack.text, "该账号已存在");
                }
                sqlite3_finalize(stmt);
            }
            write(client, &pack, sizeof(pack));
            break;
        }
        case TYPE_LOGIN: {
            sqlite3_stmt *stmt;
            const char *sql = "SELECT password FROM users WHERE username = ?;";
            int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
            if (rc != SQLITE_OK) {
                strcpy(pack.text, "登录失败");
            } else {
                sqlite3_bind_text(stmt, 1, pack.name, -1, SQLITE_STATIC);
                rc = sqlite3_step(stmt);
                if (rc == SQLITE_ROW) {
                    const char *stored_pswd = (const char*)sqlite3_column_text(stmt, 0);
                    if (strcmp(stored_pswd, pack.pswd) == 0) {
                        user_t user = {0};
                        strcpy(user.name, pack.name);
                        user.sock = client;
                        int idx = find_user(pack.name);
                        if (idx == -1) {
                            insert_user(user);
                        } else {
                            user_arr[idx].sock = client;
                        }
                        strcpy(pack.text, "登录成功");
                    } else {
                        strcpy(pack.text, "密码错误");
                    }
                } else {
                    strcpy(pack.text, "该账号不存在");
                }
                sqlite3_finalize(stmt);
            }
            write(client, &pack, sizeof(pack));
            break;
        }
        case TYPE_CHAT: {
            int idx = find_user(pack.tarname);
            if (idx == -1) {
                strcpy(pack.text, "该用户不存在");
                write(client, &pack, sizeof(pack));
            } else {
                user_t user = user_arr[idx];
                if (user.sock == 0) {
                    user_arr[idx].hasMsg = 1;
                    strcpy(user_arr[idx].msg, pack.text);
                } else {
                    write(user.sock, &pack, sizeof(pack));
                }
            }
            break;
        }
        case TYPE_FILE_UPLOAD_REQUEST: {
            int idx = find_user(pack.tarname);
            if (idx == -1) {
                strcpy(pack.text, "用户不存在");
                write(client, &pack, sizeof(pack));
            } else {
                user_t user = user_arr[idx];
                if (user.sock == 0) {
                    user_arr[idx].hasMsg = 1;
                    strcpy(user_arr[idx].msg, "用户不在线,文件上传请求已保存");
                    strcpy(pack.text, "用户不在线,文件上传请求已保存");
                    write(client, &pack, sizeof(pack));
                } else {
                    write(user.sock, &pack, sizeof(pack));
                }
            }
            break;
        }
    }
}


【client.c】

/* 客户端.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;

enum Type {
    TYPE_REGIST,
    TYPE_LOGIN,
    TYPE_CHAT,
    TYPE_FILE_UPLOAD_REQUEST
};

typedef struct Pack {
    enum Type type;
    char name[20];
    char pswd[20];
    char filename[20];
    long filesize;
    char tarname[20];
    char text[1024];
} pack_t;

void* thread_main(void* arg) {
    int client = *(int*)arg;
    while (1) {
        pack_t pack = {0};
        int res = read(client, &pack, sizeof(pack));
        if (res == 0) break;
        switch (pack.type) {
            case TYPE_REGIST:
            case TYPE_LOGIN:
                printf("%s\n", pack.text);
                break;
            case TYPE_CHAT:
                printf("接收到消息: %s\n", pack.text);
                break;
            case TYPE_FILE_UPLOAD_REQUEST: {
                char filename[128] = "./client_file_system/";
                strcat(filename, pack.filename);
                int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666);
                long filesize = pack.filesize;
                long readed_size = 0;
                while (1) {
                    pack_t filepack = {0};
                    res = read(client, &filepack, sizeof(filepack));
                    if (res != sizeof(filepack)) {
                        printf("发生分包\n");
                    }
                    int size = strlen(filepack.text);
                    write(fd, filepack.text, size);
                    readed_size += size;
                    if (readed_size >= filesize) {
                        close(fd);
                        break;
                    }
                }
                break;
            }
        }
    }
    return NULL;
}

int main(int argc, const char *argv[]) {
    if (argc != 2) {
        printf("请输入端口号\n");
        return 1;
    }
    int port = atoi(argv[1]);
    int client = socket(AF_INET, SOCK_STREAM, 0);
    addr_in_t addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(client, (addr_t*)&addr, sizeof(addr)) == -1) {
        perror("connect");
        return 1;
    }

    pthread_t id;
    pthread_create(&id, 0, thread_main, &client);
    pthread_detach(id);

    while (1) {
        int ch = -1;
        printf("1: 注册\n2: 登录\n3: 聊天\n4: 发送文件\n0: 退出\n请选择: ");
        scanf("%d", &ch);
        while (getchar() != '\n');

        pack_t pack = {0};
        switch (ch) {
            case 1:
                printf("请输入账号: ");
                scanf("%19s", pack.name);
                while (getchar() != '\n');

                printf("请输入密码: ");
                scanf("%19s", pack.pswd);
                while (getchar() != '\n');

                pack.type = TYPE_REGIST;
                write(client, &pack, sizeof(pack));
                break;
            case 2:
                printf("请输入账号: ");
                scanf("%19s", pack.name);
                while (getchar() != '\n');

                printf("请输入密码: ");
                scanf("%19s", pack.pswd);
                while (getchar() != '\n');

                pack.type = TYPE_LOGIN;
                write(client, &pack, sizeof(pack));
                break;
            case 3:
                printf("请输入目标用户名和消息: ");
                scanf("%19s %1023s", pack.tarname, pack.text);
                while (getchar() != '\n');

                pack.type = TYPE_CHAT;
                write(client, &pack, sizeof(pack));
                break;
            case 4:
                printf("请输入接收文件的用户名: ");
                scanf("%19s", pack.tarname);
                while (getchar() != '\n');

                printf("请输入想要发送的文件名: ");
                scanf("%19s", pack.filename);
                while (getchar() != '\n');

                int fd = open(pack.filename, O_RDONLY);
                if (fd == -1) {
                    printf("该文件不存在\n");
                    break;
                }
                struct stat buf;
                stat(pack.filename, &buf);
                pack.filesize = buf.st_size;
                pack.type = TYPE_FILE_UPLOAD_REQUEST;
                write(client, &pack, sizeof(pack));

                while (1) {
                    pack_t filepack = {0};
                    filepack.type = TYPE_FILE_UPLOAD_REQUEST;
                    strcpy(filepack.tarname, pack.tarname);
                    int res = read(fd, filepack.text, 1023);
                    if (res == 0) break;
                    write(client, &filepack, sizeof(filepack));
                }
                close(fd);
                break;
            case 0:
                close(client);
                return 0;
            default:
                printf("无效选项\n");
                break;
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值