IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 个人项目----基于TCP的文件传输系统 -> 正文阅读

[系统运维]个人项目----基于TCP的文件传输系统

目录

主体功能

程序使用方法

1、运行客户端?编辑

2、运行服务器

3、登录

4、注册

5、程序命令解析

6、传输文件(上传--cp、下载--lcp)

具体代码

服务器

services.c(main 函数)

sqlite.c(数据库相关模块)

demo.c(服务器与客户端交互相关模块)

demo.h

sqlite.h

makefile

客户端

client.c(main函数)

demo.c(与服务器交互相关模块)

pathread.c(线程模块,实现恢复暂停下载)

sqlite.c(登录与注册--客户端)

demo.h

pthread.h

sqlite.h

makefile


主体功能

程序总体分两部分,客户端与服务器。
1、支持上传(cp)、下载(lcp)除根目录文件任意大小的文件
2、支持多客户端连接服务器下载、上传文件
3、支持文件下载、上传的暂停与恢复
4、自动检测输入的格式,超过次数程序自动退出。
5、支持切换远程服务器(lcd)、本地客户端(cd)工作目录,以及查看目录文件(lls、ls)和当前工作目录(lpwd,pwd)。

程序使用方法

1、客户端与服务器都可以直接使用make命令编译后运行
2、客户端运行时需要输入,服务器的ip地址与端口号

1、运行客户端

?客户端运行后,属于待机状态,等待服务器连接后交互信息。

2、运行服务器

运行服务器需要输入,客户端的ip地址与端口号。(程序仅支持同一网段下的客户端与服务器交互)。

3、登录

客户端运行后,可以选择登录与注册。服务器在运行之初会自动创建4个VIP用户。可以使用VIP用户直接登录。
2345678 ?66666666
7654321 ?8888888?
654321 ? 888888
54321 ? ?88888

?

4、注册

?登录注册时,输入需要按照格式输入,否则到达次数。程序自动退出。

5、程序命令解析

客户端命令:
?? ?ls:查看当期客户端,当期目录文件
?? ?cd:切换当前客户端目录
?? ?pwd:查看当前绝对路径
?? ?cp :上传文件,格式: cp ?【当前文件路径】【待上传文件名】 ?【目的文件路径】【目的文件名】
?? ??? ??? ??? ??? ??? ?cp ./file1 ? ./file1
?? ??? ??? ??? ?
?? ?lcp:下载文件,格式: lcp ?【远程文件路径】【远程文件名】 ?【目的文件路径】【目的文件名】
?? ??? ??? ??? ??? ??? ?lcp ./file1 ./file1
?? ?***除开目的文件名不能与所传路径下的文件重名外,所有的路径(除根目录)与文件名都可以根据自己意愿确定。
?? ?***程序会检测文件是否存在,是否重名,是否为空文件,并反馈信息。
?? ?lls:查看服务器当前目录文件
?? ?lpwd:产看服务器当前工作路径
?? ?lcd:切换服务器工作目录
?? ?quit:客户端退出
?? ?lcp:

服务器命令:
?? ?CTRL+c:退出服务器,并自动删除数据库。所有账户信息清零。

6、传输文件(上传--cp、下载--lcp)

?

程序具备恢复与暂停功能,在上传或下载过程中。输入‘1’表示暂停下载、上传。输入‘2’表示恢复下载、上传。

具体代码

服务器

services.c(main 函数)

int fid = 0;

#include"sqlite.h"
int main(int argc, char* argv[])
{
	int type = 0;
	int fd1,pid,ret;
	int fid = init_socket();
	if(fid < 0)
	{
		printf("init_socket failed.\n");
		return -1;
	}

	signal(SIGCHLD,handler);//捕捉自进程信号,进行处理 
	signal(SIGINT,handler);//捕捉自进程信号,进行处理 

	sqlite3 *db;
	if(init_vip(&db) == -1)//先创建两个用户
	{
		printf("failed\n");
		close(fd1);
		system("rm ./jkl.db");
		exit(-1);
	}


	while(1)
	{
		fd1 = accept_client(fid);//等待客户端接入
		if(fd1 == -1)
		{
			close(fid);
			return -1;
		}

		pid = fork();
		if(pid  == -1)
		{
			perror("fork:");
		}else if(pid == 0 )//子进程和接入客户端交互
		{

			ret = usr_into_system(fd1,&db);//客户端登陆、注册
			if(-1 == ret )

			{
				close(fd1);
				exit(-1);

			}else if(ret == 13 )
			{
				printf("客户端登陆成功 \n");
			}	

			information_from_client(&type,fd1);//循环检测客户端输入
			exit(0);
		}

	}
	close(fid);
	exit(0);
	return 0;
}

sqlite.c(数据库相关模块)

#include"sqlite.h"

extern int vip;
int init_vip(sqlite3 **db1)//初始化数据库,创建一张表,表中两个vip
{
	char sql[100] = {0};
	
	if(init_seqlite("./jkl.db",db1) == -1) 
		return -1;
	strcpy(sql,"create table student(vip integer, log text, password text);");
	if(init_usr(*db1,sql) == -1)
		return -1;
	strcpy(sql,"insert into student values(1 , '2345678', '66666666');");
	if(init_usr(*db1,sql) == -1)
		return -1;
	strcpy(sql,"insert into student values(1 , '7654321', '8888888');");
	if(init_usr(*db1,sql) == -1)
		return -1;
	strcpy(sql,"insert into student values(1 , '654321', '888888');");
	if(init_usr(*db1,sql) == -1)
		return -1;
	strcpy(sql,"insert into student values(1 , '54321', '88888');");
	if(init_usr(*db1,sql) == -1)
		return -1;
	return 0;
}

int init_seqlite(char*r,sqlite3 ** db)//创建一张表
{
	if(0 != sqlite3_open(r,db) )
	{
		perror("open");
		return -1;
	}
	return 0;
}

int init_usr(sqlite3* db,char* sql)//创建用户
{
	if(0 != sqlite3_exec( db,sql,NULL,NULL,NULL))
	{
		perror("exec");
		return -1;
	}
	return 0;
}

int usr_into_system(int fd, sqlite3 **db)//客户端登陆注册
{
	sqlite3 *db1 = *db;
	int a = 0;
	int ret1,ret2;
	struct msgdata msg;
	bzero(&msg,sizeof(msg));
	strcpy(msg.data,"输入 1(登陆) or 2(注册).");
	send(fd,&msg,sizeof(msg),0);//提醒客户端输入
				    //	printf("services put success: %s\n",msg.data);
	while(1)
	{
		bzero(msg.data,sizeof(msg.data));
		if( 0 == recv(fd,&msg,sizeof(msg),0))//接收客户端的输入
			break;
		//	printf("msg.type  =%d\n",msg.type);
		//vip = msg.j;
	
		int n =0 ;
		char buf1[50] ={0};
		char buf2[50] ={0};
		strcpy(buf1,msg.data);
		strcpy(buf2,strtok_str(buf1,0));
		strcpy(buf1,strtok_str(msg.data,1));

	//	printf("buf1:%s\n",buf1);//密码	
	//	printf("buf2:%s\n",buf2);//帐号	
		if(msg.type == 1)
		{
			msg.type = 0;

			int x=0,y=0;
			ret1 =select_data(db1,1,buf2,&x);
			ret2 =select_data(db1,2,buf1,&y);
			if( (ret1 == 1) && (ret2 == 1))
			{
				if(  ((x != 0) &&(y != 0) )  && (x == (y) - 1)  )
				{
				//	printf("x= %d  y =%d\n",x ,y);	
					msg.type = 1;
					send(fd,&msg,sizeof(msg),0);//告诉客户端登陆成功
					return 13;

				}else{
				msg.type = -1;
                                send(fd,&msg,sizeof(msg),0);
                                continue;
				
				}
			}else{
				msg.type = -1;
				send(fd,&msg,sizeof(msg),0);
				continue;
			}	

			/*

			   if( (select_data(db1,1,buf2) == 1) && (select_data(db1,2,buf1) == 1) )
			   {
			   msg.type = 1;
			   send(fd,&msg,sizeof(msg),0);//告诉客户端登陆成功
			   return 13;

			   }else{
			   msg.type = -1;
			   send(fd,&msg,sizeof(msg),0);
			   continue;
			   }
			   */		
		}else if(msg.type == 2)
		{
	//		printf("j = %d\n",msg.j);
			if(msg.j == 1)
			{
				vip = 1;
			}else if(msg.j == -1)
			{
				vip = 0;
			}

			if(add_usr_to_system(db1,buf2,buf1,&msg) == -1)
			{
				msg.type = -1;
				send(fd,&msg,sizeof(msg),0);
				continue;
			}else{
				msg.type = 1;
				send(fd,&msg,sizeof(msg),0);
				continue;
			}
		}else if(msg.type  ==-1)
		{
			printf("client quit.\n");
			return -1;	
		}
	}
	return 0;
}



int  select_data(sqlite3* db, int m,char* buf,int* x)
{
	int ret =0 ;
	char sql[50] = {0};
	char **result;
	int R, C;

	if(0 != sqlite3_get_table(db, "select *from student;", &result, &R, &C, NULL))
	{
		printf("select is default\n");
		return -1;
	}
	int i, j;

	 m = C +m;
	for(i = 0; i <R;i++ )
	{
//		printf("%s \n",result[m]);
//		printf("%d  buf = %d\n",strlen(result[m]),strlen(buf));
		ret = strcmp(result[m],buf) ;
//		printf("%d\n",ret);
		if((ret == 0) && (strlen(buf) == strlen(result[m])))//找到相同 数据
		{
	//		printf("this m result  %s    %d\n",result[m],strlen(result[m]));
	//		printf("this m buf  %s        %d\n",buf,strlen(buf));
			*x = m;
			return 1;
		}
		m +=3;
	}

	return 0;//没有找到相同数据
} 



int add_usr_to_system(sqlite3*db,char *buf2,char *buf1,struct msgdata* msg)//注册
{
	int x; 
	char sql[100] ={0};
//	printf("buf2 = %s\n",buf2);
	int ret = select_data(db,1,buf2,&x) ;

	if(ret == 1 )
	{
		strcpy(msg->data,"用户或已存在.");
		return -1;
	}else if(ret == -1)
	{
		return -1;
	}


	sprintf(sql,"insert into student values(%d , '%s', '%s');",msg->j,buf2,buf1);
	if(init_usr(db,sql) == -1)
		return -1;
	return 0;
}


demo.c(服务器与客户端交互相关模块)

#include"demo.h"
#include<pthread.h>
//extern int type;
int vip  =1;
int pthread_type = 2;
int type_pth = 2;

void * input(void*);
extern int fid;
int init_socket()//初始化套接字
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == fd)
	{
		perror("socket:");
		return -1;
	}else{
		printf("socket success.\n");
	}
	int on =1;
	if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))< 0)//设置端口可以复用
	{
		perror("setsockopt:");
		return -1;
	}

	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
	addr.sin_port 		= htons(6150);
	addr.sin_addr.s_addr    = inet_addr("0.0.0.0");

	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
	{
		perror("bind:");
		return -1;
	}else{
		printf("bind success.\n");
	}

	if(listen(fd,13) < 0)
	{
		perror("listen:");
		return -1;
	}else{
		printf("listen........\n");
	}
	return fd;
}


int accept_client(int fd)//接收客户端连接 
{
	struct sockaddr_in addr;
	socklen_t len;
	int acce_fd = accept(fd,(struct sockaddr*)&addr,&len);
	if(acce_fd == -1)
	{
		perror("accept:");
		return -1;
	}else{
		printf("accept success.\n");
		printf("\033[1;31mip:%s,port:%d\033[0m\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
	}
	return acce_fd;
}
void handler(int no)//信号处理函数  
{

	int pid;
	if(no == SIGCHLD)
	{	
		pid = waitpid(-1,NULL,WNOHANG);//非阻塞等待子进程推出 
		if(-1 == pid)
		{
			perror("waitpid :");
		}else{
			//printf("\033[1;34msignal: child quit,pid:%d\033[0m\n",pid);
		}
	}else if(no == SIGINT)
	{
		system("rm ./jkl.db");
		close(fid);
		printf("services quit\n");
		exit(0);
	}
}



int information_from_client(int* type,int fd1)//循环接收客户端信息
{
	int ret,open_fd;
	struct msgdata *msg = (struct msgdata*)malloc(sizeof(struct msgdata));
	while(1)
	{
		bzero(msg,sizeof(struct msgdata));
		ret = recv(fd1,msg,sizeof(struct msgdata),0);
		if(ret == -1)
		{
			perror("recvmsg:");
			break;
		}else if (ret == 0)
		{
			break;
		}else{
			printf("               \033[1;34m information  from clent:%s\033[0m\n",msg->filename);
			if(get_command(type,msg,fd1) == 13 )
				break;
		}

	}
	free(msg);
	msg =	NULL;
	close(fd1);
	return 0;
}

int get_command(int* type,struct msgdata* msg,int fd)//分析命令
{	char buf[50] = {0};
	strcpy(buf,msg->filename);
//	printf("get buf =%s\n",buf);

	if(strncmp(buf,"lcd",3) == 0)	 	 *type = 1;
	else if(strncmp(buf,"lls",3) == 0)	 *type = 2;
	else if(strncmp(buf,"lpwd",4) == 0)	 *type = 3;
	else if(strncmp(buf,"lcp",3) == 0)	 *type = 4;
	else if(strncmp(buf,"cp",2) == 0)	 *type = 5;
	else if(strncmp(buf,"quit",4) == 0)	 *type = 6;
	else{
		printf("input error|n");
		return -1;
	}
//	printf("type =%d\n",*type);
	if(deal_command(type,msg,fd) == 13)
		return 13;
	return 0;
}


int deal_command(int *type,struct msgdata* msg,int fd)//处理命令
{
	char *p =NULL;
	char *p1 = NULL;
	switch(*type)
	{
		case 1:
			p1 = msg->filename;
			p1 = p1+3;
			if(*p1 == '\0')
			{
				if( chdir("/")== -1)
				{
					msg->type == -1;
					strcpy(msg->data,strerror(errno));
				}else{
					strcpy(msg->data,"\033[0;34m lcd:success\033[0m");
				}
				send(fd,msg,sizeof(struct msgdata),0);//反馈错误信息

			}else{
				p = strtok_str(msg->filename,1);
				if(chdir(p) == -1)
				{
					msg->type == -1;
					strcpy(msg->data,strerror(errno));
				}else{
					strcpy(msg->data,"\033[0;34m lcd:success\033[0m");
					//strcpy(msg->data,"success\n");
				}
				send(fd,msg,sizeof(struct msgdata),0);//反馈错误信息
			}
			break;
		case 2:
			char buf[123] = {0};
			p1 = msg->filename +3;
			if(*p1 == ' ') //判断命令lls 后跟了选项
			{
				p = strtok_str(msg->filename,1); 
				sprintf(buf,"ls %s",p);	//拼凑ls 加选项
				msg->type =3;
		//		printf("this is lls -xxxx \n");
				send(fd,msg,sizeof(struct msgdata),0);// 告诉客户端需要打印

				send_to_client(buf,fd);//读取并发送ls 结果
			}else if (*p1 == '\0')
			{	
				//iprintf("this is 2\n");
				msg->type =3;
				//printf("this  is lls \n");
				send(fd,msg,sizeof(struct msgdata),0);//告诉客户端可以后面的打印
				send_to_client("ls",fd);
			}else{
				//printf("this is 3\n");
				msg->type = -1;
				strcpy(msg->data,"lls: input error");
				send(fd,msg,sizeof(struct msgdata),0);//告诉客户端命令格式错误
			}
			break;
		case 3:
			char *p = msg->filename;
			p = p+4;
			if(*p == '\0')
			{

				send_to_client("pwd",fd);

			}else{
				msg->type = -1;
				strcpy(msg->data,"lpwd:input error");
				send(fd,msg,sizeof(struct msgdata),0);
			}

			break;
		case 4:
			p = strtok_str(msg->filename,1);
			send_file_to_client(p,fd);//发送文件
			break;
		case 5://实现cp功能,接收文件
			accept_file(msg,fd);//接收文件
			break;
		case 6:
			printf("clenit quit\n");
			return 13;
			break;
	}
	//	printf("this is end end\n");
	return 0;
}

char *strtok_str(char* str,int i)//分割字符串
{
	char *p = strtok(str," ");	
	while(i--)
	{
		p = strtok(NULL," ");	

	}
	//	printf("%s\n",p);
	return p;
}

int send_to_client(char* str,int fd)//发送命令后的内容到客户端,lpwd lls
{
	int ret ,ret1;
	//	printf("str=%s\n",str);
	struct msgdata msg;
	bzero(&msg,sizeof(msg));
	FILE* fd1 = popen(str,"r");
	if(fd1 == NULL)
	{
		perror("popen");
	}
	while(1)
	{
		ret = fread(msg.data,sizeof(msg.data),1,fd1);
		if(ret == -1)
		{
			perror("fread");
			pclose (fd1);
			return -1;
		}else if(ret == 0)//读取到末尾了
		{
		//	printf("str=         %s\n",str);
			if(NULL != strstr(str,"pwd") )
			{
				msg.type = 2;   // 告诉客户端是pwd,并且读到末尾了
			}else{
				msg.type = 13;  //lls,并且读到末尾了
			}
			ret = send(fd,&msg,sizeof(msg),0);
			if(ret == -1)
			{
				perror("send");
			}
	//		printf("msg type      %d\n",msg.type);
			bzero(msg.data,sizeof(msg.data));
			break;
		}else{

			ret = send(fd,&msg,sizeof(msg),0);
			if(ret == -1)
			{
				perror("send");
			}
			bzero(msg.data,sizeof(msg.data));
		}
	}
	pclose(fd1);
	return 0;
}




int accept_file(struct msgdata* Msg,int fd)//接收客户端复制到的文件,cp

{

	char *str = strtok_str(Msg->filename,2);//获取文件名 
	int ret,i=0,j,op_fd;
	int tail;
	char buf[130] = {0};
	memset(buf,'-',100);
	int k = 0;

	struct msgdata msg;
	int size = sizeof(msg.data);
	bzero(&msg,sizeof(msg));

	recv(fd,&msg,sizeof(msg),0);//确认客户端,文件是否打开成功
	if(msg.type  == -1)//打开失败,终止本次传输
	{
		printf("\033[传输失败\033[0m\n\n");
		return -1;
	}else{//打开成功,接收本次传输的信息,次数
		j = msg.j;
		tail =  msg.file_len - (j-1)*(size);//最后一次的长度
						    // printf("%s\n",msg.data);
	}

	struct msgdata msg2;
	op_fd = open(str,O_CREAT|O_EXCL|O_RDWR,0666);//创建文件
	if(op_fd == -1)
	{
		perror("open");
		printf("\033[0;31m创建文件失败\033[0m\n");
		msg2.type =-1;
		send(fd,&msg2,sizeof(msg2),0);//告诉客户端,这边创建失败。不要发送。终止传输
		return -1;
	}
	msg2.type =1;
	send(fd,&msg2,sizeof(msg2),0);//告诉客户端,这边创建成功。可以发送,开始接收数据

	int s = 0;
	if(vip == 1)
	{
		s =1;
	}else{
		s =6;
	}
//	printf("vip =%d , s =%d \n",vip,s);
	while(1)
	{
		i++;
		//		bzero(msg.data,sizeof(msg.data));
		memset(msg.data,0,sizeof(msg.data));
		ret = recv(fd,&msg,sizeof(msg),0);
		if(ret == -1)
		{
			perror("read");
			break;
		}
		int ret = write(op_fd,msg.data, ((i<j)?size:tail) );
		if(ret == -1)
		{
			perror("write:");
		}

		float g = (((float)i/j)*100 );
		while(k < (int)g)
		{
			buf[k++] ='*';
			printf("\033[0;32m<%s>\033[0;31m[%4.2f%%]\033[0m\033[0m \r",buf,g);
			fflush(stdout);
			usleep(70000*s);
		}

		if( j == i)
		{
			break;
		}
	}
	printf("\n传输完毕.\n");
	return 0;
}

int send_file_to_client(char *p ,int fd)//lcp,由服务器发送文件
{
	int ret ,i = 0,j,s=0;
	struct msgdata msg,msg1;
	float g;
	int size  = sizeof(msg.data);
	char buf1[50] = {0};
	char buf[130] = {0};//精度条
	memset(buf,'-',100);
	int k =0 ;

	int op_fd = open(p,O_RDONLY);// 打开本地文件
	if(op_fd == -1)//打开失败
	{
		msg.type = -1;//告诉客户端本地文件不存在,无法传输文件
		perror("open");
		strcpy(msg.data,"\033[0;31m远程文件不存在,无法完成传输\033[0m\n");

		send(fd,&msg,sizeof(msg),0);//告诉客户端文件打开失败
		return -1;
	}else{//打开成功
		msg.file_len = lseek(op_fd,0,SEEK_END);//获取文件大小
		lseek(op_fd,0,SEEK_SET);
		if(msg.file_len == 0)
		{
			msg.type =  -1;
			strcpy(msg.data,"\033[0;31m下载文件为空,不支持传输\033[0m\n");
			send(fd,&msg,sizeof(msg),0);//告诉客户端,传输终止
			return -1;
		}
		msg.type =1;
		msg.j = msg.file_len/size +  (((msg.file_len%size) > 0)?1:0);//获取本次传输次数
		strcpy(msg.data,"\033[0;34m service: 远程文件存在,可以传输\033[0m\n");
		send(fd,&msg,sizeof(msg),0);//告诉客户端,发送的次数\文件大小
		j = msg.j;
	}

	//send(fd,&msg,sizeof(msg),0);
	//等待客户端创建新文件是否成功
	struct msgdata msg2;
	recv(fd,&msg2,sizeof(msg2),0);
	if(msg2.type == -1)//客户端创建文件失败,无法完成传输
	{
		printf("\033[0;31m客户端创建文件失败,或文件已经存在,无法完成本次传输.\033[0m\n");
		close(op_fd);
		return -1;
	}else{
		if(vip == 1)
		{
			s =1;
		}else{
			s =6;
		}

		pthread_t tid;
		while(1){
			if(i == 0)
			{
				pthread_create(&tid,NULL,input,(void *)&fd);
			}
			if(type_pth == 2)
			{	

				i++;
				bzero(msg.data,sizeof(msg.data));
				ret = read(op_fd,msg.data,sizeof(msg.data));//读取文件
				if(ret == -1)
				{
					perror("read");
					close(op_fd);
					return -1;
				}
				send(fd,&msg,sizeof(msg),0);//文件数据发给客户端
				g = (((float)i/j)*100 );                                             
				while(k < (int)g)                                                          
				{                                                                          
					buf[k++] ='*';                                                     
					printf("\033[0;32m<%s>\033[0;31m[%4.2f%%]\033[0m\033[0m \r",buf,g);//打印进度条 

					fflush(stdout);
					usleep(70000*s);
				}
				if(j == i)
				{
					pthread_cancel(tid);
					pthread_join(tid,NULL);
					break;
				}
			}
		}
		printf("\n文件传输完毕.\n");
	}
}


void * input(void* fd)
{
//	printf("fd === %d\n",*((int*)fd));
	struct msgdata msg;
	while(1)
	{
		if(recv(*((int*)fd),&msg,sizeof(struct msgdata),0) == -1)
			perror("recv");
		if(msg.type_pth == 1)
		{
			type_pth = 1;
		}else if(msg.type_pth == 2 )
		{
			type_pth = 2;
		}
//		printf("type =%d   ms =%d\n",type_pth,msg.type_pth);
	}
}

demo.h

#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<errno.h>

#define lcd 	1
#define lls 	2
#define lpwd 	3
#define lcp	4
#define cp	5
#define quit	6
#define my_exit	7


struct msgdata{
	char filename[50];
	char data[300];
	int type;
	int file_len;
	int j;
	int type_pth;

};
int accept_client(int fd);//接收客户端连接 
int init_socket();//初始化套接字
void handler(int no);//信号处理函数   
int information_from_client(int* type,int fd1);//接收客户端首次发出的信息

int get_command(int* tyep ,struct msgdata*, int fd);//分析命令
int deal_command(int* type,struct msgdata*,int fd );//处理命令

char *strtok_str(char *,int);//分割字符串
int accept_file(struct msgdata *,int fd);//接收文件,cp时,服务器接收文件


int send_to_client(char* str,int fd);//lls lpwd后的信息  发送到客户端

int send_file_to_client(char *p ,int fd);//发送文件到客户端





sqlite.h

#include<stdio.h>
#include<sqlite3.h>
#include<string.h>
#include"demo.h"
int init_seqlite(char*,sqlite3 ** db);//创建一个数据
int init_usr( sqlite3* db,char *);//创建用户
int init_vip(sqlite3 **);//创建两个vip

int usr_into_system();//客户端登陆

int  select_data(sqlite3*,int,char*,int*);//查找数据,在数据库中
				     
int add_usr_to_system(sqlite3*,char* ,char*,struct msgdata*);//注册帐号

makefile

test:services.o demo.o sqlite.o

	gcc  -Wall services.o demo.o sqlite.o  -o test -lsqlite3 -L/usr/local/lib -I/usr/local/include  -lpthread
services.o:services.c
	gcc -c services.c  -o services.o
demo.o:demo.c
	gcc -c demo.c -o demo.o
sqlite.o:sqlite.c
	gcc -c sqlite.c -o sqlite.o -lsqlite3

.PHONY:clean
clean:
	rm -r ./*.o  test

# -lsqlite3 -L/usr/local/lib -I/usr/local/include -static -lpthread

客户端

client.c(main函数)

#include"sqlite.h"

int fd;
int main(int argc, char *argv[])
{
	int ret;
	char buf[50] = {0};
	int type = 0;
	fd = connect_services(argc,argv[1],argv[2]);
	if(fd == -1)
	{
		printf("connect_services failed\n");
		return -1;
	}

	ret = login_reges_to_servises(fd);
	if(ret == -1)
	{
		close(fd);
		return -1;
	}else if(ret == 13)
	{


		printf("本程序支持功能:\n1、下载文件:cp [路径][文件名] [目标路径][目标文件名]\n2、上传文件:lcp [路径][文件名] [目标路径][目标文件名]\n3、查看本地文件、切换本地工作目录、查看工作目录路径(ls ,cd,pwd及相关选项)\n4、查看远程文件、切换目录、路径(lls、lcd、lpwd)及相关选项\n");

	}

	get_command(buf,sizeof(buf),&type);//对命令进行解析
	close(fd);
	return 0;
}

demo.c(与服务器交互相关模块)

#include"demo.h"
#include"pthread.h"
int vip = 1;
int pthread_type = 2;
int pthread_interrupt( pthread_t*);//建立线程 cp(上传文件)
int pthread_interrupt2( pthread_t*);//建立线程 ,lcp(下载文件)

extern int fd;
int connect_services(int argc,char* ip, char * port)//连接 
{
	if(argc < 3){
		printf("too few arguements: ./test [ip] [port]\n" );
		return -1;
	}
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
		perror("socket");
		return -1;
	}
	printf("sockfd: %d\n", sockfd);

	struct sockaddr_in addr = {
		.sin_family			= AF_INET,
		.sin_port			= htons( atoi(port) ),
		.sin_addr.s_addr		= inet_addr( ip )
	};
	if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		perror("connect");
		close(sockfd);
		return -1;
	}
	return sockfd;

}


int get_command(char *str,int size,int* type )//分析命令
{
	char *p = NULL;
	while(1)
	{	memset(str,0,50);
		fgets(str,size,stdin);
		p = str+ strlen(str)-1;
		*p = '\0';
		//	printf("str strlen= %d\n",strlen(str));
		if(strncmp(str,"cd",2) == 0)		*type  =1;
		else if(strncmp(str,"lcd",3) == 0)	*type  =2;
		else if(strncmp(str,"ls",2) == 0)	*type  =3;
		else if(strncmp(str,"lls",3) == 0)	*type  =4;
		else if(strncmp(str,"pwd",3) == 0)	*type  =5;
		else if(strncmp(str,"lpwd",4) == 0)	*type  =6;
		else if(strncmp(str,"cp",2) == 0)	*type  =7;
		else if(strncmp(str,"lcp",3) == 0)	*type  =8;
		else if(strncmp(str,"quit",4) == 0)	*type  =9;
		else {
			printf("input error:\n");
			continue;
		}
		//	printf("type %d\n",*type);
		if( 13 == deal_comman(*type,str))
			return -1;
	}
	return 0;

}	

char * strtok_str(char* str,int i)//分割字符串
{
	char *p = strtok(str," ");
	while(i--)
	{
		p = strtok(NULL," ");
	}
	return p;
}


int deal_comman(int type,char *str)//处理命令
{
	struct msgdata msg;
	bzero(&msg,sizeof(msg));
	int ret;
	char *p =NULL;
	char buf[30] ={0};
	switch(type)
	{
		case 3:
		case 5:
			system(str);
			printf("\033[0;34m===========================================================\033[0m\n\n");
			break;

		case 1:
			p = strtok_str(str,1);
			chdir(p);		
			printf("\033[0;34m===========================================================\033[0m\n\n");
			break;
		case 2:
			strncpy(msg.filename,str,strlen(str));	
			ret = send_command_to_service(msg);
			recv(fd,&msg,sizeof(msg),0);
			if(msg.type == -1)
			{
				printf("\033[0;31m%s\033[0m\n",msg.data);
				printf("type =%d\n",msg.type);
			}
			printf("%s\n",msg.data);
			printf("\033[0;34m===========================================================\033[0m\n\n");
			break;
		case 4:
		case 6:

			strncpy(msg.filename,str,strlen(str));	
			send_command_to_service(msg);//发送lpwd lls命令
			if(ret == -1)
			{
				printf("error:send_to_services.\n");
			}
			recv(fd,&msg,sizeof(struct msgdata),0);//接收反馈信息,判断是否打印
							       //	printf("msg type = %d\n",msg.type);
			if(msg.type == 2)//反馈为lpwd
			{
				printf("%s",msg.data);
				//			break;  	
			}
			else if(msg.type == -1)//反馈为命令格式错误
			{
				printf("\033[0;31m%s\033[0m\n",msg.data);
				printf("\033[0;34m===========================================================\033[0m\n\n");
				break;
			}else{

				while(1)
				{

					if(msg.type == 13)
					{
						break;
					}
					ret = recv(fd,&msg,sizeof(msg),0);
					if(ret == -1)
					{
						perror("recv");
						break;
					}else if(ret == 0)
					{
						printf("services quit:\n");
						break;
					}else{
						printf("%s",msg.data);
					}
					bzero(msg.data,sizeof(msg.data));

				}

			}
			printf("\033[0;34m===========================================================\033[0m\n\n");

			break;
		case 7://实现cp功能,向服务器发文件
			bzero(&msg,sizeof(msg));
			strncpy(msg.filename,str,strlen(str));
			send_command_to_service(msg);//文件名及路径传给服务器

			p = strtok_str(str,1);//获取本地文件名及路径
			send_file_to_services(p);//打开本地文件并传输文件
			break;
		case 8:
			strncpy(msg.filename,str,strlen(str));
			send_command_to_service(msg);//命令发送到,服务器

			p = strtok_str(str,2);
			recv_file_from_services(p);	

			break;

		case 9:
			printf("client quit\n");
			strncpy(msg.filename,str,strlen(str));
			send_command_to_service(msg);//命令发送到,服务器
			return 13;	
			break;
		default:
			printf("\033[1;31miuput error:\033[0m\n");

	}
	return 0;
}


int send_command_to_service(struct msgdata msg)//部分解析后的命令发送到服务器:lls lpwd lcd quit

{
	int ret = send(fd,&msg,sizeof(msg),0);
	if(-1 == ret)
	{
		perror("send");
		exit(-1);
	}
	return 0;
}



int send_file_to_services(char *filename)//发送文件到服务器,cp
{

	int ret ,i = 0,j,s=0;
	struct msgdata msg;
	float g;
	int size  = sizeof(msg.data);
	char buf[130] = {0};//精度条
	memset(buf,'-',100);
	int k =0 ;
	char buf1[50] = {0};
	int op_fd = open(filename,O_RDONLY);// 打开本地文件
	if(op_fd == -1)//打开失败
	{
		msg.type = -1;//告诉服务器本地文件不存在,无法传输文件
			      //perror("open");
		printf("\033[1;31m本地文件不存在,无法完成传输\033[0m\n");
		send(fd,&msg,sizeof(msg),0);//告诉服务器文件打开失败
		return -1;
	}else{//打开成功

		msg.file_len = lseek(op_fd,0,SEEK_END);//获取文件大小
		lseek(op_fd,0,SEEK_SET);
		if(msg.file_len == 0)
		{
			msg.type =  -1;
			printf("\033[1;31m上传文件为空,不支持传输\033[0m\n");
			send(fd,&msg,sizeof(msg),0);//告诉服务器,传输终止
			return -1;
		}
		msg.type =1;
		msg.j = msg.file_len/size +  (((msg.file_len%size) > 0)?1:0);//获取本次传输次数
		send(fd,&msg,sizeof(msg),0);//告诉服务器,发送的次数\文件大小
		j = msg.j;
	}

	//等待服务器创建新文件是否成功
	struct msgdata msg2;
	recv(fd,&msg2,sizeof(msg2),0);
	if(msg2.type == -1)//服务器创建文件失败,无法完成传输
	{
		printf("\033[1;31m服务器创建文件失败,或文件已经存在,无法完成本次传输.\033[0m\n");
		close(op_fd);
		return -1;
	}

	if(vip == 1)//判断是否为vip,进而决定传输速率
	{
		s =1;
		  printf("\033[1;32m亲爱滴VIP用户,正在为你加速\033[0m\n");
	}else{
		s =6;
	}
//	printf("vip =%d , s =%d \n",vip,s);
	while(1)
	{
		i++;
		memset(msg.data,0,sizeof(msg.data));

		ret = read(op_fd,msg.data,sizeof(msg.data));
		if(ret == -1)
		{
			perror("read");
			close(op_fd);
			return -1;
		}

		send(fd,&msg,sizeof(msg),0);
		 g = (((float)i/j)*100 );
		while(k < (int)g)
		{
			buf[k++] ='*';
			printf("\033[0;32m [输入1(暂停)或2(恢复)]: <<%s>>\033[0;31m[%4.2f%%]\033[0m\033[0m \r",buf,g);
			fflush(stdout);
			usleep(70000*s);
		}

		pthread_t tid;

		if((i == 1) && (j >1))//当传输次数大于1时,第一次传输结束就开线程
		{
			pthread_interrupt(&tid);
		}
		if(pthread_type == 1)
		{

			while(1)
			{
				bzero(buf1,sizeof(buf1));
				if(pthread_type == 1)
				{
					//	fflush(stdin);
					fgets(buf1,sizeof(buf1),stdin);	
					if((strlen(buf1) != 2 ) || ( buf1[0] != '2')  )
					{
						printf("\033[1;31m格式错误 \033[0m\n");
					}else{
						pthread_type =2;
						break;
					}
				}
			}	
		}
		if(j == i)
		{
			pthread_type = -1;//传输结束,全局变量赋值为-1,线程退出
			pthread_cancel(tid);
			pthread_join(tid,NULL);
			break;
		}
	}
	printf("\n\033[1;36m文件传输完毕.\033[0m\n");
			printf("\033[0;34m===========================================================\033[0m\n\n");
}




int recv_file_from_services(char* str)//接收文件,lcp
{

	int ret,i=0,j,op_fd,s=0;
	int tail;
	char buf[130] = {0};
	memset(buf,'-',100);
	int k = 0;

	struct msgdata msg;
	int size = sizeof(msg.data);
	bzero(&msg,sizeof(msg));

	recv(fd,&msg,sizeof(msg),0);//确认服务器,文件是否打开成功
	if(msg.type  == -1)//打开失败,终止本次传输
	{
		printf("\033[%s\033[0m\n\n",msg.data);
		return -1;
	}else{//打开成功,接收本次传输的信息,次数
		j = msg.j;
		tail =  msg.file_len - (j-1)*(size);//最后一次的长度
		printf("%s\n",msg.data);
	}


	struct msgdata msg2;
	op_fd = open(str,O_CREAT|O_EXCL|O_RDWR,0666);//创建文件
	if(op_fd == -1)
	{
		perror("open");
		printf("\033[0;31m创建文件失败,本地文件或已经存在\033[0m\n");
		msg2.type =-1;
		send(fd,&msg2,sizeof(msg2),0);//告诉服务器,这边创建失败。不要发送。终止传输
		return -1;
	}else{
		msg2.type =1;
		send(fd,&msg2,sizeof(msg2),0);//告诉服务器,这边创建成功。可以发送,开始接收数据
	}


	if(vip == 1)
	{
		s =1;
		  printf("\033[1;32m亲爱滴VIP用户,正在为你加速\033[0m\n");
	}else{
		s =6;
	}

		pthread_t tid2;	
	while(1)
	{
		if(i == 0)
		{
			pthread_interrupt2( &tid2 );//建立线程 ,lcp(下载文件)
		}
		i++;
		bzero(msg.data,sizeof(msg.data));
		ret = recv(fd,&msg,sizeof(msg),0);//接收服务器文件数据
		if(ret == -1)
		{
			perror("read");
			break;
		}
		int ret = write(op_fd,msg.data, ((i<j)?size:tail) );//写入本地文件
		if(ret == -1)
		{
			perror("write:");
		}

		float g = (((float)i/j)*100 );
		while(k < (int)g)
		{
			buf[k++] ='*';
			printf("\033[0;32m[输入1(暂停)或2(恢复)]: <<%s>>\033[0;31m[%4.2f%%]\033[0m\033[0m \r",buf,g);//打印进度条	

			fflush(stdout);
			usleep(70000*s);
		}


		if(i == j)
		{
			pthread_cancel(tid2);//数据传输完成,结束发送线程
			pthread_join(tid2,NULL);
			break;
		}	
	}
	printf("\n\033[1;36m文件传输完毕.\033[0m\n");
			printf("\033[0;34m===========================================================\033[0m\n\n");
	return 0;
}






pathread.c(线程模块,实现恢复暂停下载)

#include"pthrea.h"
#include"demo.h"
extern int pthread_type ;
extern int fd;
int pthread_interrupt(pthread_t * tid)

{
	if(pthread_create(tid,NULL,input,NULL) != 0)
		perror("pthread_create");
	return 0;
}

void * input()
{
	while(1)
	{
		if( pthread_type == -1 )
		{
			pthread_exit(NULL);
			break;
		}else
		{
			if( pthread_type  == 2 )
			{

				char buf[50] = {0};
				fgets(buf,sizeof(buf),stdin);
			//	printf("%s\n",buf);
				if((strlen(buf) != 2)  || (buf[0] != '1')  )
				{
			printf("\033[1;31m格式错误\033[0m\n");
				}else{
					if(buf[0] == '1')
					{
						pthread_type = 1;
					}
				}
			}
		}
	}
}




int pthread_interrupt2(pthread_t * tid)

{
	if(pthread_create(tid,NULL,input2,NULL) != 0)
		printf("pthread_create error\n");
	return 0;
}

void * input2( )
{
	char buf[50] = {0};
	struct msgdata msg;
	while(1)
	{
		fgets(buf,sizeof(buf),stdin);
		if((strlen(buf) != 2) || ((buf[0] != '1') && (buf[0] != '2') ))
		{
			printf("\033[1;31m格式错误\033[0m\n");
		}else{
		
			if(buf[0] == '1')
			{
				msg.type_pth = 1;
			}else if(buf[0] == '2')
			{
			
				msg.type_pth = 2;
			}
			int ret =send(fd,&msg,sizeof(msg),0);//告诉服务器暂停\恢复发送数据
			if(ret == -1)
			perror("send");	
		//	printf("msgtype  =%d ret = %d  fd = %d\n",msg.type_pth,ret,fd);
		}
	}
}






sqlite.c(登录与注册--客户端)

#include"sqlite.h"
extern int vip;

int login_reges_to_servises(int fd)
{
	char a =0;
	int m = 9; 
	struct msgdata msg,msg1;
	bzero(&msg1,sizeof(msg1));
	//	bzero(&msg,sizeof(msg));
	recv(fd,&msg1,sizeof(msg1),0);
	while(1)
	{
		if(m == 0)
			return -1;
		char str[20] ={0};
		printf("\033[1;34mservices:%s\033[0m\n",msg1.data);
		fgets(str,sizeof(str),stdin);
		//	printf("%s\n",str);
		if(strlen(str) != 2  || (str[0] != '1' && str[0] != '2'))
		{
			printf("\033[0;31m格式错误,请输入整数‘1’(登陆)或‘2’(注册).你还有%d次机会. \033[0m\n",--m);
		printf("\033[0;34m===========================================================\033[0m\n\n");
			continue;
		}else{
			a = str[0];
		}
		if(a == '1')
		{
			bzero(&msg,sizeof(msg));
			if(get_log(&msg,&m) == -1)
			{
				msg.type  = -1;
				send(fd,&msg,sizeof(msg),0);
				return -1;
			}else{
				msg.type =1;
			//	printf("msg:%s\n",msg.data);
				send(fd,&msg,sizeof(msg),0);//发送信息
							    //		bzero(&msg,sizeof(msg));
				recv(fd,&msg,sizeof(msg),0);
			//	printf("msg.type = %d\n",msg.type);
				if(msg.type == -1)
				{
					printf("\033[0;31m密码或帐号错误,你还有%d次机会\033[0m\n",--m);
		printf("\033[0;34m===========================================================\033[0m\n\n");

					if(m == 0)
						return -1;
					continue;
				}else{
					if(vip == 1 )
					{
					
					printf("\033[1;34m登陆成功,亲爱的VIP。\033[0m\n");
		printf("\033[0;34m===========================================================\033[0m\n\n");
					}else{
					
					
					printf("\033[1;34m登陆成功,你是普通用户,下载、上传速度不是很妙.\033[0m\n");
		printf("\033[0;34m===========================================================\033[0m\n\n");
					}
					return 13;
				}

			}

		}else if(a == '2')
		{
			bzero(&msg,sizeof(msg));
			/*	
				if(get_log(&msg,&m) == -1)
				{	msg.type = -1;
				send(fd,&msg,sizeof(msg),0);
				return -1;
				}*/

			puts("\033[1;34m是否需要会员,y/n\033[0m");
			memset(str,0,sizeof(str));
			fgets(str,sizeof(str),stdin);
			if((strlen(str)) != 2  || (str[0] != 'y' && str[0] != 'n'))
			{
				printf("\033[0;31m格式错误,请输入整数‘y'或‘n’,你还有%d次机会.\033[0m\n",--m);
		printf("\033[0;34m===========================================================\033[0m\n\n");
				continue;
			}
			
			if(str[0] == 'y')
			{
				vip = 1;
				msg.j =1;
			}else{
				vip = 0;
				msg.j = -1;
			}


			if(get_log(&msg,&m) == -1)
			{	msg.type = -1;
				send(fd,&msg,sizeof(msg),0);
				return -1;
			}else{

				msg.type =2;
			}


			send(fd,&msg,sizeof(msg),0);//发送信息	

			recv(fd,&msg,sizeof(msg),0);
			if(msg.type == 1)
			{
				vip *= 1;
				printf("\033[1;34m注册成功\033[0m\n");
		printf("\033[0;34m===========================================================\033[0m\n\n");
			//	printf("%d\n",vip);
				continue;
			}else{
				vip *= 0;

				printf("\033[1;31m%s\033[0m\n",msg.data);
				printf("\033[1;31m注册失败,还有%d次机会\033[0m\n",--m);
		printf("\033[0;34m===========================================================\033[0m\n\n");
			//	printf("%d\n",vip);
				continue;
			}

		}else{
		printf("\033[0;31m格式错误\033[0m\n");
		printf("\033[0;34m===========================================================\033[0m\n\n");
		continue;	
	}
}
return 0;
}


int get_log(struct msgdata *msg,int *n)
{
	char buf1[50] = {0};
	char buf2[50] = {0};
	char *p =NULL;

	while(1)
	{
		if(*n == 0)
			return -1;

		fflush(stdin);
		puts("\033[1;34m输入帐号(不超过8位的字符,不能包含空格)\033[0m");
		fgets(buf1,sizeof(buf1),stdin);
		p = buf1 + strlen(buf1);
		*(--p) = '\0';
	
		puts("\033[1;34m输入密码(不超过8位的字符,不能包含空格)\033[0m");
		fgets(buf2,sizeof(buf2),stdin);
		p = buf2 + strlen(buf2);
		*(--p) = '\0';

		
		if(strlen(buf1) >8 || strstr(buf1," ") != NULL  || strlen(buf2) >8 || strstr(buf2," ") != NULL )
		{
			*n -=1;
			printf("\033[0;31m格式错误,重新输入,你还有%d次机会\033[0m\n",*n);
		printf("\033[0;34m===========================================================\033[0m\n\n");
				continue;
		}else{
			
			printf("\033[1;34m格式正确\033[0m\n");
	
			sprintf(msg->data,"%s %s",buf1,buf2);
		//	printf("%s\n",msg->data);
			return 0;
		}
	
	//	printf("n =%d\n",n);
	}
	return 0;
}





demo.h

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>



#define cd 	1
#define lcd 	2
#define ls 	3
#define lls 	4
#define pwd 	5
#define lpwd 	6
#define cp 	7
#define lcp	8
#define quit	9
struct msgdata{
	char filename[50];
	char data[300];
	int type;
	int file_len;
	int j;
	int type_pth;
};


char * strtok_str(char*,int i);//分割字符串
int connect_services(int argc,char* ip, char * port);//链接服务器
int get_command(char *str,int size,int *type);//解析命令 
int deal_comman(int type,char* str);//处理命令

int send_command_to_service(struct msgdata msg);//lls lpwd 部分解析后命令发送服务器
int send_file_to_services(char *filename);//发送文件到服务器

int recv_file_from_services(char* str);//接收文件


pthread.h

#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include<stdio.h>
int pthread_interrupt( pthread_t*);//建立线程, cp(上传文件)
int pthread_interrupt2( pthread_t*);//建立线程 ,lcp(下载文件)
void * input();
void * input2();

sqlite.h

#include"demo.h"

#include<stdio.h>
#include<sqlite3.h>
#include<string.h>


sqlite3** init_seqlite(char *r,sqlite3 ** db);//创建一个数据
int init_usr(char* r , sqlite3* db);//创建用户
	
int login_reges_to_servises(int );// 客户端进入登陆注册

int client_log_to_system(int fd);

int get_log(struct msgdata* ,int*);

makefile

test:client.o demo.o pthrea.o sqlite.o

	gcc  -Wall client.o demo.o pthrea.o sqlite.o  -o test -lsqlite3 -L/usr/local/lib -I/usr/local/include  -lpthread
client.o:client.c
	gcc -c client.c  -o client.o
demo.o:demo.c
	gcc -c demo.c -o demo.o
pthread.o:pthread.c
	gcc -c pthread.c -o pthread.o
sqlite.o:sqlite.c
	gcc -c sqlite.c -o sqlite.o

.PHONY:clean
clean:
	rm -r ./*.o test 


  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:20:33  更:2022-07-20 19:21:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:09:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码