背景:任何一种词语,使用频率高了,就会成为公共词汇。英语作为一种国际通用性语言,部分单词已成为公共词汇,并且直接应用到汉语中来。因其表达简洁,准确而受到人们青睐。但是,这些新引入或者比较专业词汇,对于较少关注新闻,接触网络的群体而言,就会造成阅读困难。传统的纸质英汉词典因为更新周期久,携带不便,价格相对昂贵而不能广泛应用。方便,高效,快捷的电子词典才能满足现代人的需要。

项目描述:本系统是一个网络电子词典,主要功能是为用户提供一个能实现查询用户注册,用户登陆和查询单词机查询历史记录等功能。本文从项目的角度进行了简单的描述。该系统是基于Linux系统下利用了网络的基本知识,及sqlite数据库的相关知识。这套系统采用的是tcp协议并发服务器设计,可以满足多用户同时登录,用户登录后可以查询单词及历史记录,对于数据的存储我们采用的是sqlite数据库技术。

功能:

1、登录:输入正确的账号和密码。

2、注册:注册账号和密码,保存在数据库中,注册已经存在的账号会提示账号已经存在,并                注册失败。

3、查询单词:通过数据库查询单词,如果有则返回单词注释,没有则返回无此单词。

4、查询搜索历史:用time函数获取搜索时的时间,利用文件IO技术来保存搜索历史。

5、退出。

运用技术点:

1、运用的是tcp协议进行网络通信,确保了传输的稳定性。

2、使用多进程来进行运行,提高运行效率,子进程负责登录,注册,查询等功能。

3、使用signal函数检测子进程改变状态,回收僵尸态子进程,释放子进程的资源。

4、传输数据包内容的时候同时传输数据包大小,可以解决粘包问题。

5、数据库中手动创建两个表,一个是要查询的单词及解释,另一个是保存账户和密                             码,方便实现不同的功能。

6、 采用文件IO的方式创建一个文件来保存查询历史,利用time函数能获得查询时间 一                         起保存。

结构体的设计:

注:__attribute__((__packed__))表示取消结构体对齐
typedef struct
{
char _username[25];  //用户名
char _password[25];     //密码
} __attribute__((__packed__))user_t;   typedef struct
{
int  type;        //判断要实现的对应功能
int  size;        //解决粘包问题
union
{
user_t  uinfo;    //用户信息
char    _word[100];
} content;//客户端填词,服务端填写单词解释
#define word            content._word
#define username        content.uinfo._username
#define password       content.uinfo._passwd
}__attribute__((__packed__))mhead_t;

头文件:

#ifndef _HEAD_H_
#define _HEAD_H_#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int do_register(int sockfd,sqlite3 *pdb,char *_username,char *_password);
int do_client(int sockfd,sqlite3 *pdb);
int do_login(int sockfd,sqlite3 *pdb,char *_username,char *_password);//消息的类型
#define USER_REGISTER    10
#define USER_LOGIN       20
#define USER_WORD        30
#define USER_SUCCESS     40
#define USER_FAILURE     50
#define USER_HISTORY     60//__attribute__((__packed__))
//作用:告诉编译器在编译的过程中取消优化对齐。
//      方便我们在发送和接收数据的时候一个字节一个字节的排列
typedef struct
{char _username[25]; //用户名char _password[25]; //密码
}__attribute__((__packed__))user_t;typedef struct
{int type;           //消息类型int size;           //消息大小union{user_t uinfo;        //用户信息char   _word[1000]; }content;//客户端填单词,服务端填写单词解释
#define word         content._word
#define username     content.uinfo._username
#define password       content.uinfo._password
}__attribute__((__packed__))mhead_t;//'\'表示多行链接上一行表示, #deifne ....do...while(0);
//表示封装成独立的语法单元,防止被语法错误。
//注意:'\'之后不要留空格,要不然编译会有警告#define EXEC_SQL(db,sql,errmsg) do{\if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) < 0)\{\fprintf(stderr,"sqlite3 execl [%s] error : %s.\n",sql,errmsg);\exit(EXIT_FAILURE);\}\
}while(0);#endif

服务器源码:

#include "head.h"void signal_handler(int signum)
{waitpid(-1,NULL,WNOHANG);return;
}
int init_tcp(char *ip,char *port)
{int sockfd;struct sockaddr_in server_addr;if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){perror("Fail to socket");exit(EXIT_FAILURE);}bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port   = htons(atoi(port));server_addr.sin_addr.s_addr = inet_addr(ip);if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0){perror("Fail to bind");exit(EXIT_FAILURE);}listen(sockfd,5);printf("listen....\n");return sockfd;
}//.server ip port db
//数据库中已经手动创建了2个表:user_table,word_table
//注:由于我们后面函数要传承,故这里的const应该去掉
int main(int argc, char *argv[])
{int pid;   sqlite3 *pdb;int listenfd,connect_fd;int addr_len = sizeof(struct sockaddr);struct sockaddr_in peer_addr;if(argc < 4){fprintf(stderr,"Usage : %s ip port system.db.\n",argv[0]);exit(EXIT_FAILURE);}//探测子进程的改变状态,回收僵尸态子进程if(signal(SIGCHLD,signal_handler) == SIG_ERR){perror("Fail to signal");exit(EXIT_FAILURE);}if(sqlite3_open(argv[3],&pdb) != SQLITE_OK){fprintf(stderr,"sqlite3 open %s : %s.\n",argv[3],sqlite3_errmsg(pdb));exit(EXIT_FAILURE);}//初始化tcp连接,得到监听套接字listenfd = init_tcp(argv[1],argv[2]);//提取客户段的链接请求,创建子进程和客户端交互while(1){if((connect_fd = accept(listenfd,(struct sockaddr *)&peer_addr,&addr_len)) < 0){perror("Fail to accept");exit(EXIT_FAILURE);}if((pid = fork()) < 0){perror("Fail to fork");exit(EXIT_FAILURE);}//创建子进程处理客户端的请求if(pid == 0){close(listenfd);do_client(connect_fd,pdb);}close(connect_fd);}exit(EXIT_SUCCESS);
}
#include "head.h"
//注册
int do_register(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{char *errmsg;char buf[1024];char **dbresult;int nrow = 0,ncolumn = 0;char sql[1024] = {0};mhead_t *head = (mhead_t *)buf;  sprintf(sql,"select * from user_table where NAME='%s';",_username);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}//没有这样的用户名if(nrow == 0){//录入数据库bzero(sql,sizeof(sql));sprintf(sql,"insert into user_table values('%s','%s');",_username,_password);EXEC_SQL(pdb,sql,errmsg);printf("ok ........\n");head->type = USER_SUCCESS;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//用户名存在}else{head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//表示未知    printf("???????\n");}//插入到数据库之后,释放dbresult结果sqlite3_free_table(dbresult);return 0;
}
//登录
int do_login(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{char *errmsg;char buf[1024];char **dbresult;int nrow = 0,ncolumn = 0;char sql[1024] = {0};mhead_t *head = (mhead_t *)buf;  sprintf(sql,"select * from user_table where NAME='%s' and PASSWORD='%s';",_username,_password);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}//没有这样的用户名和密码if(nrow == 0){//返回错误信息,登录失败head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//登录成功,用户名存在}else{head->type = USER_SUCCESS;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}printf("登录成功!\n");}//插入到数据库之后,释放dbresult结果sqlite3_free_table(dbresult);return 0;
}
//单词查询
int do_query(int sockfd,sqlite3 *pdb,char *_word)
{int fd;fd=open("history.txt",O_WRONLY|O_CREAT|O_APPEND,0666);char *errmsg;char buf[1024];char **dbresult;time_t t;time(&t);char tim1[1024];sprintf(tim1,"\n%s",ctime(&t));
//  lseek(fd,10,SEEK_END);memset(buf,0,sizeof(buf));int nrow = 0,ncolumn = 0;char sql[1024] = {0};mhead_t *head = (mhead_t *)buf;   sprintf(sql,"select * from dict_table where word='%s' ;",_word);if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0){fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);exit(EXIT_FAILURE);}//没有这样的单词if(nrow == 0){//返回错误信息,登录失败head->type = USER_FAILURE;if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//单词存在}else{write(fd,tim1,strlen(tim1));head->type = USER_SUCCESS;write(fd,_word,strlen(_word));lseek(fd,10,SEEK_END);write(fd,"             ",sizeof(20));strcpy(head->word,dbresult[ncolumn+1]);write(fd,head->word,strlen(head->word));if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}}//插入到数据库之后,释放dbresult结果close(fd);sqlite3_free_table(dbresult);return 0;
}//历史查询
int do_history(int sockfd)
{int fd1;char *errmsg;char buf[1024];char buf1[1024];char **dbresult;int nrow = 0,ncolumn = 0;mhead_t *head = (mhead_t *)buf;fd1=open("history.txt",O_RDONLY);if(fd1<0){head->type =USER_REGISTER;}else{head->type = USER_SUCCESS;}    read(fd1,buf1,sizeof(buf1));strcpy(head->word,buf1);if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send");exit(EXIT_FAILURE);}//插入到数据库之后,释放dbresult结果close(fd1);return 0;
}int do_client(int sockfd,sqlite3 *pdb)
{int n;int count = 0;char buf[1024];mhead_t *head = (mhead_t *)buf;   while(1){count = 0;//接收协议头while(1){n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){exit(EXIT_FAILURE);}count += n;printf("count : %d mhead_t : %ld\n",count,sizeof(mhead_t));if(count == sizeof(mhead_t))break;}switch(head->type){case USER_REGISTER:do_register(sockfd,pdb,head->username,head->password);    break;case USER_LOGIN:do_login(sockfd,pdb,head->username,head->password);     break;case USER_WORD:do_query(sockfd,pdb,head->word);        break;case USER_HISTORY:do_history(sockfd);     break;defalut:exit(EXIT_SUCCESS);}  }return 0;
}

客户端源码:

#include "head.h"
//用户提示界面1
void help_info1()
{printf("\t-----------------------------------------------\n");printf("\t|               HENRY   在线辞典               |\n");printf("\t|版本:0.0.1                                    |\n");printf("\t|作者:我不是高科                               |\n");printf("\t|功能:                                         |\n");printf("\t|    [1] 登录                                  |\n");printf("\t|    [2] 注册                                  |\n");printf("\t|    [3] 退出                                  |\n");printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");printf("\t------------------------------------------------\n");return;
}//查询系统界面
void help_info2()
{printf("\t-----------------------------------------------\n");printf("\t|               HENRY   在线辞典               |\n");printf("\t|版本:0.0.1                                    |\n");printf("\t|作者:我不是高科                               |\n");printf("\t|功能:                                         |\n");printf("\t|    [1] 查单词                                |\n");printf("\t|    [2] 查询历史记录                          |\n");printf("\t|    [3] 退出查询系统                          |\n");printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");printf("\t------------------------------------------------\n");return;
}//用户输入指令,供大家选择
enum{LOGIN    = 1,  //登陆REGISTER = 2,  //注册QUIT     = 3,  //退出QUERY    = 1,  //查询单词HISTORY  = 2,  //查询历史
};int init_tcp(char *ip,char *port)
{int sockfd;struct sockaddr_in server_addr;if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){perror("Fail to socket"); exit(EXIT_FAILURE);}bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(port));server_addr.sin_addr.s_addr = inet_addr(ip);if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0){perror("Fail to bind");   exit(EXIT_FAILURE);}return sockfd;
}
//查询单词
int do_query(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在查询单词\n");head->type = USER_WORD;head->size = sizeof(mhead_t);printf("Input world : ");fgets(head->word,sizeof(head->word),stdin);head->username[strlen(head->username) - 1] = '\0';      //去掉回车//发给服务器端if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send2");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n < 0){perror("Fail to send3");exit(EXIT_FAILURE);}else if(n ==0 ){perror("Fail to send5");break;};//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n%s\n",head->word);    return 0;}else{printf("\nNo such word:\n");   return -1;}}//历史查询
int do_history(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在查询记录\n");head->type = USER_HISTORY;head->size = sizeof(mhead_t);if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send2");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n < 0){perror("Fail to send3");exit(EXIT_FAILURE);}else if(n ==0 ){perror("Fail to send5");break;};//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n%s\n",head->word);    return 0;}else{printf("\nerror\n");   return -1;}}int do_task1(int sockfd)
{int cmd;while(1){//提示界面帮助,用户选择help_info2(); printf("\n\n请选择>");scanf("%d",&cmd);//吃掉回车键getchar();switch(cmd){//查询单词case QUERY:if(do_query(sockfd) < 0)         continue;break;//查询历史记录case HISTORY:   if(do_history(sockfd) < 0)continue;break;//退出查询系统         case QUIT:                  exit(EXIT_SUCCESS);default:printf("Unknow cmd.\n");continue;}}return 0;
}//注册
int do_register(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在注册,请输入用户名和密码\n");head->type = USER_REGISTER;head->size = sizeof(mhead_t);printf("Input username : ");fgets(head->username,sizeof(head->username),stdin);head->username[strlen(head->username) - 1] = '\0';      //去掉回车printf("Input password : ");fgets(head->password,sizeof(head->password),stdin);head->password[strlen(head->password) - 1] = '\0';//发给服务器端if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send0");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n <= 0){perror("Fail to sendi0");exit(EXIT_FAILURE);}//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n恭喜您,注册成功!\n"); return 0;}else{printf("\n很遗憾,这个用户名已经被其它用户注册过了,请重新注册");  return -1;}}int do_login(int sockfd)
{int n = 0;int count = 0;char buf[1024] = {0};//定义发送的协议头mhead_t *head = (mhead_t *)buf;printf("\n您正在登录,请输入用户名和密码\n");head->type = USER_LOGIN;head->size = sizeof(mhead_t);printf("Input username : ");fgets(head->username,sizeof(head->username),stdin);head->username[strlen(head->username) - 1] = '\0';      //去掉回车printf("Input password : ");fgets(head->password,sizeof(head->password),stdin);head->password[strlen(head->password) - 1] = '\0';//发给服务器端if(send(sockfd,buf,sizeof(mhead_t),0) < 0){perror("Fail to send2");exit(EXIT_FAILURE);}bzero(&buf,sizeof(buf));while(1){//接收数据,TCP是可靠的连接,若是数据//未完全接收的话,可以在接收n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);if(n < 0){perror("Fail to send3");exit(EXIT_FAILURE);}else if(n ==0 ){perror("Fail to send5");break;};//若是数据未发送完成,再次接收的时候可补充count += n;if(count == sizeof(mhead_t))break;}if(head->type == USER_SUCCESS){printf("\n恭喜您,登录成功!\n");   do_task1( sockfd);return 0;}else{printf("\n登录失败,用户名或者密码错误!\n"); return -1;}}int do_task(int sockfd)
{int cmd;while(1){//提示界面帮助,用户选择help_info1(); printf("\n\n请选择>");scanf("%d",&cmd);//吃掉回车键getchar();switch(cmd){//用户注册,我们先来写注册的函数case REGISTER:if(do_register(sockfd) < 0)         //注册continue;break;//用户登陆case LOGIN:   if(do_login(sockfd) < 0)continue;break;case QUIT:                     exit(EXIT_SUCCESS);default:printf("Unknow cmd.\n");continue;}}return 0;
}//./client ip port
//由于后面要传递参数,故这里的const省略
int main(int argc, char *argv[])
{int sockfd;    int addr_len = sizeof(struct sockaddr);struct sockaddr_in peer_addr;if(argc < 3){fprintf(stderr,"Usage : %s argv[1] argv[2]\n",argv[0]);  exit(EXIT_FAILURE);}sockfd = init_tcp(argv[1],argv[2]);do_task(sockfd);return 0;
}

基于TCP协议的电子词典相关推荐

  1. 用C#实现基于TCP协议的网络通讯

    TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...

  2. MFC基于TCP协议的CSocket类套接字服务器端代码示范

    MFC基于TCP协议的CSocket类套接字服务器端代码示范 https://blog.csdn.net/txwtech/article/details/93417667 转载于:https://ww ...

  3. 基于 tcp协议完成的上传下载

    ps: 下面两个版本全部是 基于 tcp协议完成的. 简单版(无注释): 服务器(server)代码: from socket import socket sk = socket() sk.bind( ...

  4. 网络编程应用:基于TCP协议【实现对象传输】--练习

    要求: 基于TCP协议实现,客服端向服务器发送一个对象 服务器接受并显示用户信息 ,同时返回给客户端 "数据已收到"建一个Student类,属性:name age Student类 ...

  5. 网络编程应用:基于TCP协议【实现文件上传】--练习

    要求: 基于TCP协议实现一个向服务器端上传文件的功能 客户端代码: package Homework2;import java.io.File; import java.io.FileInputSt ...

  6. 网络编程应用:基于TCP协议【实现一个聊天程序】

    要求: 基于TCP协议实现一个聊天程序,客户端发送一条数据,服务器端发送一条数据 客户端代码: package Homework1;import java.io.IOException; import ...

  7. python 网络编程 套接字的初使用 基于TCP协议的socket

    文章目录 基于TCP协议的socket server端 client端 尝试启动 基于TCP协议的socket tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 server端 # ...

  8. 浅析C#基于TCP协议的SCOKET通信

    TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...

  9. python中基于tcp协议的通信(数据传输)

    tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...

  10. 异常处理、socke基于TCP协议编程

    一.异常处理 1.错误和异常 1.程序中难免出现错误,而错误分成两种 (1)语法错误(这种错误过不了Python解释器的语法检测,必须在程序执行前改正) #语法错误示范一 if#语法错误示范二 def ...

最新文章

  1. 都是虚拟串口软件惹的祸
  2. Spring AOP编程-传统基于aspectJ切点AOP开发
  3. 216. Combination Sum III 组合总和 III
  4. iframe内嵌框架自适应高度
  5. 通州区机器人比赛活动总结_马驹桥镇中心小学在2017通州区青少年机器人竞赛中勇创佳绩...
  6. 学习TeXworks编辑器(二)TAB补全详解与自定义补全命令总结
  7. Windows下如何生成数字证书
  8. python day33
  9. 每周荐书:大数据、深度学习、架构(评论送书)
  10. PHP开发工资条短信通知
  11. Python 使用openpyxl处理Excel文件
  12. c语言编程吉他和弦,学吉他和弦怎么弹啊?就C大调C,Dm,Em,F,G,Am,Bdim.1,2,3,4,5,6,7级和弦举个简单的例帮帮我...
  13. 小白到学会python要多久_零基础小白多久能学会python
  14. 年轻人的第一次破产,从二次元开始
  15. U盘安装原版XP系统全攻略
  16. 浏览器被强制修改成 桔梗网—Google, Firefox
  17. “史上“最疯狂”的顶级数学家,看完后忍不住感慨太神了”
  18. 24 张图总结 TCP 基础知识,看完我飘了。
  19. Licode Demo搭建
  20. 感悟西游记——成气候的妖精都是领导家的

热门文章

  1. 数据库系统概论第五版(第 5 章 数据库完整性)笔记
  2. excel (wps) 文件受保护处理方案
  3. 华胜天成助力中国银联实现移动互联时代的多渠道扩张
  4. 文件夹html文件批量替换,DirRplacer(文件批量替换工具)
  5. 【Java爬虫】接口模拟微信公众号登录,搜索公众号,获取公众号文章,java实现
  6. Monkey命令详解
  7. linux初级:用useradd SB2,来建立新账户时,显示 bash:useradd:command not found的解决方法
  8. android fastboot 最新版,用adb、fastboot拯救你的安卓手机
  9. windows下Mysql 5.7 绿色版安装与卸载教程,MySQL可视化管理
  10. 为什么在 Windows 7系统下无法显示 STEP 7 MicroWin SP9的帮助文件?