wangpang.xingkong(tou)

目录

client

│   ├── client.h/c     connect  login  recv  send  getcommand  pausecommand  putscommand  

│   ├── main.c  登陆&监听

│   ├── str_util.h/c  分割token字符串

 conf

│   └── server.conf

server

│   ├── config.h/c   读取文件,解析文件

│   ├── hashtable.h/c   哈希表

│   ├── linked_list.h/c  链表

│   ├── str_util.h/c   解析字符串

│   ├── task_queue.c   线程任务队列实现

│   ├── thread_pool.h/c    线程池(超多)

│   ├── user.h/c   登录检查

│   ├── bussiness.c   业务合集

│   ├── transfer.c  交换文件

│   ├── ls.c  指令接口,过

│   ├── main.c   启动启动,监听

│   ├── puts.c  puts指令接口

│   ├── pwd.c  指令接口,过

│   ├── server.c    服务端实现,连接监听

 test

    ├── getspnam.c

    ├── jwt

    │   ├── decode.c

    │   ├── encode.c

    │   └── Makefile

    ├── Makefile

    ├── md5

    │   ├── Makefile

    │   ├── md5.c

    │   └── test.txt

    └── split_string.c

client

│   ├── client.h/c     connect  login  recv  send  getcommand  pausecommand  putscommand  

#pragma once

#define USER_NAME "please input a valid user name:\n"
#define PASSWORD "please input the right password:\n"

typedef enum {
    CMD_TYPE_PWD=1,
    CMD_TYPE_LS,
    CMD_TYPE_CD,
    CMD_TYPE_MKDIR,
    CMD_TYPE_RMDIR,
    CMD_TYPE_PUTS,
    CMD_TYPE_GETS,
    CMD_TYPE_NOTCMD,  //不是命令

    TASK_LOGIN_SECTION1 = 100,
    TASK_LOGIN_SECTION1_RESP_OK,
    TASK_LOGIN_SECTION1_RESP_ERROR,
    TASK_LOGIN_SECTION2,
    TASK_LOGIN_SECTION2_RESP_OK,
    TASK_LOGIN_SECTION2_RESP_ERROR,

}CmdType;


typedef struct 
{
    int len;//记录内容长度
    CmdType type;//消息类型
    char buff[1000];//记录内容本身
}train_t;

int tcpConnect(const char * ip, unsigned short port);
int recvn(int sockfd, void * buff, int len);
int sendn(int sockfd, const void * buff, int len);

int userLogin(int sockfd);

int parseCommand(const char * input, int len, train_t * pt);

//判断一个字符串是什么命令
int getCommandType(const char * str);
//执行上传文件操作
void putsCommand(int sockfd, train_t * pt);
#include "client.h"
#include "str_util.h"
#include <stdio.h>
#include <unistd.h>

int tcpConnect(const char * ip, unsigned short port)
{
    //1. 创建TCP的客户端套接字
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if(clientfd < 0) {
        perror("socket");
        return -1;
    }

    //2. 指定服务器的网络地址
    struct sockaddr_in serveraddr;
    //初始化操作,防止内部有脏数据
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;//指定IPv4
    serveraddr.sin_port = htons(port);//指定端口号
    //指定IP地址
    serveraddr.sin_addr.s_addr = inet_addr(ip);

    //3. 发起建立连接的请求
    int ret = connect(clientfd, (const struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret < 0) {
        perror("connect");
        close(clientfd);
        return -1;
    }
    return clientfd;
}

static int userLogin1(int sockfd, train_t *t);
static int userLogin2(int sockfd, train_t *t);

int userLogin(int sockfd)
{
    int ret;
    train_t t;
    memset(&t, 0, sizeof(t));
    userLogin1(sockfd, &t);
    userLogin2(sockfd, &t);
    return 0;
}

static int userLogin1(int sockfd, train_t *pt)
{
    /* printf("userLogin1.\n"); */
    train_t t;
    memset(&t, 0, sizeof(t));
    while(1) {
        printf(USER_NAME);
        char user[20]= {0};
        int ret = read(STDIN_FILENO, user, sizeof(user));
        user[ret - 1] = '\0';
        t.len = strlen(user);
        t.type = TASK_LOGIN_SECTION1;
        strncpy(t.buff, user, t.len);
        ret = sendn(sockfd, &t, 8 + t.len);
        /* printf("login1 send %d bytes.\n", ret); */

        //接收信息
        memset(&t, 0, sizeof(t));
        ret = recvn(sockfd, &t.len, 4);
        /* printf("length: %d\n", t.len); */
        ret = recvn(sockfd, &t.type, 4);
        if(t.type == TASK_LOGIN_SECTION1_RESP_ERROR) {
            //无效用户名, 重新输入
            printf("user name not exist.\n");
            continue;
        }
        //用户名正确,读取setting
        ret = recvn(sockfd, t.buff, t.len);
        break;
    }
    memcpy(pt, &t, sizeof(t));
    return 0;
}

static int userLogin2(int sockfd, train_t * pt)
{
    /* printf("userLogin2.\n"); */
    int ret;
    train_t t;
    memset(&t, 0, sizeof(t));
    while(1) {
        char * passwd = getpass(PASSWORD);
        /* printf("password: %s\n", passwd); */
        char * encrytped = crypt(passwd, pt->buff);
        /* printf("encrytped: %s\n", encrytped); */
        t.len = strlen(encrytped);
        t.type = TASK_LOGIN_SECTION2;
        strncpy(t.buff, encrytped, t.len);
        ret = sendn(sockfd, &t, 8 + t.len);
        /* printf("userLogin2 send %d bytes.\n", ret); */

        memset(&t, 0, sizeof(t));
        ret = recvn(sockfd, &t.len, 4);
        /* printf("2 length: %d\n", t.len); */
        ret = recvn(sockfd, &t.type, 4);
        if(t.type == TASK_LOGIN_SECTION2_RESP_ERROR) {
            //密码不正确
            printf("sorry, password is not correct.\n");
            continue;
        } else {
            ret = recvn(sockfd, t.buff, t.len);
            printf("Login Success.\n");
            printf("please input a command.\n");
            fprintf(stderr, "%s", t.buff);
            break;
        } 
    }
    return 0;
}

//其作用:确定接收len字节的数据
int recvn(int sockfd, void * buff, int len)
{
    int left = len;//还剩下多少个字节需要接收
    char * pbuf = buff;
    int ret = -1;
    while(left > 0) {
        ret = recv(sockfd, pbuf, left, 0);
        if(ret == 0) {
            break;
        } else if(ret < 0) {
            perror("recv");
            return -1;
        }

        left -= ret;
        pbuf += ret;
    }
    //当退出while循环时,left的值等于0
    return len - left;
}

//作用: 确定发送len字节的数据
int sendn(int sockfd, const void * buff, int len)
{
    int left = len;
    const char * pbuf = buff;
    int ret = -1;
    while(left > 0) {
        ret = send(sockfd, pbuf, left, 0);
        if(ret < 0) {
            perror("send");
            return -1;
        }

        left -= ret;
        pbuf += ret;
    }
    return len - left;
}

//解析命令
int parseCommand(const char * pinput, int len, train_t * pt)
{
    char * pstrs[10] = {0};
    int cnt = 0;
    splitString(pinput, " ", pstrs, 10, &cnt);
    pt->type = getCommandType(pstrs[0]);
    //暂时限定命令行格式为:
    //1. cmd
    //2. cmd content
    if(cnt > 1) {
        pt->len = strlen(pstrs[1]);
        strncpy(pt->buff, pstrs[1], pt->len);
    }
    return 0;
}

int getCommandType(const char * str)
{
    if(!strcmp(str, "pwd")) 
        return CMD_TYPE_PWD;
    else if(!strcmp(str, "ls"))
        return CMD_TYPE_LS;
    else if(!strcmp(str, "cd"))
        return CMD_TYPE_CD;
    else if(!strcmp(str, "mkdir"))
        return CMD_TYPE_MKDIR;
    else if(!strcmp(str,"rmdir"))
        return CMD_TYPE_RMDIR;
    else if(!strcmp(str, "puts"))
        return CMD_TYPE_PUTS;
    else if(!strcmp(str, "gets"))
        return CMD_TYPE_GETS;
    else
        return CMD_TYPE_NOTCMD;
}

void putsCommand(int sockfd, train_t * pt)
{
    char filename[20] = {0};
    strcpy(filename, pt->buff);

    //打开文件
    int fd = open(filename, O_RDWR);
    if(fd < 0) {
        perror("open"); return;
    }
    //获取文件大小
    struct stat st;
    memset(&st, 0, sizeof(st));
    fstat(fd, &st);
    printf("file length: %ld\n", st.st_size);
    //发送文件大小
    sendn(sockfd, &st.st_size, sizeof(st.st_size));
    off_t cur = 0;
    char buff[1000] = {0};
    int ret = 0;
    //发送内容
    while(cur < st.st_size) {
        memset(buff, 0, sizeof(buff));
        ret = read(fd, buff, sizeof(buff));
        if(ret == 0) {
            break;
        }
        ret = sendn(sockfd, buff, ret);
        cur +=  ret;
    }
    //发送完成
    printf("file send over.\n");
    close(fd);
}

│   ├── main.c  登陆&监听

#include "client.h"
#include <func.h>

int main()
{
    int clientfd = tcpConnect("127.0.0.1", 8080);

    //用户登录操作
    userLogin(clientfd);

    char buf[128] = {0};
    //4. 使用select进行监听
    fd_set rdset;
    train_t t;
    while(1) {
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(clientfd, &rdset);
        
        int nready = select(clientfd + 1, &rdset, NULL, NULL, NULL);
        printf("nready:%d\n", nready);
        if(FD_ISSET(STDIN_FILENO, &rdset)) {
            //读取标准输入中的数据
            memset(buf, 0, sizeof(buf));
            int ret = read(STDIN_FILENO, buf, sizeof(buf));
            if(0 == ret) {
                printf("byebye.\n");
                break;
            }
            memset(&t, 0, sizeof(t));
            //解析命令行
            buf[strlen(buf)-1] = '\0';
            parseCommand(buf, strlen(buf), &t);
            sendn(clientfd, &t, 4 + 4 + t.len);
            if(t.type == CMD_TYPE_PUTS) {
                putsCommand(clientfd, &t);
            }
        } else if(FD_ISSET(clientfd, &rdset)) {
            recv(clientfd, buf, sizeof(buf), 0);
            printf("recv:%s\n", buf);
        }
    }
    close(clientfd);
    return 0;
}

│   ├── str_util.h/c  分割token字符串

#pragma once
#include <func.h>

//作用: 分割字符串
//@pstr        原字符串
//@delimiter   分隔符
//@ptokens     字符串数组首地址
//@max_tokens  指定最大值
//@pcount      传出参数,获取分割后的字符串个数
void splitString(const char *pstr, const char * delimiter, char *ptokens[], int max_tokens, int * pcount);
void freeStrs(char * pstrs[], int count);
#include "str_util.h"

// 假设max_tokens是数组tokens的最大大小
// 在使用后,记得要释放空间
void splitString(const char * pstrs, const char* delimiter, char *tokens[], int max_tokens, int * pcount) {
    int token_count = 0;
    char *token = strtok((char *)pstrs, delimiter); // 使用delimiter作为分隔符

    while (token != NULL && token_count < max_tokens - 1) { // 保留一个位置给NULL终止符
        char * pstr = (char*)calloc(1, strlen(token) + 1);
        strcpy(pstr, token);
        tokens[token_count] = pstr;//保存申请的堆空间首地址
        token_count++;
        token = strtok(NULL, delimiter); // 继续获取下一个token
    }
    // 添加NULL终止符
    tokens[token_count] = NULL;
    *pcount= token_count;
}

void freeStrs(char * pstrs[], int count)
{
    for(int i = 0; i < count; ++i) {
        free(pstrs[i]);
    }
}

 conf

│   └── server.conf

ip=127.0.0.1
port=8080
thread_num=3

server

│   ├── config.h/c   读取文件,解析文件

#ifndef __CONFIG_H__
#define __CONFIG_H__

#include "hashtable.h"
#include "str_util.h"

#define IP "ip"
#define PORT "port"
#define THREAD_NUM "thread_num"

void readConfig(const char* filename, HashTable * ht);

#endif
#include "config.h"

void readConfig(const char* filename, HashTable * ht)
{
    FILE * fp = fopen(filename, "rw");
    if(fp == NULL) {
        printf("open file %s error.\n", filename);
        return;
    }

    char buff[128] = {0};
    while(fgets(buff, sizeof(buff), fp) != NULL) {
        char * strs[3] = {0};
        int cnt = 0;
        splitString(buff, "=", strs, 3, &cnt);
        /* printf("cnt: %d\n", cnt); */
        /* for(int i = 0; i < cnt; ++i) { */
        /*     printf("strs[%d]: %s\n", i, strs[i]); */
        /* } */
        /* printf("\n"); */
        char * value = (char*)calloc(1, strlen(strs[1]) + 1);
        strcpy(value, strs[1]);
        insert(ht, strs[0], value);
        freeStrs(strs, cnt);
    }

    fclose(fp);
}

│   ├── hashtable.h/c   哈希表

#ifndef __HASH_TABLE_H__
#define __HASH_TABLE_H__
#include <stdio.h>  
#include <stdlib.h>
#include <string.h>  

  
#define MAX_SIZE 100  
#define EMPTY NULL  
  
// 键值对结构体  
typedef struct {  
    char key[50];  
    void * value;  
} KeyValue;  
  
// 哈希表结构体  
typedef struct {  
    KeyValue table[MAX_SIZE];  
    int size;  
} HashTable;  
  
// hash函数
unsigned int hash(const char *key);
  
// 初始化哈希表  
void initHashTable(HashTable *ht);  
  
// 插入键值对  
void insert(HashTable *ht, const char *key, void * value);
  
// 查找值  
void * find(HashTable *ht, const char *key); 

// 删除键值对  
void erase(HashTable *ht, const char *key); 
  
// 打印哈希表内容(仅用于调试)  
void printHashTable(HashTable *ht);   

// 销毁哈希表
void destroyHashTable(HashTable *ht);
  
#endif
#include "hashtable.h" 

// hash函数
unsigned int hash(const char *key) {  
    unsigned int hashVal = 0;  
    while (*key != '\0') {  
        hashVal = (hashVal << 5) + hashVal + *key++;  
    }  
    return hashVal % MAX_SIZE;  
}  
  
// 初始化哈希表  
void initHashTable(HashTable *ht) {  
    ht->size = 0;  
    for (int i = 0; i < MAX_SIZE; i++) {  
        strcpy(ht->table[i].key, "");  
        ht->table[i].value = EMPTY;  
    }  
}  
  
// 插入键值对  
void insert(HashTable *ht, const char *key, void * value) {  
    unsigned int index = hash(key);  
    // 线性探测解决冲突  
    while (ht->table[index].value != EMPTY) {  
        index = (index + 1) % MAX_SIZE;  
        if (strcmp(ht->table[index].key, key) == 0) {  
            // 如果键已存在,更新值  
            ht->table[index].value = value;  
            return;  
        }  
    }  
    // 插入新键值对  
    strcpy(ht->table[index].key, key);  
    ht->table[index].value = value;  
    ht->size++;  
}  
  
// 查找值  
void * find(HashTable *ht, const char *key) {  
    unsigned int index = hash(key);  
    while (ht->table[index].value != EMPTY) {  
        if (strcmp(ht->table[index].key, key) == 0) {  
            return ht->table[index].value;  
        }  
        index = (index + 1) % MAX_SIZE;  
    }  
    return EMPTY; // 键不存在  
}  
  
// 删除键值对  
void erase(HashTable *ht, const char *key) {  
    unsigned int index = hash(key);  
    while (ht->table[index].value != EMPTY) {  
        if (strcmp(ht->table[index].key, key) == 0) {  
            strcpy(ht->table[index].key, "");  
            free(ht->table[index].value);
            ht->table[index].value = EMPTY;  
            ht->size--;  
            return;  
        }  
        index = (index + 1) % MAX_SIZE;  
    }  
}  
  
// 打印哈希表内容(仅用于调试)  
void printHashTable(HashTable *ht) {  
    printf("hashtable's content:\n");
    for (int i = 0; i < MAX_SIZE; i++) {  
        if (ht->table[i].value != EMPTY) {  
            printf("Key: %s, Value: %s\n", ht->table[i].key, (const char*)ht->table[i].value);  
        }  
    }  
    printf("\n");
}  

void destroyHashTable(HashTable *ht) {
    for(int i = 0; i < MAX_SIZE; i++) {
        if(ht->table[i].value != EMPTY) {
            strcpy(ht->table[i].key, "");  
            free(ht->table[i].value);
            ht->table[i].value = EMPTY;  
            ht->size--;  
        }
    }
}

│   ├── linked_list.h/c  链表

#ifndef __LINKED_LIST_H__
#define __LINKED_LIST_H__
#include <stdio.h>  
#include <stdlib.h>  
  
// 定义链表节点结构体  
typedef struct ListNode {  
    void * val;              // 节点的值  
    struct ListNode *next;   // 指向下一个节点的指针  
} ListNode;  
  
// 创建新的链表节点  
ListNode* createNode(void * val);
  
// 在链表末尾添加元素  
void appendNode(ListNode **head, void *val);
  
// 删除链表中值为target的节点(假设只删除一个)  
void deleteNode(ListNode **head, void * target);

// 删除链表中值为peerfd的节点(假设只删除一个)  
void deleteNode2(ListNode **head, int peerfd);
  
// 打印链表  
void printList(ListNode *head);
  
// 释放链表内存
void freeList(ListNode *head);
  
#endif
#include "linked_list.h" 
#include "user.h"

// 创建新的链表节点  
ListNode* createNode(void * val) {  
    ListNode *newNode = (ListNode*)malloc(sizeof(ListNode));  
    if (!newNode) {  
        printf("Memory allocation failed\n");  
        exit(1);  
    }  
    newNode->val = val;  
    newNode->next = NULL;  
    return newNode;  
}  
  
// 在链表末尾添加元素  
void appendNode(ListNode **head, void *val) {  
    ListNode *newNode = createNode(val);  
    if (*head == NULL) {  
        *head = newNode;  
        return;  
    }  
    ListNode *current = *head;  
    while (current->next != NULL) {  
        current = current->next;  
    }
    current->next = newNode;  
}  
  
// 删除链表中值为target的节点(假设只删除一个)  
void deleteNode(ListNode **head, void * target) {  
    if (*head == NULL) return;  
  
    if ((*head)->val == target) {  
        ListNode *temp = *head;  
        *head = (*head)->next;  
        free(temp);  
        return;  
    }  
  
    ListNode *current = *head;  
    while (current->next != NULL && current->next->val != target) {  
        current = current->next;  
    }  
  
    if (current->next != NULL) {  
        ListNode *temp = current->next;  
        current->next = current->next->next;  
        free(temp);  
    }  
}  

// 删除链表中值为peerfd的节点(假设只删除一个)  
void deleteNode2(ListNode **head, int peerfd)
{
    if (*head == NULL) return;  
    user_info_t * user = (user_info_t*)((*head)->val);
  
    if (user->sockfd == peerfd) {  
        ListNode *temp = *head;
        *head = (*head)->next;
        free(temp);
        printf("free user node.\n");
        return;
    }  
  
    ListNode *current = *head;  
    while (current->next != NULL) {
        if(((user_info_t*)current->next->val)->sockfd != peerfd) {  
            current = current->next;  
        }   
    }
  
    if (current->next != NULL) {  
        ListNode *temp = current->next;  
        current->next = current->next->next;  
        free(temp);  
        printf("free user node 2.\n");
        return;
    }  
}
  
// 打印链表(仅供调试使用)
void printList(ListNode *head) {  
    ListNode *current = head;  
    while (current != NULL) {  
        printf("%x ", current->val);  
        current = current->next;  
    }  
    printf("\n");  
}  
  
// 释放链表内存
void freeList(ListNode *head) {  
    ListNode *current = head;  
    while (current != NULL) {  
        ListNode *temp = current;  
        current = current->next;  
        free(temp);  
    }  
}  
  

│   ├── str_util.h/c   解析字符串

#pragma once
#include <func.h>

//作用: 分割字符串
//@pstr        原字符串
//@delimiter   分隔符, 如空格、= 等等
//@ptokens     字符串数组首地址
//@max_tokens  指定最大值
//@pcount      传出参数,获取分割后的字符串个数
void splitString(const char *pstr, const char * delimiter, char *ptokens[], int max_tokens, int * pcount);
void freeStrs(char * pstrs[], int count);
#include "str_util.h"

// 假设max_tokens是数组tokens的最大大小
// 在使用后,记得要释放空间
void splitString(const char * pstrs, const char * delimiter, char *tokens[], int max_tokens, int * pcount) {
    int token_count = 0;
    char *token = strtok((char *)pstrs, delimiter); // 使用delimiter作为分隔符

    while (token != NULL && token_count < max_tokens - 1) { // 保留一个位置给NULL终止符
        char * pstr = (char*)calloc(1, strlen(token) + 1);
        strcpy(pstr, token);
        tokens[token_count] = pstr;//保存申请的堆空间首地址
        token_count++;
        token = strtok(NULL, delimiter); // 继续获取下一个token
    }
    // 添加NULL终止符
    tokens[token_count] = NULL;
    *pcount= token_count;
}

void freeStrs(char * pstrs[], int count)
{
    for(int i = 0; i < count; ++i) {
        free(pstrs[i]);
    }
}

│   ├── task_queue.c   线程任务队列实现

#include "thread_pool.h"
#include <pthread.h>


int queueInit(task_queue_t * que)
{
    if(que) {
        que->pFront = NULL;
        que->pRear = NULL;
        que->queSize = 0;
        que->flag = 1;
        int ret = pthread_mutex_init(&que->mutex, NULL);
        THREAD_ERROR_CHECK(ret, "pthread_mutex_init");

        ret = pthread_cond_init(&que->cond, NULL);
        THREAD_ERROR_CHECK(ret, "pthread_cond_init");
    }
    return 0;
}

int queueDestroy(task_queue_t * que)
{
    if(que) {
        int ret = pthread_mutex_destroy(&que->mutex);
        THREAD_ERROR_CHECK(ret, "pthread_mutex_destroy");

        ret = pthread_cond_destroy(&que->cond);
        THREAD_ERROR_CHECK(ret, "pthread_cond_destroy");
    }
    return 0;
}

int queueIsEmpty(task_queue_t * que)
{
    return que->queSize == 0;
}

int taskSize(task_queue_t * que)
{
    return que->queSize;
}

int taskEnque(task_queue_t * que, task_t * ptask)
{
    int ret = pthread_mutex_lock(&que->mutex);
    THREAD_ERROR_CHECK(ret, "pthread_mutex_lock");
    if(queueIsEmpty(que)) {
        que->pFront = que->pRear = ptask;
    } else {//不为空
        que->pRear->pNext = ptask;
        que->pRear = ptask;
    }
    que->queSize++;
    ret = pthread_mutex_unlock(&que->mutex);
    THREAD_ERROR_CHECK(ret, "pthread_mutex_unlock");
    //通知消费者取任务
    ret = pthread_cond_signal(&que->cond);
    THREAD_ERROR_CHECK(ret, "pthread_cond_signal");
    return 0;
}

//获取一个任务
task_t * taskDeque(task_queue_t * que)
{
    int ret = pthread_mutex_lock(&que->mutex);
    THREAD_ERROR_CHECK(ret, "pthread_mutex_lock");
    task_t * pret;
    while(que->flag && queueIsEmpty(que)) {//虚假唤醒
        pthread_cond_wait(&que->cond, &que->mutex);
    }
    if(que->flag) {
        //元素出队操作
        pret = que->pFront;
        if(taskSize(que) == 1) {
            que->pFront = que->pRear = NULL;
        } else {
            que->pFront = que->pFront->pNext;
        }
        que->queSize--;
    } else {
        pret = NULL;
    }
    ret = pthread_mutex_unlock(&que->mutex);
    THREAD_ERROR_CHECK(ret, "pthread_mutex_unlock");
    return pret;
}

//主线程调用
int broadcastALL(task_queue_t * que)
{
    //先修改要退出的标识位
    que->flag = 0;
    int ret = pthread_cond_broadcast(&que->cond);
    THREAD_ERROR_CHECK(ret, "pthread_cond_broadcast");

    return 0;
}

│   ├── thread_pool.h/c    线程池(超多)

#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <error.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <sys/uio.h>
#include <sys/sendfile.h>

#define SIZE(a) (sizeof(a)/sizeof(a[0]))

typedef void (*sighandler_t)(int);

#define ARGS_CHECK(argc, num)   {\
    if(argc != num){\
        fprintf(stderr, "ARGS ERROR!\n");\
        return -1;\
    }}

#define ERROR_CHECK(ret, num, msg) {\
    if(ret == num) {\
        perror(msg);\
        return -1;\
    }}

#define THREAD_ERROR_CHECK(ret, func) {\
    if(ret != 0) {\
        fprintf(stderr, "%s:%s\n", func, strerror(ret));\
    }}

typedef enum {
    CMD_TYPE_PWD=1,
    CMD_TYPE_LS,
    CMD_TYPE_CD,
    CMD_TYPE_MKDIR,
    CMD_TYPE_RMDIR,
    CMD_TYPE_PUTS,
    CMD_TYPE_GETS,
    CMD_TYPE_NOTCMD,  //不是命令

    TASK_LOGIN_SECTION1 = 100,
    TASK_LOGIN_SECTION1_RESP_OK,
    TASK_LOGIN_SECTION1_RESP_ERROR,
    TASK_LOGIN_SECTION2,
    TASK_LOGIN_SECTION2_RESP_OK,
    TASK_LOGIN_SECTION2_RESP_ERROR,
}CmdType;


typedef struct 
{
    int len;//记录内容长度
    CmdType type;
    char buff[1000];//记录内容本身
}train_t;

typedef struct task_s{
    int peerfd;//与client进行通信的套接字
    int epfd;//epoll的实例
    CmdType type;
    char data[1000];
    struct task_s * pNext;
}task_t;

typedef struct task_queue_s
{
    task_t * pFront;
    task_t * pRear;
    int queSize;//记录当前任务的数量
    pthread_mutex_t mutex; 
    pthread_cond_t cond;
    int flag;//0 表示要退出,1 表示不退出

}task_queue_t;

typedef struct threadpool_s {
    pthread_t * pthreads;
    int pthreadNum;
    task_queue_t que;//...任务队列
}threadpool_t;

int queueInit(task_queue_t * que);
int queueDestroy(task_queue_t * que);
int queueIsEmpty(task_queue_t * que);
int taskSize(task_queue_t * que);
int taskEnque(task_queue_t * que, task_t * task);
task_t * taskDeque(task_queue_t * que);
int broadcastALL(task_queue_t* que);

int threadpoolInit(threadpool_t *, int num);
int threadpoolDestroy(threadpool_t *);
int threadpoolStart(threadpool_t *);
int threadpoolStop(threadpool_t *);


int tcpInit(const char * ip, const char * port);
int addEpollReadfd(int epfd, int fd);
int delEpollReadfd(int epfd, int fd);
int transferFile(int sockfd);
int sendn(int sockfd, const void * buff, int len);
int recvn(int sockfd, void * buff, int len);

//处理客户端发过来的消息
void handleMessage(int sockfd, int epfd, task_queue_t * que);
//执行任务的总的函数
void doTask(task_t * task);
//每一个具体命令的执行
void cdCommand(task_t * task);
void lsCommand(task_t * task);
void pwdCommand(task_t * task);
void mkdirCommand(task_t * task);
void rmdirCommand(task_t * task);
void notCommand(task_t * task);
void putsCommand(task_t * task);
void getsCommand(task_t * task);

//用户登录的操作
void userLoginCheck1(task_t * task);
void userLoginCheck2(task_t * task);

#endif
#include "thread_pool.h"
#include <bits/pthreadtypes.h>

//每一个子线程在执行的函数执行体(start_routine)
void * threadFunc(void* arg)
{
    //不断地从任务队列中获取任务,并执行
    threadpool_t * pThreadpool = (threadpool_t*)arg;
    while(1) {
        task_t * ptask = taskDeque(&pThreadpool->que);
        if(ptask) {
            //执行业务逻辑
            doTask(ptask);
            free(ptask);//执行完任务后,释放任务节点
        } else {//ptask为NULL的情况
            break;
        }
    }
    printf("sub thread %ld is exiting.\n", pthread_self());
    return NULL;
}


int threadpoolInit(threadpool_t * pthreadpool, int num)
{
    pthreadpool->pthreadNum = num;
    pthreadpool->pthreads = calloc(num, sizeof(pthread_t));
    queueInit(&pthreadpool->que);

    return 0;
}

int threadpoolDestroy(threadpool_t * pthreadpool)
{
    free(pthreadpool->pthreads);
    queueDestroy(&pthreadpool->que);
    return 0;
}

int threadpoolStart(threadpool_t * pthreadpool)
{
    if(pthreadpool) {
        for(int i = 0; i < pthreadpool->pthreadNum; ++i) {
            int ret = pthread_create(&pthreadpool->pthreads[i],
                           NULL,
                           threadFunc, pthreadpool);
            THREAD_ERROR_CHECK(ret, "pthread_create");
        }
    }
    return 0;
}

int threadpoolStop(threadpool_t * pthreadpool)
{
    //如果任务队列中还有任务,先等待一下,所有任务执行完毕之后
    //再发广播,退出
    while(!queueIsEmpty(&pthreadpool->que))
    {
        sleep(1);//每一个线程都可以sleep
    }
    
    //发广播, 通知所有的子线程退出
    broadcastALL(&pthreadpool->que);
    //回收所有子线程的资源
    for(int i = 0; i < pthreadpool->pthreadNum; ++i) {
        pthread_join(pthreadpool->pthreads[i], NULL);
    }
    return 0;
}

│   ├── user.h/c   登录检查

#ifndef __USER_H__
#define __USER_H__

typedef enum {
    STATUS_LOGOFF = 0,
    STATUS_LOGIN
}LoginStatus;

typedef struct {
    int sockfd;//套接字文件描述符
    LoginStatus status;//登录状态
    char name[20];//用户名(客户端传递过来的)
    char encrypted[100];//从/etc/shadow文件中获取的加密密文
    char pwd[128];//用户当前路径

}user_info_t;

void loginCheck1(user_info_t * user);
void loginCheck2(user_info_t * user, const char * encrypted);

#endif

#include "user.h"
#include "thread_pool.h"
#include <stdio.h>
#include <string.h>
#include <shadow.h>

static void get_setting(char *setting,char *passwd)
{
    int i,j;
    //取出salt,i 记录密码字符下标,j记录$出现次数
    for(i = 0,j = 0; passwd[i] && j != 4; ++i) {
        if(passwd[i] == '$')
            ++j;
    }
    strncpy(setting, passwd, i);
}

void loginCheck1(user_info_t * user)
{
    printf("loginCheck1.\n");
    train_t t;
    int ret;
    memset(&t, 0, sizeof(t));
    struct spwd * sp = getspnam(user->name);
    if(sp == NULL) {// 用户不存在的情况下
        t.len = 0;   
        t.type = TASK_LOGIN_SECTION1_RESP_ERROR;
        ret = sendn(user->sockfd, &t, 8);
        printf("check1 send %d bytes.\n", ret);
        return;
    }
    //用户存在的情况下
    char setting[100] = {0};
    //保存加密密文
    strcpy(user->encrypted, sp->sp_pwdp);
    //提取setting
    get_setting(setting, sp->sp_pwdp);
    t.len = strlen(setting);
    t.type = TASK_LOGIN_SECTION1_RESP_OK;
    strncpy(t.buff, setting, t.len);
    //发送setting
    ret = sendn(user->sockfd, &t, 8 + t.len);
    printf("check1 send %d bytes.\n", ret);
}

void loginCheck2(user_info_t * user, const char * encrypted)
{
    /* printf("loginCheck2.\n"); */
    int ret;
    train_t t;
    memset(&t, 0, sizeof(t));
    if(strcmp(user->encrypted, encrypted) == 0) {
        //登录成功
        user->status = STATUS_LOGIN;//更新用户登录成功的状态
        t.type = TASK_LOGIN_SECTION2_RESP_OK;
        t.len = strlen("/server/$ ");// 暂定将 /server/ 作为pwd传递给client 
        strcpy(t.buff, "/server/$ ");
        ret = sendn(user->sockfd, &t, 8 + t.len);
        printf("Login success.\n");
    } else {
        //登录失败, 密码错误
        t.type = TASK_LOGIN_SECTION2_RESP_ERROR;
        printf("Login failed.\n");
        ret = sendn(user->sockfd, &t, 8);
    }
    printf("check2 send %d bytes.\n", ret);
    return;
}

│   ├── bussiness.c   业务合集

#include "linked_list.h"
#include "thread_pool.h"
#include "user.h"

//外部变量(userList是在main.c文件中定义的)
extern ListNode * userList;

//主线程调用:处理客户端发过来的消息
void handleMessage(int sockfd, int epfd, task_queue_t * que)
{
    //消息格式:cmd content
    //1.1 获取消息长度
    int length = -1;
    int ret = recvn(sockfd, &length, sizeof(length));
    if(ret == 0) {
        goto end;
    }
    printf("\n\nrecv length: %d\n", length);

    //1.2 获取消息类型
    int cmdType = -1;
    ret = recvn(sockfd, &cmdType, sizeof(cmdType));
    if(ret == 0) {
        goto end;
    }
    printf("recv cmd type: %d\n", cmdType);

    task_t *ptask = (task_t*)calloc(1, sizeof(task_t));
    ptask->peerfd = sockfd;
    ptask->epfd = epfd;
    ptask->type= cmdType;
    if(length > 0) {
        //1.3 获取消息内容
        ret = recvn(sockfd, ptask->data, length);
        if(ret > 0) {
            //往线程池中添加任务
            if(ptask->type == CMD_TYPE_PUTS) {
                //是上传文件任务,就暂时先从epoll中删除监听
                delEpollReadfd(epfd, sockfd);
            }
            taskEnque(que, ptask);
        }
    } else if(length == 0){
        taskEnque(que, ptask);
    }
end:
    if(ret == 0) {//连接断开的情况
        printf("\nconn %d is closed.\n", sockfd);
        delEpollReadfd(epfd, sockfd);
        close(sockfd);
        deleteNode2(&userList, sockfd);//删除用户信息
    }
}

//注意:此函数可以根据实际的业务逻辑,进行相应的扩展
//子线程调用
void doTask(task_t * task)
{
    assert(task);
    switch(task->type) {
    case CMD_TYPE_PWD:  
        pwdCommand(task);   break;
    case CMD_TYPE_CD:
        cdCommand(task);    break;
    case CMD_TYPE_LS:
        lsCommand(task);    break;
    case CMD_TYPE_MKDIR:
        mkdirCommand(task);  break;
    case CMD_TYPE_RMDIR:
        rmdirCommand(task);  break;
    case CMD_TYPE_NOTCMD:
        notCommand(task);   break;
    case CMD_TYPE_PUTS:
        putsCommand(task);   break;
    case CMD_TYPE_GETS:
        getsCommand(task);   break;
    case TASK_LOGIN_SECTION1:
        userLoginCheck1(task); break;
    case TASK_LOGIN_SECTION2:
        userLoginCheck2(task); break;
    }
}

//每一个具体任务的执行,交给一个成员来实现

void cdCommand(task_t * task)
{
    printf("execute cd command.\n");
}


void mkdirCommand(task_t * task)
{
    printf("execute mkdir command.\n");
}

void rmdirCommand(task_t * task)
{
    printf("execute rmdir command.\n");
}

void notCommand(task_t * task)
{
    printf("execute not command.\n");
}


void getsCommand(task_t * task) {
    printf("execute gets command.\n");
}


void userLoginCheck1(task_t * task) {
    printf("userLoginCheck1.\n");
    ListNode * pNode = userList;
    while(pNode != NULL) {
        user_info_t * user = (user_info_t *)pNode->val;
        if(user->sockfd == task->peerfd) {
            //拷贝用户名
            strcpy(user->name, task->data);
            loginCheck1(user);
            return;
        }
        pNode = pNode->next;
    }
}

void userLoginCheck2(task_t * task) {
    printf("userLoginCheck2.\n");
    ListNode * pNode = userList;
    while(pNode != NULL) {
        user_info_t * user = (user_info_t *)pNode->val;
        if(user->sockfd == task->peerfd) {
            //拷贝加密密文
            loginCheck2(user, task->data);
            return;
        }
        pNode = pNode->next;
    }
}

│   ├── transfer.c  交换文件

#include "thread_pool.h"
#define FILENAME "bigfile.avi"


int transferFile(int sockfd)
{
    //进行文件的发送
    //1. 先发送文件名
    //1.1 设置文件名的长度
    train_t t;
    memset(&t, 0, sizeof(t));
    t.len = strlen(FILENAME);
    strcpy(t.buff, FILENAME);
    send(sockfd, &t, 4 + t.len, 0);

    //2. 读取文件内容( 相对路径 )
    int fd = open(FILENAME, O_RDWR);
    ERROR_CHECK(fd, -1, "open");
    memset(&t, 0, sizeof(t));

    //2.1 获取文件的长度
    struct stat fileInfo;
    memset(&fileInfo, 0, sizeof(fileInfo));
    fstat(fd, &fileInfo);
    off_t length = fileInfo.st_size;
    printf("file length: %ld\n", length);

    //发送文件的长度
    sendn(sockfd, &length, sizeof(length));

    //借助于一条管道来搬运数据
    int fds[2];
    int ret = -1;
    int curSize = 0;
    pipe(fds);

    //发送文件内容
    while(curSize < length) {
        ret = splice(fd, NULL, fds[1], NULL, 4096, SPLICE_F_MORE);
        ret = splice(fds[0], NULL, sockfd, NULL, ret, SPLICE_F_MORE);
        curSize += ret;
    }
    printf("curSize:%d\n", curSize);
    close(fd);
    return 0;
}

│   ├── ls.c  指令接口,过

/*
 *@author lwh created
 *
 *
 *
 */
#include "thread_pool.h"

void lsCommand(task_t * task)
{
    printf("execute ls command.\n");
}

│   ├── main.c   启动启动,监听

#include "config.h"
#include "linked_list.h"
#include "thread_pool.h"
#include "user.h"

#define EPOLL_ARR_SIZE 100

int exitPipe[2];
//定义存储用户信息的链表
ListNode * userList = NULL;

void sigHandler(int num)
{
    printf("\n sig is coming.\n");
    //激活管道, 往管道中写一个1
    int one = 1;
    write(exitPipe[1], &one, sizeof(one));
}

int main(int argc, char ** argv)
{   
    //命令行参数要读取配置文件
    ARGS_CHECK(argc, 2);
    //创建匿名管道
    pipe(exitPipe);

    //fork之后,将创建了子进程
    pid_t pid = fork();
    if(pid > 0) {//父进程
        close(exitPipe[0]);//父进程关闭读端
        signal(SIGUSR1, sigHandler);
        wait(NULL);//等待子进程退出,回收其资源
        close(exitPipe[1]);
        printf("\nparent process exit.\n");
        exit(0);//父进程退出
    }
    //以下都是子进程中的执行流程
    close(exitPipe[1]);//子进程关闭写端

    //初始化hash表,用来存储配置信息
    HashTable ht;
    initHashTable(&ht);
    //读取服务器配置文件
    readConfig(argv[1], &ht);

    //创建线程池结构体
    threadpool_t threadpool;
    memset(&threadpool, 0, sizeof(threadpool));
    //初始化线程池
    threadpoolInit(&threadpool, atoi((const char*)find(&ht, THREAD_NUM)));
    //启动线程池
    threadpoolStart(&threadpool);

    //创建监听套接字
    int listenfd = tcpInit(find(&ht, IP), find(&ht, PORT));

    //创建epoll实例
    int epfd = epoll_create1(0);
    ERROR_CHECK(epfd, -1, "epoll_create1");

    //对listenfd进行监听
    addEpollReadfd(epfd, listenfd);
    addEpollReadfd(epfd, exitPipe[0]);


    struct epoll_event * pEventArr = (struct epoll_event*)
        calloc(EPOLL_ARR_SIZE, sizeof(struct epoll_event));
    while(1) {
        int nready = epoll_wait(epfd, pEventArr, EPOLL_ARR_SIZE, -1);
        if(nready == -1 && errno == EINTR) {
            continue;
        } else if(nready == -1) {
            ERROR_CHECK(nready, -1, "epoll_wait");
        } else {
            //大于0
            for(int i = 0; i < nready; ++i) {
                int fd = pEventArr[i].data.fd;
                if(fd == listenfd) {//对新连接进行处理
                    int peerfd = accept(listenfd, NULL, NULL);
                    printf("\n conn %d has conneted.\n", peerfd);
                    //userCheck();
                    //将新连接添加到epoll的监听红黑树上
                    addEpollReadfd(epfd, peerfd);
                    //添加用户节点
                    user_info_t * user = (user_info_t*)calloc(1, sizeof(user_info_t));
                    user->sockfd = peerfd;
                    appendNode(&userList, user);
                } else if(fd == exitPipe[0]) {
                    //线程池要退出
                    int howmany = 0;
                    //对管道进行处理
                    read(exitPipe[0], &howmany, sizeof(howmany));
                    //主线程通知所有的子线程退出
                    threadpoolStop(&threadpool);
                    //子进程退出前,回收资源
                    threadpoolDestroy(&threadpool);
                    close(listenfd);
                    close(epfd);
                    close(exitPipe[0]);
                    printf("\nchild process exit.\n");
                    destroyHashTable(&ht);
                    exit(0);
                } else {//客户端的连接的处理
                    handleMessage(fd, epfd, &threadpool.que);
                }
            }
        }
    }

    return 0;
}

│   ├── puts.c  puts指令接口

#include "thread_pool.h"

void putsCommand(task_t * task) {
    printf("execute puts command.\n");
    char filename[20] = {0};
    strcpy(filename, task->data);
    printf("filname: %s\n", filename);
    off_t len = 0;
    int ret = recvn(task->peerfd, &len, sizeof(len));
    printf("filelen: %ld.\n", len);

    //打开文件
    int fd = open(filename, O_CREAT|O_RDWR, 0644);
    if(fd < 0) {
        perror("open"); return;
    }
    //接收并写入文件
    char buff[1000] = {0};
    off_t left = len;
    while(left > 0) {
        if(left < 1000) {
            ret = recvn(task->peerfd, buff, left);
        } else {
            ret = recvn(task->peerfd, buff, sizeof(buff));
        }
        if(ret < 0) {
            break;
        }
        ret = write(fd, buff, ret);
        left -= ret;
    }
    close(fd);
    //上传任务执行完毕之后,再加回来
    addEpollReadfd(task->epfd, task->peerfd);
}

│   ├── pwd.c  指令接口,过


#include "thread_pool.h"

void pwdCommand(task_t * task)
{
    printf("execute pwd command.\n");
}

│   ├── server.c    服务端实现,连接监听

#include "thread_pool.h"

int tcpInit(const char * ip, const char * port)
{
    //1. 创建TCP的监听套接字
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("listenfd: %d\n", listenfd);
    if(listenfd < 0) {
        perror("socket");
        return -1;
    }
    int on = 1;
    int ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    if(ret < 0) {
        perror("setsockopt");
        close(listenfd);
        return -1;
    }

    //2. 绑定网络地址
    struct sockaddr_in serveraddr;
    //初始化操作,防止内部有脏数据
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;//指定IPv4
    serveraddr.sin_port = htons(atoi(port));//指定端口号
    //指定IP地址
    serveraddr.sin_addr.s_addr = inet_addr(ip);
    ret = bind(listenfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if(ret < 0) {
        perror("bind");
        close(listenfd);//关闭套接字
        return -1;
    }
    //3. 进行监听
    ret = listen(listenfd, 10);
    if(ret < 0) {
        perror("listen");
        close(listenfd);
        return -1;
    }
    printf("server start listening.\n");
    return listenfd;
}

int addEpollReadfd(int epfd, int fd)
{
    struct epoll_event evt;
    memset(&evt, 0, sizeof(evt));
    evt.data.fd = fd;
    evt.events = EPOLLIN;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &evt);
    ERROR_CHECK(ret, -1, "epoll_ctl");
    return 0;
}

int delEpollReadfd(int epfd, int fd)
{
    struct epoll_event evt;
    memset(&evt, 0, sizeof(evt));
    evt.data.fd = fd;
    evt.events = EPOLLIN;
    int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &evt);
    ERROR_CHECK(ret, -1, "epoll_ctl");
    return 0;
}

//其作用:确定发送len字节的数据
int sendn(int sockfd, const void * buff, int len)
{
    int left = len;
    const char * pbuf = buff;
    int ret = -1;
    while(left > 0) {
        ret = send(sockfd, pbuf, left, 0);
        if(ret < 0) {
            perror("send");
            return -1;
        }

        left -= ret;
        pbuf += ret;
    }
    return len - left;
}

//其作用:确定接收len字节的数据
int recvn(int sockfd, void * buff, int len)
{
    int left = len;//还剩下多少个字节需要接收
    char * pbuf = buff;
    int ret = -1;
    while(left > 0) {
        ret = recv(sockfd, pbuf, left, 0);
        if(ret == 0) {
            break;
        } else if(ret < 0) {
            perror("recv");
            return -1;
        }

        left -= ret;
        pbuf += ret;
    }
    //当退出while循环时,left的值等于0
    return len - left;
}

 test

    ├── getspnam.c

#include <func.h>
#include <shadow.h>

void help(void)
{
    printf("用户密码验证程序\n 第一个参数为用户名!\n");
    exit(-1);
}

void error_quit(char *msg)
{
    perror(msg);
    exit(-2);
}

void get_setting(char *salt,char *passwd)
{
    int i,j;
    //取出salt,i 记录密码字符下标,j记录$出现次数
    for(i = 0,j = 0;passwd[i]&& j!=4;++i)
    {
        if(passwd[i] == '$')
            ++j;
    }
    strncpy(salt,passwd,i);
}

int main(int argc,char *argv[])
{
    struct spwd *sp;
    char *passwd;
    char setting[512] = {0};
    if(argc != 2)
        help();
    passwd = getpass("请输入密码:");
    printf("passwd:%s\n", passwd);

    if((sp = getspnam(argv[1]))==NULL)
    {
        error_quit("获取用户名和密码");
    }

    get_setting(setting,sp->sp_pwdp);
    char * encoded = crypt(passwd, setting);
    printf("encrypted: %s\n", encoded);

    if(strcmp(sp->sp_pwdp, encoded)==0)
        printf("验证通过!\n");
    else
        printf("验证失败!\n");
    return 0;
}

    ├── jwt

    │   ├── decode.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <l8w8jwt/decode.h>

static const char KEY[] = "YoUR sUpEr S3krEt 1337 HMAC kEy HeRE";
static const char JWT[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1ODA5MzczMjksImV4cCI6MTU4MDkzNzkyOSwic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciJ9.7oNEgWxzs4nCtxOgiyTofP2bxZtL8dS7hgGXRPPDmwQWN1pjcwntsyK4Y5Cr9035Ro6Q16WOLiVAbj7k7TeCDA";

int main(void)
{
    struct l8w8jwt_decoding_params params;
    l8w8jwt_decoding_params_init(&params);

    params.alg = L8W8JWT_ALG_HS512;

    params.jwt = (char*)JWT;
    params.jwt_length = strlen(JWT);

    params.verification_key = (unsigned char*)KEY;
    params.verification_key_length = strlen(KEY);

    /* 
     * Not providing params.validate_iss_length makes it use strlen()
     * Only do this when using properly NUL-terminated C-strings! 
     */
    params.validate_iss = "Black Mesa"; 
    params.validate_sub = "Gordon Freeman";

    /* Expiration validation set to false here only because the above example token is already expired! */
    params.validate_exp = 1; 
    params.exp_tolerance_seconds = 60;

    params.validate_iat = 1;
    params.iat_tolerance_seconds = 60;

    enum l8w8jwt_validation_result validation_result;

    int decode_result = l8w8jwt_decode(&params, &validation_result, NULL, NULL);

    if (decode_result == L8W8JWT_SUCCESS && validation_result == L8W8JWT_VALID) 
    {
        printf("\n Example HS512 token validation successful! \n");
    }
    else
    {
        printf("\n Example HS512 token validation failed! \n");
    }
    
    /*
     * decode_result describes whether decoding/parsing the token succeeded or failed;
     * the output l8w8jwt_validation_result variable contains actual information about
     * JWT signature verification status and claims validation (e.g. expiration check).
     * 
     * If you need the claims, pass an (ideally stack pre-allocated) array of struct l8w8jwt_claim
     * instead of NULL,NULL into the corresponding l8w8jwt_decode() function parameters.
     * If that array is heap-allocated, remember to free it yourself!
     */

    return 0;
}

    │   ├── encode.c

#include <stdio.h>
#include <string.h>
#include <l8w8jwt/encode.h>

int main(void)
{
    char* jwt;
    size_t jwt_length;

    struct l8w8jwt_encoding_params params;
    l8w8jwt_encoding_params_init(&params);

    params.alg = L8W8JWT_ALG_HS512;

    params.sub = "Gordon Freeman";
    params.iss = "Black Mesa";
    params.aud = "Administrator";

    params.iat = l8w8jwt_time(NULL);
    params.exp = l8w8jwt_time(NULL) + 600; /* Set to expire after 10 minutes (600 seconds). */

    params.secret_key = (unsigned char*)"YoUR sUpEr S3krEt 1337 HMAC kEy HeRE";
    params.secret_key_length = strlen(params.secret_key);

    params.out = &jwt;
    params.out_length = &jwt_length;

    int r = l8w8jwt_encode(&params);

    printf("\n l8w8jwt example HS512 token: %s \n", r == L8W8JWT_SUCCESS ? jwt : " (encoding failure) ");

    /* Always free the output jwt string! */
    l8w8jwt_free(jwt);

    return 0;
}

    │   └── Makefile

    ├── Makefile

    ├── md5

    │   ├── Makefile

    │   ├── md5.c

#include <func.h>
#include <openssl/md5.h>

int main(int argc, char ** argv)
{
    int fd = open(argv[1], O_RDONLY);
    ERROR_CHECK(fd, -1, "open");
    char buff[1000] = {0};
    int ret = 0;
    
    MD5_CTX ctx;
    MD5_Init(&ctx);//1. 初始化MD5的结构体
    while(1) {
        memset(buff, 0, sizeof(buff));
        //2. 读取文件中的一段内容
        ret = read(fd, buff, sizeof(buff));
        if(ret == 0) {
            break;
        }
        //3. 对每一段内容调用更新函数
        MD5_Update(&ctx, buff, ret);
    }
    unsigned char md[16] = {0};
    MD5_Final(md, &ctx);//4. 结束
    char result[33] = {0};
    for(int i = 0; i < 16; ++i) {
        //printf("%2x", md[i]);
        char frag[3] = {0};
        sprintf(frag, "%02x", md[i]);
        strcat(result, frag);
    }
    printf("result:%s\n", result);

    return 0;
}

    │   └── test.txt

-- MySQL dump 10.13  Distrib 8.0.36, for Linux (x86_64)
--
-- Host: localhost    Database: 58th
-- ------------------------------------------------------
-- Server version	8.0.36-0ubuntu0.22.04.1

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `person`
--

DROP TABLE IF EXISTS `person`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `person` (
  `id` int NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  `balance` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `person`
--

LOCK TABLES `person` WRITE;
/*!40000 ALTER TABLE `person` DISABLE KEYS */;
/*!40000 ALTER TABLE `person` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `student`
--

DROP TABLE IF EXISTS `student`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `student` (
  `id` int NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  `chinese` int DEFAULT '60',
  `math` int DEFAULT '60',
  `english` int DEFAULT '60',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `student`
--

LOCK TABLES `student` WRITE;
/*!40000 ALTER TABLE `student` DISABLE KEYS */;
INSERT INTO `student` VALUES (1,'dijia','1998-08-09',85,70,60),(2,'taijia','1998-07-06',60,70,60),(3,'sailuo','2000-01-01',60,70,80),(4,'oubu','2002-02-02',50,60,60),(5,'leiou','2003-03-03',90,88,80),(6,'daina','2004-04-04',66,65,90),(7,'迪迦','2001-02-02',78,67,90),(9,'赛罗','1995-02-02',78,67,90),(10,'gaiya','2004-05-06',40,80,50),(11,'泰迦','2005-06-07',60,95,95),(12,NULL,'1997-08-09',60,30,20);
/*!40000 ALTER TABLE `student` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `student2`
--

DROP TABLE IF EXISTS `student2`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `student2` (
  `id` int DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  `chinese` int DEFAULT NULL,
  `math` int DEFAULT NULL,
  `english` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `student2`
--

LOCK TABLES `student2` WRITE;
/*!40000 ALTER TABLE `student2` DISABLE KEYS */;
/*!40000 ALTER TABLE `student2` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `student3`
--

DROP TABLE IF EXISTS `student3`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `student3` (
  `id` int DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  `chinese` int DEFAULT NULL,
  `math` int DEFAULT NULL,
  `english` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `student3`
--

LOCK TABLES `student3` WRITE;
/*!40000 ALTER TABLE `student3` DISABLE KEYS */;
/*!40000 ALTER TABLE `student3` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `torder`
--

DROP TABLE IF EXISTS `torder`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `torder` (
  `id` int DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `s_id` int DEFAULT NULL,
  KEY `fk_1` (`s_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `torder`
--

LOCK TABLES `torder` WRITE;
/*!40000 ALTER TABLE `torder` DISABLE KEYS */;
INSERT INTO `torder` VALUES (1,'变身器',12),(2,'光剑',3),(3,'头镖',6),(4,'双截棍',5),(5,'手枪',13),(6,'AK47',14);
/*!40000 ALTER TABLE `torder` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2024-06-11 14:35:06

    └── split_string.c

#include <func.h>

// 假设max_tokens是数组tokens的最大大小
// 在使用后,记得要释放空间
void splitString(const char * pstrs, char *tokens[], int max_tokens, int * pcount) {
    int token_count = 0;
    char *token = strtok((char *)pstrs, " "); // 使用空格作为分隔符

    while (token != NULL && token_count < max_tokens - 1) { // 保留一个位置给NULL终止符
        char * pstr = (char*)calloc(1, strlen(token) + 1);
        strcpy(pstr, token);
        tokens[token_count] = pstr;//保存申请的堆空间首地址
        token_count++;
        token = strtok(NULL, " "); // 继续获取下一个token
    }
    // 添加NULL终止符
    tokens[token_count] = NULL;
    *pcount= token_count;
}

void freeStrs(char * pstrs[], int count)
{
    for(int i = 0; i < count; ++i) {
        free(pstrs[i]);
    }
}


int main()
{
    char input[] = "A bird came down the walk";
    printf("Parsing the input string '%s'\n", input);
    char * strs[10] = {0};
    int cnt = 0;
    splitString(input, strs, 10, &cnt);
    for(int i = 0; i < cnt; ++i) {
        printf("strs[%d]: %s\n", i, strs[i]);
    }
    freeStrs(strs, cnt);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值