目录
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(¶ms);
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(¶ms, &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(¶ms);
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(¶ms);
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;
}