赶代码昨晚没来得及赶出来。从本地文件树到服务端整合文件树用了五六个小时真是难绷。
实现功能:客户端输入命令行,在服务端创建文件。新的文件与服务端exe文件同目录。
客户端各自操作自己的文件,两个客户端文件名相同,但是互不影响。原因就是文件前缀名在客户端隐藏了。
参考文件树过渡代码DevC++ C文件树 实现 用户文件前缀的自定义-CSDN博客
以及之前的通信样例DevC++ 用C语言的多线程 实现简单的客户端和服务器-CSDN博客
用户名
0到9任意一个数字
密码
和用户名相同
但是用户名密码输入错误也能跑,这里只是实现简单的提示,后续可以把提示printf()换成退出函数,用户名输入错误就直接退出程序啥的。
命令:
cf 创建.txt文件
cd 创建文件夹
st 显示文件树
dead 删除(隐藏节点)
stwd 显示文件树,连同之前删除(隐藏)的文件 ?(show tree with hide)
sthd 显示文件树,隐藏之前删除(隐藏)的文件
close退出
?
效果图
代码改写分了两个阶段
一个是就着privaterootname参数改,发现嵌入服务端,每个函数的printf()都需要替换成send(),就初步增加函数参数目标客户端sClient,结果尝试了两次,发现改动太大,想想能不能偷懒点。中途被其他事情打断,一边在开会的时候一边想,重复演绎代码的发展历程。然后意识到把参数用一个结构体包装,这样表面上就不用再新加入参数了。通过结构体进行传输数据,实现少改动函数声明和函数头。
写结构体打包参数的时候又想起来类似全局变量,有个静态变量,C语言里是static类型,好像可以实现函数不传参数,直接访问,而更外部的服务器却不能访问的这个变量。但是没有来得及试试。只能说下次偷懒验证一下静态变量这个效果,也就是所谓的作用域。可以改代码的时候参考其他先生的博客静态局部变量和全局变量的区别!_静态局部变量和静态全局变量的区别-CSDN博客
另一个阶段就是数据send()rec(),替换printf()和scanf()函数等字符串处理问题:解决传输数据重叠,旧的数据不自动清空导致的重复输出,数据突然冒出数码,原来是旧的数据没覆盖完全。纯纯的printf到处检测数据。一点一点缩小范围。然后意识到所有的暂存的旧数据都要清空。关于字符串一点就是printf("%s",a),是读到‘\0’就停止输出,但是数据传输不是读到'\0'就停止传输。于是就是这个默认之下,我把传输长度设置为1400字节,导致没覆盖的旧数据也传输过来了。
客户端也增加了一个线程,用于持续监测读入数据。原来的读一次接受一次数据,得读入之后然后才提示输出数据,这样一个办完之前不能办下一个的阻塞情况也让当时调试时确认不出来问题,把问题的范围扩大了才意识到原来是客户端代码还没改,客户端接受数据但是得等scanf输入数据之后才轮到打印上个信息emmm.
服务端代码,复制粘贴就能跑。如果还有dll缺失问题详见DevC++ 多线程创建与删除与exe文件脱离DevC++运行中发现dll文件和exe文件的关系-CSDN博客
一脉相承的代码,一脉相承的解决方案。?
实现了数据传输之后进行执行,客户端象征性的用户名输入函数检验,可以后续改成注册,登录检查。文件创建,删除,和前缀增加,也可以用于实现局域网上传文件,可以有局域网网盘存储的开发方向。
代码改的时候跨度有点大,一开始还没确认参数都有啥就直接复制粘贴过来,然后一有了新的参数,嵌套的函数也跟着改头大。但是可以先自己想象一下自己写要怎么改,想想自己可能遇到的困难,然后再对照DevC++ C文件树 实现 用户文件前缀的自定义-CSDN博客看看鄙人当时遇到意料之外的情况。BUG一定得接触,但是可以换个方式接触,知道如何察觉矛盾就行。自然而然地遇着矛盾,遇上的矛盾有点大遭不住就可以临时妥协一下,找找别人解决矛盾的方案的历史过程,然后再解决矛盾。
完整服务端代码,客户端得使劲往下翻。遇到半页A4大小的空白就到客户端代码了。
#include <stdio.h>
#include <winsock2.h>
#include<pthread.h>
#include<string.h>
#include<conio.h>
#include<io.h>
#pragma comment(lib,"ws2_32.lib")
typedef struct ThreadNode {
int index;
pthread_t* Thread;
SOCKET Client;
struct ThreadNode * next;
ThreadNode() {
index = 0;
this->next = NULL;
}
} hThread;
hThread *clientHeadNote, *clientEndNote;
hThread * addClient() {
hThread * ClientNote = new hThread();
return ClientNote;
}
//解决13.里面的删除标记失败以及新增显示已删除的文件夹文件
//child->child->bro解决多个同级文件夹插入file文件,不能确认母节点问题
//解决文件加入a->child,实现子文件
//lenth-tab,解决文件打印竖线 0开始,%5=1打印,5倒数 +=5
//typedef struct paper{
// char name[50];
// int isfile;
// paper* next;
//}paper;
//
//
//typedef struct dictionary{
// char name[50];
// int isfile;
node* parent;
// dictionary* child;
// paper* pa;
//};
//由于一个节点下面,既要有文件,也要有文件夹,所以file->child既要能指向文件类型,也要指向文件夹类型。所以只能是这里同一个节点类型,因为兼容类型还不会写。
//同一个节点类型,但是区分不同使用,要在节点里加上标志位flag ——is_dictionary
int lenth = 20;
//文件类型后缀统一
typedef struct node {
// 文件树节点结构体
char name[100];
// 节点名称,和路径不同,路径自动加入前缀,藏进了path里,printf 出来 可了解成员存储的内容
char path[100];
// 10.版本出现文件创建,需要相对路径 ,path存储到上一级文件名+./,因为再加当前文件名,同级文件就会塞进下一个文件里
int is_dictionary;
// 标志1:节点是文件夹:1,否则=0
int is_dead = 0;
// 标志2:节点是标记删除的节点:1否则=0
node* child;
node* parent;
node* bro;
} node;
node *root;
typedef struct user {
char acount[100];
char code[100];
} user;
//这是改secret_前缀的测试思路,成功穷举出矛盾。这个函数属于零起点,遇到矛盾,自然而然的理解,主要是找到这个并记录自然而然
typedef struct mess {
char privaterootname[255];
SOCKET sClient;
char ch[255];
char said[255];
char tree[1400];
} mess;
//封装了数据接收数据,函数参数整合在一起
int fun(char*filepath);
void checkloginv2(SOCKET sClient, char a[], char b[]);
void initv2(node* rootv2, char privaterootname[]);
//void registname(char privaterootname[]);
void registname(char headname[], char privaterootname[]);
//void checkorder(node* rootv2, char ch[], char privaterootname[]);
void checkorder(node * rootv2, mess* order, char privaterootname[]);
void addbro(node* parent, node* a);
void addChildv2(node* parent, node* a, char privaterootname[]);
void addFilev2(node* parent, char a[], char privaterootname[]);
void addDictionaryv2(node* parent, char a[], char privaterootname[]);
void findandaddv2(node* list, int floor, char name[], char have[], char privaterootname[]);
void findadd_dirv2(node* list, int floor, char name[], char have[], char privaterootname[]);
void findanddead(node* list, mess* order);
void deadaddsign_v2(node* list);
void show(node* list, int floor, int head, mess* order);
void showhidedead(node* list, int floor, int head, mess* order);
void showwithdead(node* list, int floor, int head, mess* order);
每个建立链接的客户端分配一个函数,每个线程运行一个这样的函数
//void* ThreadClient(void*) {
// if (clientEndNote == NULL) {
// printf("empty Link\n");
// return 0;
// }
// char revData[255];
// SOCKET sClient = clientEndNote->Client;
// printf("client logged in \n");
// while (1) {
// //接收数据
// int ret = recv(sClient, revData, 255, 0);
// if (ret > 0) {
// revData[ret] = 0x00;
// printf(revData);
puts(0);
// printf("\n");
新功能:加入客户端控制签退
// if (strcmp("log_out", revData) == 0) {
// char a[255];
// strcpy(a, "已注销\n");
// send(sClient, a, 255, 0);
反馈:向客户端sClienr发送“已注销”消息,发送长度为255个字节,0是指不阻塞。
printf("签退成功\n");
// closesocket(sClient);
// printf("签退成功\n");
//
// } else {
// char a[255];
// strcpy(a, "服务器已收到消息\n");
// send(sClient, a, 255, 0);
// }
// }
// }
// closesocket(sClient);
//}
//每个建立链接的客户端分配一个函数,每个线程运行一个这样的函数
void* ThreadClient(void*) {
mess* order = (mess*)malloc(sizeof(mess));
strcpy(order->ch, "");
strcpy(order->privaterootname, "");
// char privaterootname[100] ;
node* rootv2 = (node*)malloc(sizeof(node));
// char ch[100] = {""};
char a[100] = "";
char b[100] = "";
if (clientEndNote == NULL) {
printf("empty Link\n");
return 0;
}
char revData[255] = "";
SOCKET sClient = clientEndNote->Client;
order->sClient = sClient;
printf("client logged in \n");
send(sClient, "input user name:\n", 255, 0);
int ret = recv(sClient, revData, 255, 0);
printf("阻塞成功\n"); //就是接收到数据才继续往下,否则就停留在这里。
// 客户端和服务端的255后面那个0,要一样,客户端为0,服务端为0,就不会收到消息
strcpy(a, revData);
strcpy(revData, "");
send(sClient, "input user secret code\n", 255, 0);
ret = recv(sClient, revData, 255, 0);
strcpy(b, revData);
strcpy(revData, "");
checkloginv2(sClient, a, b);
//如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm..
printf("请输入用户空间名\n");
send(sClient, "请输入用户空间名\n", 255, 0);
ret = recv(sClient, revData, 255, 0);
printf("收到客户端命名空间\n");
char headname[100];
strcpy(headname, revData);
strcpy(revData, "");
registname(headname, order->privaterootname);
initv2(rootv2, order->privaterootname);
// printf("rootv2 path is%s", rootv2->path);
char a2[100] = "n";
char b2[100] = "nwoevi";
// printf("add dic\n");
addDictionaryv2(rootv2, a2, order->privaterootname);
// printf("dictionary ok!\n");
send(sClient, "dictionary ok!\n", 255, 0);
addFilev2(rootv2, b2, order->privaterootname);
// printf("File b ok!\n");
send(sClient, "File b ok!\n", 255, 0);
char c[100] = "3894";
addFilev2(rootv2, c, order->privaterootname);
// printf("File 3849 ok!\n");
send(sClient, "File 3849 ok!\n", 255, 0);
char d[100] = "floor2";
addDictionaryv2(rootv2, d, order->privaterootname);
char k[100] = "vnsn";
findadd_dirv2(rootv2, 0, a2, k, order->privaterootname);
char f[100] = "ij";
findandaddv2(rootv2, 0, k, f, order->privaterootname);
strcpy(order->tree, "");
show(rootv2, 0, 10, order);
// send(order->sClient, order->tree, 1400, 0);
send(order->sClient, order->tree, strlen(order->tree), 0);
strcpy(order->tree, "");
// 需要延时,否则就会频繁传送,导致乱码或者数据传不对
Sleep(200);
send(sClient, "input order\n", 255, 0);
while (1) {
//接收数据
strcpy(revData, "");
int ret = recv(sClient, revData, 255, 0);
for (int i = 0; i < 255; i++) {
order->said[i] = '\0';
}
if (ret > 0) {
revData[ret] = 0x00;
printf(revData);
printf("\n");
// 新功能:加入客户端控制签退
if (strcmp("log_out", revData) == 0) {
// char a[255];
strcpy(a, "已注销\n");
send(sClient, a, 255, 0);
// 反馈:向客户端sClienr发送“已注销”消息,发送长度为255个字节,0是指不阻塞。
// printf("签退成功\n");
closesocket(sClient);
printf("签退成功\n");
} else {
// char a[255];
strcpy(a, "服务器已收到消息\n");
send(sClient, a, 255, 0);
strcpy(order->ch, revData);
// scanf("%s", ch);
// if (strcmp("close", ch) == 0) {
// break;
// } else {
// checkorder(rootv2, ch, privaterootname);
// }
checkorder(rootv2, order, order->privaterootname);
}
}
}
findandaddv2(rootv2, 0, "n", "newnode", order->privaterootname);
show(rootv2, 0, 10, order);
closesocket(sClient);
}
//以上是多线程相关的函数
//以下是文件系统函数,函数体在主函数之后
//这个函数解决了如何判断前缀./的封装程度
void addprofix(char a[]) {
char *b = "./mysecret_root./";
// 根节点加前缀
// 非根节点加后缀
// 只输入文件名,其他对外隐藏
char *c = "./";
// addbro ,在本层文件里,加在前缀
// 如 n->./mysecret_root./n
// p->./mysecret_root./p
// addchild,在下层文件里, 加后缀上
// p->./mysecret_root./->./mysecret_root./n./p
// 后缀的./也一起存进去。名称之前,封装为路径,孩子之后,复制自己名称,在加入./作为路径。
// addchild,在下层文件里, 上级文件名的加后缀上./
// p->./mysecret_root./->./mysecret_root./n./ p
// 每个节点存上一个路径,不存自己的名字,
// 添加兄弟就直接复制粘贴,
// 添加孩子就再补充自己的名字,然后进入下一条。自然而然。
// 默认创建在本地同文件里的mysecret_root文件里
// 且直接输入名字即可创建
// char a[100];
// scanf("%s", a);
char m[100] = {0};
strcat(m, b);
strcat(m, a);
printf("%s\n", m);
}
int main(int argc, char* argv[]) {
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0) {
return 0;
}
//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET) {
printf("socket error !");
return 0;
}
//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
// 端口和客户端端口要相同,否则链接建立不上
// sin.sin_port = htons(8880);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) {
printf("bind error !");
}
//开始监听
if (listen(slisten, 5) == SOCKET_ERROR) {
printf("listen error !");
return 0;
}
//这里是多线程服务器,接受多个客户端,可以对比单线程看看多了什么,哪些函数的位置调整了
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
while (true) {
printf("等待连接...\n");
SOCKET sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET) {
printf("accept error !");
continue;
}
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
if (clientHeadNote == NULL) {
clientHeadNote = addClient();
// printf("node creat\n");
clientEndNote = clientHeadNote;
}
// 必须要先创建给end节点,然后再创建线程,因为线程的参数通过全局变量传入,而不是通过线程生成函数传入
// 原来线程参数除了 pthread_create()还能通过全局变量传参数
hThread* clientnode = (hThread*)malloc(sizeof(hThread));
clientnode->Client = sClient;
clientnode->next = NULL;
clientHeadNote->next = clientnode;
clientEndNote = clientnode;
printf("endnode is created\n");
clientnode->Thread = (pthread_t*)malloc(sizeof(pthread_t));
clientnode->index = pthread_create(clientnode->Thread, NULL, ThreadClient, NULL);
printf("线程创建成功\n");
}
printf("server is closing\n");
hThread * p;
while (clientHeadNote) { //释放占用的内存空间
p = clientHeadNote;
clientHeadNote = clientHeadNote->next;
delete p;
}
closesocket(slisten);
WSACleanup();
return 0;
}
//以下是文件树的代码,对应17.文件树
int fun(char*filepath) {
int res;
//filepath为绝对路径
//如果文件夹不存在
if (_access(filepath, 0) != 0)
//创建文件夹
res = mkdir(filepath);// 返回 0 表示创建成功,-1 表示失败
//remove(filename) 删除文件
else
res = 1;
return res;
}
//map字母数字负责映射,或者符号数字负责映射
//然后switch case来直接跳转判断速度
void initv2(node * rootv2, char privaterootname[]) {
// rootv2 = (node*)malloc(sizeof(node)); 如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm.
// strcpy(root->path, "./mysecret_root");
char *a = "./";
char path[100] = {""};
strcat(path, a);
strcat(path, privaterootname);
strcat(path, "_root"); //这是改出来的代码新增的一行/
strcpy(rootv2->path, path);
strcpy(rootv2->name, "root");
rootv2->is_dead = 0;
rootv2->parent = NULL;
rootv2->child = NULL;
rootv2->bro = NULL;
rootv2->is_dictionary = 1;
printf("%s\n", rootv2->path);
// int flag = fun("./mysecret_root");
// int flag = fun(root->path);
// if (flag == -1)
// printf("创建失败\n");
// else if (flag == 0)
// printf("创建成功\n");
// else if (flag == 1)
// printf("文件夹已存在\n");
int flag = fun(rootv2->path);
if (flag == -1)
printf("磁盘根文件创建失败\n");
else if (flag == 0)
printf("磁盘根文件创建成功\n");
else if (flag == 1)
printf("磁盘根文件夹已存在\n");
}
void addbro(node * parent, node * a) {
if (parent->bro == NULL) {
parent->bro = a;
} else {
node* bro = parent->bro;
while (bro->bro != NULL) {
bro = bro->bro;
}
bro->bro = a;
}
}
//void addChild(node* parent, node* a)
//跳转前的内存位置,取得改内存里跳转后的新内存地址
//void addChild(node* &parent, node* a)
//不进行跳转获取原位置,因为节点永远存在不为空
void addChildv2(node * parent, node * a, char privaterootname[]) {
// printf("addChild path is a%s\n",a->path); //debug记录:如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm.
// printf("parent path is %s",parent->path); //debug记录:如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm.
strcpy(a->path, parent->path); //path是上级文件,所以下面需要./继续深入
// 人脑跑代码,跑到第二次发现,名字后面缺了./
// 意识到如果./在根文件开始,就会一直有这个二次文件沿用一次文件,复制粘贴一次,缺少下级./情况
// 于是按照二次文件改,看看这样在根文件创建的时候能不能适配
// 发现新创建的文件可以直接加上下级指令,根文件最后是名字,然后下次创建子文件,在子文件上在加上./
strcat(a->path, "./"); //下层指令,此时才算进入下层
strcat(a->path, privaterootname);
strcat(a->path, "_");
// strcat(a->path, "mysecret_"); 替换固定头文件。 其他不思考。 回头再按之前的mysecret_实现的思路重复一遍,加前缀后重复一遍,留前缀去后缀替换后再重复一遍。
strcat(a->path, a->name);
//古早注释在新的需求下有了新的含义,复用穷举法
// 下面三次循环可以看成从根节点开始,连续三次创建文件,三层文件名都间隔了一个./符合路径格式,一切正常
// strcat(a->path, "./"); //下层指令
// strcat(a->path, a->name);
//
//
// strcat(a->path, "./"); //下层指令
// strcat(a->path, a->name);
//
// strcat(a->path, "./"); //下层指令
// strcat(a->path, a->name);
// printf("check add head now a is %s\n", a->path); //debug记录:如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm.
if (a->is_dictionary) {
fun(a->path);
} else {
strcat(a->path, ".txt");
FILE* fp;
fp = fopen(a->path, "w");
fclose(fp);
}
if (parent->child == NULL) {
parent->child = a;
} else {
node* bro = parent->child;
while (bro->bro != NULL) {
bro = bro->bro;
}
// bro->bro = NULL;
bro->bro = a;
}
}
//缩进输出文件类型名,只能减少,不能增加,增加就会都增加,减少可以选择性减少
//增加就会先算出最深的,然后算出距离差,然后再加上,不如一遍算缩进,还剩下多少,直接打印没有被占用的
//void format(node *a, int tab, int see) {
// printf("%-18s", a->name);
// for (int i = 0; i < see - tab; i++) {
// printf(" ");
// }
// printf("%-9s", "dictionary");
//
//}
void addFilev2(node * parent, char a[], char privaterootname[]) {
node* p = (node*)malloc(sizeof(node));
strcpy(p->name, a);
p->is_dictionary = 0;
p->is_dead = 0;
p->bro = NULL;
p->child = NULL;
p->parent = NULL;
// addbro(parent, p);
// printf("add function is running\n");
addChildv2(parent, p, privaterootname); //重写的另一种情况:从注释一行代码到 复制粘贴代码块,代码块里面的函数直接加新的参数。
// 核心函数增加参数 ,其他嵌套函数增加相同参数,可以借此形成线索,这个新思想的背景,则是来源于相同代码的复制粘贴,修改参数。
// 从改一个函数一个参数到改两个函数一个参数
//从改两个函数一个参数到改两个嵌套函数一个参数
//再到新增功能,新增自定义功能时,各个函数新增相同参数作为控制
//再到模块移植,从一个功能实现到模块化,就是把原来的主函数变成某个小函数,原来主函数需要的变量,全部放进小函数里。
//这就是新功能实现的无中生有的流程,纯纯一个一个函数加参数 ,改的多了,规模大了,遇着的函数与函数之间的联系,自然而然描述出来修改思路。
// printf("success creat file\n");
}
void addDictionaryv2(node * parent, char a[], char privaterootname[]) {
node* p = (node*)malloc(sizeof(node));
strcpy(p->name, a);
p->is_dictionary = 1;
p->is_dead = 0;
p->bro = NULL;
p->child = NULL;
p->parent = NULL;
// addbro(parent, p);
// printf("add function is running\n"); //debug记录:如果rootv2 是空的,在函数内部初始化时,数据就不能传出去emm.
addChildv2(parent, p, privaterootname);
// printf("success creat dictionary\n");
}
//先就着基本有的直接敲上,然后打印递归的时候发现传进指针,一开始的root变量就要改成指针
void show(node * list, int floor, int head, mess* order) {
node *a = list;
int tab = floor;
// printf("%d\n", floor);
while (a != NULL) {
for (int i = 0; i < head + 20; i++) {
printf("^");
strcat(order->tree, "^");
}
floor = tab;
// 去除一开始的缩进,由之前的addbro改成addchild,文件夹的child不为NULL,->child才能进入,这样才能打开文件夹
while (floor > 0) {
// printf("%d\n", floor);
// printf("|");
if (floor % 5 == 1) {
// 从0开始,倒数到1
printf("|");
strcat(order->tree, "|");
// 每下一层就是floor+5,对应一个竖,
} else {
printf(" ");
strcat(order->tree, " ");
}
floor--;
}
if (a->is_dictionary) {
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
printf("%-9s\n", "dictionary");
strcat(order->tree, "dictionary");
for (int i = 0; i < 18 - strlen("dictionary"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
// printf("-%-18s %-3s\n", a->name, "dictonary");
// 左对齐缩进
show(a->child, tab + 5, head, order);
} else {
// format(a->name,tab,lenth);
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
// 左对齐18长度,lenth=20被挤出,lenth=30不会超出
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
// printf("%-5d", tab);
printf("%-9s\n", "<file>");
strcat(order->tree, "<file>");
for (int i = 0; i < 18 - strlen("<file>"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
// printf("-%-7s %-s\n", a->name, "<file>");
}
a = a->bro;
}
}
//输出,查找,标记,替换,增加 族
//文件夹读取
//文件所有子项目读取
//文件写入.txt
//配合链表,实现代码命令创建
//选择文件传输
//在结合服务器实现云创建文件传送
//原版查找增加,现用于增加文件
void findandaddv2(node * list, int floor, char name[], char have[], char privaterootname[]) {
node *a = list;
// int tab = floor;
while (a != NULL) {
// floor = tab;
// while (floor) {
// printf(" ");
// floor--;
// }
if (a->is_dictionary) {
// printf("-%s\n", a->name);
if (strcmp(a->name, name) == 0) {
printf("FIND name\n");
addFilev2(a, have, privaterootname);
return ;
}
findandaddv2(a->child, 0, name, have, privaterootname);
// show(a->child, floor + 1);
} else {
if (strcmp(a->name, name) == 0) {
printf("FIND name\n");
addFilev2(a, have, privaterootname);
return ;
}
}
a = a->bro;
}
//需要控制只要查到,递归就不再做其他运算,直接退出,加结构体
}
//优化版本-增加文件夹
void findadd_dirv2(node * list, int floor, char name[], char have[], char privaterootname[]) {
node *a = list;
while (a != NULL) {
if (a->is_dictionary) {
// printf("-checking dictionary-%s\n", a->name);
if (strcmp(a->name, name) == 0) {
printf("FIND name %s\n", a->name);
addDictionaryv2(a, have, privaterootname);
return ;
}
findadd_dirv2(a->child, 0, name, have, privaterootname);
} else {
}
a = a->bro;
}
}
//void changename(node* list, char have[]) {
node *a = list;
node *prior;
node *dead = a;
char newname[100];
char *deadsign = "dead_";
strcat(newname, deadsign) ;
strcat(newname, dead->name);
rename(dead->name, newname);
//
// node *a = list;
// rename(a->name, have);
//}
//void deadsignshow(node* list) {
// node *a = list;
// node *dead = a;
// char newname[100];
// char *deadsign = "dead_";
// strcat(newname, deadsign) ;
// strcat(newname, dead->name);
// rename(dead->name, newname);
//}
void deadaddsign_v2(node * list) {
// node* a=list->child
// node* a=list;
node* a = list->child;
while (a != NULL) {
if (a->is_dictionary) {
a->is_dead = 1;
// deadsignv2(a);
// deadaddsign_v2(a->child);
deadaddsign_v2(a);
} else {
a->is_dead = 1;
}
a = a->bro;
// 因为传入本级bro,所以要a=list->child,不然list-bro就会一块标记
}
}
//
//void deadChild(node* list) {
// node *a = list;
// node *prior;
// node *dead;
// // int tab = floor;
// while (a != NULL) {
// // floor = tab;
// // while (floor) {
// // printf(" ");
// // floor--;
// // }
// if (a->is_dictionary) {
// printf("-%s\n", a->name);
if (strcmp(a->name, name) == 0) {
printf("FIND name\n");
// addFile(a, have);
return ;
}
// deadChild(a->child);
// // show(a->child, floor + 1);
// } else {
// while (a != NULL) {
// prior = a;
// if (a->bro->is_dictionary == 1) {
//
if(a->bro->child==NULL){
dead=a->bro;
a->bro=dead->bro;
free(a);
}
// } else {
// dead = a->bro;
// char *deadsign = "dead_";
// char newname[100];
//
// strcat(newname, deadsign) ;
// strcat(newname, dead->name);
// rename(dead->name, newname);
remove(dead->path);
//
// }
//
// }
// }
// prior = a;
// a = a->bro;
// }
//}
void findanddead(node * list, mess *order) {
node *a = list->child;
// int tab = floor;
while (a != NULL) {
if (a->is_dictionary) {
if (strcmp(a->name, order->said) == 0) {
printf("FIND name\n");
addFile(a, have);
a->is_dead = 1;
deadsignv2(a->child);
a->is_dead = 1;
deadaddsign_v2(a);
v2到_v2,封装当前目录先标记,然后再进入child进行修改
return ;
} else {
if (a->child != NULL) {
findanddead(a->child, order);
}
}
// show(a->child, floor + 1);
} else {
if (strcmp(a->name, order->said) == 0) {
printf("FIND name\n");
// addFile(a, have);
a->is_dead = 1;
return ;
}
}
a = a->bro;
}
}
//复制粘贴自show()函数,隐藏删除文件
void showhidedead(node * list, int floor, int head, mess* order) {
// node *a = list->child;
node *a = list;
int tab = floor;
while (a != NULL) {
if (a->is_dead) {
a = a->bro;
continue;
} else {
for (int i = 0; i < head + 20; i++) {
printf("^");
strcat(order->tree, "^");
}
floor = tab;
while (floor > 0) {
if (floor % 5 == 1) {
printf("|");
strcat(order->tree, "|");
} else {
printf(" ");
strcat(order->tree, " ");
}
floor--;
}
if (a->is_dictionary) {
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
printf("%-9s\n", "dictionary");
strcat(order->tree, "dictionary");
for (int i = 0; i < 18 - strlen("dictionary"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
if (a->child != NULL) {
showhidedead(a->child, tab + 5, head, order);
}
} else {
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
printf("%-9s\n", "<file>");
strcat(order->tree, "<file>");
for (int i = 0; i < 18 - strlen("<file>"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
}
a = a->bro;
}
}
}
//显示删除文件,并且打印标记已经删除的文件前缀
void showwithdead(node * list, int floor, int head, mess* order) {
node *a = list;
int tab = floor;
while (a != NULL) {
// if (a->is_dead) {
// a = a->bro;
// deadsignshow(a);
// continue;
// } else
{
for (int i = 0; i < head + 20; i++) {
printf("^");
strcat(order->tree, "^");
}
floor = tab;
while (floor > 0) {
if (floor % 5 == 1) {
printf("|");
strcat(order->tree, "|");
} else {
printf(" ");
strcat(order->tree, " ");
}
floor--;
}
if (a->is_dictionary) {
if (a->is_dead) {
char newname[100];
char *deadsign = "dead_";
strcat(newname, deadsign) ;
strcat(newname, a->name);
printf("%-18s", newname);
strcat(order->tree, newname);
for (int i = 0; i < 18 - strlen(newname); i++) {
strcat(order->tree, " ");
}
// printf("%-18s", a->name);
} else {
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
}
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
printf("%-9s\n", "dictionary");
strcat(order->tree, "dictionary");
for (int i = 0; i < 18 - strlen("dictionary"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
showwithdead(a->child, tab + 5, head, order);
} else {
if (a->is_dead) {
char newname[100];
char *deadsign = "dead_";
strcat(newname, deadsign) ;
strcat(newname, a->name);
printf("%-18s", newname);
strcat(order->tree, newname);
for (int i = 0; i < 18 - strlen(newname); i++) {
strcat(order->tree, " ");
}
// printf("%-18s", a->name);
} else {
printf("%-18s", a->name);
strcat(order->tree, a->name);
for (int i = 0; i < 18 - strlen(a->name); i++) {
strcat(order->tree, " ");
}
}
for (int i = 0; i < lenth - tab; i++) {
printf(" ");
strcat(order->tree, " ");
}
printf("%-9s\n", "<file>");
strcat(order->tree, "<file>");
for (int i = 0; i < 18 - strlen("<file>"); i++) {
strcat(order->tree, " ");
}
strcat(order->tree, "\n");
}
a = a->bro;
}
}
}
void checkloginv2(SOCKET sClient, char a[], char b[]) {
// char a[100];
// char b[100];
FILE* fp;
fp = fopen("./mysecret_root./pl.txt", "a");
// send(sClient,"input user name:\n" , 255, 0);
// printf("input user name:\n");
// scanf("%s", a);
// printf("input user secret code\n");
// scanf("%s", b);
// if (strcmp(a, "k") == 0) {
// printf("用户名核对正确\n");
// } else {
// printf("用户名错误\n");
// }
//
// if (strcmp(b, "p") == 0) {
// printf("密码名核对正确\n");
// } else {
// printf("密码错误\n");
// }
user have[100];
char c[10];
int i = 0;
for (i = 0; i < 10; i++) {
c[0] = '0' + i;
c[1] = '\0';
strcpy( have[i].acount, c);
strcpy(have[i].code, c);
}
for (i = 0; i < 100; i++) {
if (strcmp(a, have[i].acount) == 0) {
// printf("用户名核对正确\n");
send(sClient, "用户名核对正确\n", 255, 0);
if (strcmp(b, have[i].code) == 0) {
// printf("密码正确\n");
send(sClient, "密码正确\n", 255, 0);
} else {
// printf("密码错误\n");
send(sClient, "密码错误\n", 255, 0);
}
break;
}
}
if (i == 100) {
// printf("用户名错误\n");
send(sClient, "用户名错误\n", 255, 0);
}
// rewind(fp);
fseek(fp, SEEK_END, +1); // 再基于end处向后移动一位
if ( feof(fp) == 0) {
// 如果文件结束,则返回非0值,否则返回0
// printf("now file point in the end\n");
send(sClient, "now file point in the end\n", 255, 0);
}
// 直接使用时的错误分析:
// 对于一个空文件来说,当程序打开它的时候,它的光标会停在文件的开头,但是由于文件里什么内容都没有存(但是EOF是存在的),即整个文件就存贮了一个EOF。当程序打开文件,并直接调用feof()时,这个函数就会站在光标的位置向后张望,结果就看见了EOF,然后就当然返回0了。
// ————————————————
// 版权声明:本文为CSDN博主「konghouy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
// 原文链接:https://blog.csdn.net/konghouy/article/details/80530937
}
//第一步复制粘贴,第二步,在改完之前的小函数之后,对checkorder内部函数加入v2后缀和括号内创建参数privatename,化原来新写函数变成一步一步就地替换函数,避免一开始思考架构。
//第三步参数改rootv2
//就着必然要的参数进行增加,对着框架一个个比着运行,自然而然写出
void checkorder(node * rootv2, mess* order, char privaterootname[]) {
if (strcmp("cf", order->ch) == 0) {
send(order->sClient, "Input file name:\n", 255, 0);
// printf("Input file name:\n");
// scanf("%s", ch);
for (int i = 0; i < 255; i++) {
order->ch[i] = '\0';
}
recv(order->sClient, order->ch, 255, 0);
// printf("Input parent contest name:\n");
send(order->sClient, "Input parent contest name:\n", 255, 0);
char tar[100] = "";
for (int i = 0; i < 100; i++) {
tar[i] = '\0';
}
// 清空,不然下一次覆盖前面的数据,后半部分没有覆盖的旧数据也成了参数
// strcpy(tar, "");
// scanf("%s", tar);
recv(order->sClient, tar, 255, 0);
findandaddv2(rootv2, 0, tar, order->ch, privaterootname);
printf("success creat file\n");
send(order->sClient, "success creat file\n", 255, 0);
strcpy(tar, "");
} else if (strcmp("cd", order->ch) == 0) {
// printf("Input dictionary name:\n");
// scanf("%s", ch);
// char tar[100];
// printf("Input parent contest name:\n");
// scanf("%s", tar);
send(order->sClient, "Input dictionary name:\n", 255, 0);
for (int i = 0; i < 255; i++) {
order->ch[i] = '\0';
}
recv(order->sClient, order->ch, 255, 0);
char tar[100];
strcpy(tar, "");
send(order->sClient, "Input parent contest name:\n", 255, 0);
recv(order->sClient, tar, 255, 0);
findadd_dirv2(rootv2, 0, tar, order->ch, privaterootname);
strcpy(tar, "");
} else if (strcmp("st", order->ch) == 0) {
strcpy(order->tree, "");
show(rootv2, 0, 10, order);
strcat(order->tree, "\ntree end\n");
// send(order->sClient, order->tree, 1400, 0);
// bug发现原来是"\0"不是数据的停止输出点,而是1440长度参数控制,不像printf遇到'\0'自动停止。
send(order->sClient, order->tree, strlen(order->tree), 0);
strcpy(order->tree, "");
// printf("\ntree end\n");
// 13.之后都是增加了删除之后,有了删除标志位后,显示函数,删除函数之类的操作,结构体更新
} else if (strcmp("dead", order->ch) == 0) {
strcpy(order->said, "");
// printf("Input dead target name:\n");
// scanf("%s", ch);
for (int i = 0; i < strlen(order->said); i++) {
order->said[i] = '\0';
}
send(order->sClient, "Input dead target name:\n", 255, 0);
strcpy(order->said, "");
// 不能覆盖导致的bug
recv(order->sClient, order->said, 255, 0);
printf("Input dead parent name:\n");
// char tar[100];
// scanf("%s", tar);
printf("寄!%s\n", order->said); //不初始化就会乱码从而字符匹配失败
findanddead(rootv2, order);
send(order->sClient, "dead already signed\n", 255, 0);
strcpy(order->said, "");
} else if (strcmp("sthd", order->ch) == 0) {
strcpy(order->tree, "");
showhidedead(rootv2, 0, 30, order);
// send(order->sClient, order->tree, 1400, 0); bug发现原来是"\0"不是数据的停止输出点,而是1440长度参数控制
send(order->sClient, order->tree, strlen(order->tree), 0);
strcpy(order->tree, "");
} else if (strcmp("stwd", order->ch) == 0) {
strcpy(order->tree, "");
showwithdead(rootv2, 0, 35, order);
// send(order->sClient, order->tree, 1400, 0); bug发现原来是"\0"不是数据的停止输出点,而是1440长度参数控制。
send(order->sClient, order->tree, strlen(order->tree), 0);
strcpy(order->tree, "");
} else {
// printf("commond not found\n");
send(order->sClient, "commond not found\n", 255, 0);
}
}
void registname(char headname[], char privaterootname[]) {
// FILE* fp;
// fp = fopen("./mysecret_root./pl.txt", "a+");
// fclose(fp);
// printf("请输入用户空间名\n");
// char headname[100];
// scanf("%s", headname);
char *b = "./";
// strcat(privaterootname, b);
strcat(privaterootname, headname);
char d[100] = "";
strcat(d, b);
strcat(d, privaterootname);
strcat(d, b);
strcat(d, "pl.txt");
printf("%s\n", d);
FILE* fp;
fp = fopen(d, "a+");
fclose(fp);
}
客户端代码。直接来源DevC++ 用C语言的多线程 实现简单的客户端和服务器-CSDN博客
对比可以发现其实重走了服务器从单线程到多线程的路子。
可以见得保留历史代码,实际原因就是没来得及主动意识到其他作用,在遇到情况之后按计划回顾代码的延续,才理解能解决问题的知识是如何无中生有产生的。在偶然解决问题后,显著提高解决下次问题的概率。?
客户端完整代码,由于有多线程了,如果出现某dll缺失问题,解决方案详见DevC++ 多线程创建与删除与exe文件脱离DevC++运行中发现dll文件和exe文件的关系-CSDN博客
#include <WINSOCK2.H>
#include <STDIO.H>
#include<pthread.h>
#pragma comment(lib,"ws2_32.lib")
char sendData[255];
//SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//全局变量不能在直接编译的地方赋值
SOCKET sclient;
void *see(void* ) {
while (1) {
scanf("%s", sendData);
if (strlen(sendData) == 0) {
} else {
send(sclient, sendData, strlen(sendData), 0);
}
}
}
//格式和服务端一样
int main(int argc, char* argv[]) {
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if (WSAStartup(sockVersion, &data) != 0) {
return 0;
}
// SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sclient == INVALID_SOCKET) {
printf("无效的 socket !");
return 0;
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
//表示使用IPv4地址协议
// https://blog.csdn.net/u012736362/article/details/130392547
serAddr.sin_port = htons(8888);
// puts("请输入对方的IP地址");
char loa[16] = "127.0.0.1";
// 就是当服务器的电脑的IP , win建+r 输入cmd,打开命令行,输入再直接 ipconfig/all可查看IP。
// IP是读取目标机器的IP,由于一台电脑当服务器和客户端,127.0.0.1是自回路,自己发送给自己
serAddr.sin_addr.S_un.S_addr = inet_addr(loa);
// IP赋值
//链接发消息
if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) {
//主动连接服务器
printf("连接错误 !");
closesocket(sclient);
return 0;
}
puts("连接成功!!");
puts("你现在可以向服务器发送信息:");
pthread_t in;
pthread_create(&in, NULL, see, NULL);
while (1) {
char recData[255];
int ret = recv(sclient, recData, 255, 0);
// 接收服务端消息
if (ret > 0) {
recData[ret] = 0x00;
printf(recData);
}
strcpy(recData,"");
}
closesocket(sclient);
WSACleanup();
return 0;
}