超详细:实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。--注册和登录(但之后会连续更新内容,直至全部实现)
- 前言
在学完不够全面的Linux操作系统编程后(这也意味着我后期也要不断学习,这也符合我活到老学到老的人生观点),需要以一些项目来检测自己的所学,毕竟实践见真章。
所以在今后的几天里,我将以无界面聊天室为例子,来以我个人查阅相关资料后总结并介绍所用到的知识点,来以作为巩固与加强。(本人刚刚入门不久,但勤奋爱学,较为聪慧(本人自封,见笑了),如有错误,望见谅与通知改正)同时,我也会根据相应进度与情况来更新我的简书。接下来就进入正题吧。
一、确定无界面聊天室的大概功能。
我以当今流行的几个聊天软件为参考,大概先确定和聊天相关的一些功能。
Linux 环境下的简易聊天室,采用C/S模型,实现多客户端之间的稳定数据传输。
登陆注册账号,登陆时密码不会显
好友管理
a. 查看好友列表(不同颜色代表不同的状态)
b. 添加好友
c. 删除好友
d. 屏蔽好友
群管理
a. 查看所加群,查看群中成员
b. 创建群
c. 加群
d. 退群
e. 解散群
f. 设置管理员
g. 踢人
聊天通讯
a. 私聊
b. 群聊
c. 查看聊天记录
离线传输
a. 离线消息
b. 离线文件
传送文件
容错处理
对服务器要求
a. 面对同时大量的客户端发送连接与数据时,服务器能正常处理
b. 客户端的意外退出,不会导致服务器退出
c. 数据能够正确接收和发送
二、基于功能来理顺代码实现大概思路。
登陆、注册账号
首先注册账号,然后才可以谈登录一说,所以我们先实现注册账号。但思考一下,这个用户账号的信息应该需要属性呢?(成员变量),不如先创建一个用户结构体,先加入一些用得着的变量,如果在之后需要增、删、用户的属性,直接对成员变量修改就行,并且也可以一目了然的确定用户的属性有哪些。就以当前注册、登陆为目的的属性并参考一些流行的聊天软件,我们要需要三个成员变量,用户名、账号、密码。
struct Usr{char name[20];char account[10];char password[16];}
注册的时候我们可以先输入用户名,然后自动分配账号,之后手动设置密码。设置密码的时
mysql> use easytalk Database changed mysql> create table usr(name VARCHAR(20),account VARCHAR(10),password VARCHAR(16),PRIMARY KEY(account)); Query OK, 0 rows affected (0.01 sec)
候需要二次核验密码,以减少用户输入密码后所记住的密码与输入的不一致或者忘记密码情况。如果用户输入用户名和密码格式不对会被提示重新注册,有三次连续机会。当错误操作达到三次是,就跳出注册操作,进入主界面。如果注册操作成功,将注册信息存到mysql数据库里面
(Linux本身并不带有Mysql,需要手动下载,在Ubuntu终端界面输入下面代码即可)
sudo apt-get install mysql-server //安装mysql
可以提前创建一个easytalk的数据库
mysql> create database easytalk; Query OK, 1 row affected (0.02 sec)
(database),然后在use(使用)这个database,之后用create语句创建一个usr的一个表,里面来存放用户的三个成员变量,name、account、password。我们在插入到数据表时,应该设置一个主键,那就是账号,用户的账号不能相同,但是用户名可以相同。
mysql> use easytalk Database changed mysql> create table usr(name VARCHAR(20),account VARCHAR(10),password VARCHAR(16),PRIMARY KEY(account)); Query OK, 0 rows affected (0.01 sec)
登陆的时候我们需要输入账号和密码,然后查询数据库,能查询出来相关的账号密码,那就允许登陆。
下面为主要代码部分
声明用到的全局变量和将要马上用到的全局变量
MYSQL mysql; MYSQL *pmysql=NULL; MYSQL_RES *res=NULL; char slc[1024]=""; //全局变量用来存放查询语句的字符串 unsigned int num_fields;struct sockaddr_in ser_addr,cli_addr; int sockfd=0; char str[MAXSIZE]={0}; char buf[MAXSIZE]={0}; pthread_t thread;
先要连接下数据库
101 int InitDB() 102 { 103 pmysql=mysql_init(&mysql); 104 //mysql_options(&mysql,MYSQL_OPT_COMPRESS,0); 105 //mysql_options(&mysql,MYSQL_INIT_COMMAND,"SET autocommit=1"); 106 107 pmysql=mysql_real_connect(&mysql, //数据库连接处理数据结构,如同FILE* 108 "localhost", //MySQL服务器运行的主机名 109 "root", //MySQL服务端提供的用户名 110 "0", //MySQL服务端提供的用户密码,我的数据库没密码,所以0就可以了,然后管理员模式执行代码 111 "easytalk", //MySQL连接时指定连接到的数据库 112 0, //端口号 113 NULL,//指定为套接字或者命名管道 114 0);//标志,通常为0 115 //printf("mysql_real_connect success\n"); 116 117 }
创建一个建议的登录界面
156 void Window() // 用户进入用户端界面 157 { 158 printf("/ ******************************************************************************************** /\n"); 159 if(Loggingstatus==0) 160 { 161 printf("/ **** 欢迎进入easytalk 当前为未登陆状态 *** ***** /\n"); 162 } 163 else 164 { 165 printf("/ **** 欢迎用户%-20s 当前为已登陆状态 ******** /\ n",USR.name); 166 167 } 168 printf("/ **** 请输入你想操作的操作码 ******** /\n"); 169 printf("/ **** 0.退出 ******** /\n"); 170 printf("/ **** 1.注册账号 ******** /\n"); 171 printf("/ **** 2.登陆账号 ******** /\n"); 172 printf("/ **** 3.退出当前登陆 ******** /\n"); 173 printf("/ ******************************************************************************************** /\n"); 174 }
下面为注册账号的代码,其中需要规定用户名和密码的格式,同时也需要二次校验密码。最后添加到数据库,并同时为用户创建一个表,用户表我们暂时存放好友列,群列表和屏蔽好友列表三个成员。
175 int Register() 176 { 177 char nm[1024]={0}; 178 char ac[1024]={0}; 179 char ps1[1024]={0}; 180 char ps2[1024]={0}; 181 int count1=3; 182 int count2=3; 183 int count3=3; 184 int tmprand=0; 185 int len=0; 186 int cs=0; 187 int aclen=0; //scanf的返回值,错误返回-1 188 int row=0; 189 srand((unsigned)time(NULL)); 190 printf("创建账号需要用户名,账号,密码,其中用户名和密码手动输入,账号位随机分配8-10位\n"); 191 a: 192 printf("请输入用户名\n"); 193 cs=scanf("%s",nm); 194 len=strlen(nm); 195 if((cs==-1)||(len>20)||(len<8))//输入函数执行错误或者用户名长度不在8-20 196 { 197 count1--; 198 if(count1==0) 199 { 200 printf("输入用户名格式错误三次,即将退出注册功能\n"); 201 return -1; 202 } 203 printf("输入格式错误,请重试,你还剩余%d次机会,当机会为零将自动退出注册系统\n",count1); 204 goto a; 205 } 206 b: 207 printf("请输入密码\n"); 208 cs=scanf("%s",ps1); 209 len=strlen(ps1); 210 if((cs==-1)||(len>16)||(len<8))//第一次输入密码输入函数错误错误或者密码不在8-16位 211 { 212 count2--; 213 if(count2==0) 214 { 215 printf("输入密码格式错误三次,即将退出注册功能\n"); 216 return -2; 217 } 218 printf("输入密码格式错误,请重试,你还剩余%d次机会,当机会为0将自动退出注册系统\n",count2); 219 goto b; 220 } 221 printf("请再次输入密码\n"); 222 cs=scanf("%s",ps2); 223 len=strlen(ps2); 224 if((cs==-1)||(strcmp(ps1,ps2)!=0)) //两次密码输入不一样或者输入错误 225 { 226 count2--; 227 if(count2==0) 228 { 229 printf("输入密码格式错误三次,即将退出注册功能\n"); 230 return -3; 231 } 232 printf("两次输入的密码不一致,请重试,你还剩余%d次机会,当机会为0将自动退出注册系统\n",count2); 233 goto b; 234 } 235 do 236 { 237 aclen=rand()%3+8; 238 //printf("aclen%d\n",aclen); 239 int arr[aclen]; 240 for(int i=0;i<aclen;i++) 241 { 242 int x=0; 243 if(i==0) 244 { 245 arr[i]=rand()%9+1+48; 246 } 247 else 248 { 249 arr[i]=rand()%10+48; 250 251 } 252 ac[i]=(char)arr[i]; 253 } 254 sprintf(slc,"select * from usr where account='%s'",ac); 255 int b=mysql_real_query(&mysql,slc,strlen(slc)); 256 if(b!=0) 257 { 258 //printf("检测账号\n"); 259 perror("sql_real_query"); 260 } 261 else 262 { 263 //printf("%d\n",b); 264 } 265 res=mysql_store_result(&mysql); 266 row=mysql_num_rows(res); 267 //if(row==1) 268 mysql_free_result(res); 269 270 }while(row==1); //do while 检测自动分配的账号是否重复,重复的话重新分配账号 271 printf("分配的账号为%s\n",ac); 272 sprintf(slc,"insert into usr values('%s','%s','%s')",nm,ac,ps1); 273 //printf("%s\n",slc); 274 int r1=mysql_real_query(&mysql,slc,strlen(slc));//将注册的账号信息插入到数据表 275 int r2=0; 276 //res=mysql_store_result(res); 277 //mysql_free_result(res); 278 279 //printf("insert%d\n",r1); 280 if(r1!=0) 281 { 282 283 perror("mysql_real_query"); 284 } 285 else 286 { 287 char slc1[1024]="create table "; 288 strcat(slc1,nm); 289 strcat(slc1,ac); 290 strcat(slc1,"(friends VARCHAR(20),sheilfriends VARCHAR(20),talkgroup VARCHAR(20))"); 291 //printf("%s\n",slc1); 292 int slc1len=0; 293 int i=0; 294 while(((slc1[i]=='\0')&&(slc[i+1]=='\0')==0)) 295 { 296 slc1len++; 297 i++; 298 299 } 300 //slc1len=strlen(slc1); 301 //printf("slc1len:%d",slc1len); 302 //r2=mysql_real_query(&mysql,slc1,slc1len);//以用户名和账号共同创建个数据表,并储存好友列表,屏蔽好友列表和群列表 303 r2=mysql_real_query(&mysql,slc1,strlen(slc1));//以用户名和账号共同创建个数据表,并储存好友列表,屏蔽好友列表和群列表 304 //res=mysql_store_result(res); 305 //mysql_free_result(res); 306 } 307 /*if(r2!=0) 308 { 309 perror("mysql_real_query"); 310 }*/ 311 //printf("%d\n",r2); 312 if(r2!=0) 313 { 314 perror("mysql_real_query"); 315 316 } 317 if((r1==0)&&(r2==0)) 318 { 319 printf("注册成功\n"); 320 } 321 else 322 { 323 printf("注册失败\n"); 324 } 325 return 0; 326 327 328 }
然后为登录的函数,需要查询数据库比对,然后更改当前进程登录用户信息。
329 int Login() 330 { 331 char ac[10]={0}; 332 char ps[16]={0}; 333 char slc[1024]="select * from usr where(account='"; 334 printf("你已进入登陆系统\n"); 335 printf("请输入你的账号\n"); 336 scanf("%s",ac); 337 strncat(slc,ac,strlen(ac)); 338 printf("请输入你的密码\n"); 339 scanf("%s",ps); 340 strcat(slc,"' AND password='"); 341 strncat(slc,ps,strlen(ps)); 342 strcat(slc,"')"); 343 int ret=mysql_real_query(&mysql,slc,strlen(slc)); 344 res=mysql_store_result(&mysql); 345 int row=mysql_num_rows(res); 346 if(row==1) 347 { 348 unsigned long *lenths=0; 349 unsigned int i=0; 350 MYSQL_ROW fetchrow; 351 //num_fields = mysql_num_fields(result); 352 fetchrow=mysql_fetch_row(res); 353 strncpy(USR.name,fetchrow[0],strlen(fetchrow[0])); //修改当前程序用户信息,以便后续使用 354 strncpy(USR.account,ac,strlen(ac)); 355 strncpy(USR.password,ps,strlen(ps)); 356 Loggingstatus=1; 357 printf("登陆成功\n"); 358 return 0; 359 } 360 else 361 { 362 printf("登陆失败\n"); 363 } 364}
上面的代码为暂时的主要代码,下面为我现在进程的全部代码,有些代码没有在上面列出,但是今后也会一一介绍。
#include<stdio.h> #include<string.h> #include <stdlib.h> #include <mysql/mysql.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <poll.h> #include <sys/socket.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #define handle_error_en(en, msg) \do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)#define MAXSIZE 1024 struct Usr{char name[20];char account[10];char password[16]; }USR,Tmpusr; int Loggingstatus=0; //设置一个登陆标志,0未位有人登陆,1为已登录登陆; MYSQL mysql; MYSQL *pmysql=NULL; MYSQL_RES *res=NULL; char slc[1024]=""; //全局变量用来存放查询语句的字符串 unsigned int num_fields;struct sockaddr_in ser_addr,cli_addr; int sockfd=0; char str[MAXSIZE]={0}; char buf[MAXSIZE]={0}; pthread_t thread;int InitDB(); int Register(); int InitSocket(char const* argv1,char const* argv2); void Window(); int Login();int main(int argc,char const* argv[]) {int c=0;InitDB();InitSocket(argv[1],argv[2]);while(c!=-1){Window();scanf("%d",&c);switch(c){case 0:c=-1;break;case 1:Register();break;case 2:Login();break;case 3:Loggingstatus=0;strcpy(USR.name," ");strcpy(USR.account," ");strcpy(USR.password," ");break;default:printf("请输入争取的操作玛\n");break;}}mysql_close(&mysql);return 0;} /*****************************初始化数据库函数,连接到数据库easytalk** ************************/int InitSocket(char const* argv1,char const* argv2) {int port = atoi(argv1);int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){handle_error_en(sockfd,"socket");}ser_addr.sin_family=AF_INET;ser_addr.sin_port=htons(port);ser_addr.sin_addr.s_addr=inet_addr(argv2);socklen_t addrlen=sizeof(ser_addr);int ret=connect(sockfd,(struct sockaddr *)&ser_addr,addrlen);}int InitDB() { pmysql=mysql_init(&mysql);//mysql_options(&mysql,MYSQL_OPT_COMPRESS,0);//mysql_options(&mysql,MYSQL_INIT_COMMAND,"SET autocommit=1");pmysql=mysql_real_connect(&mysql, //数据库连接处理数据结构,如同FILE*"localhost", //MySQL服务器运行的主机名"root", //MySQL服务端提供的用户名"0", //MySQL服务端提供的用户密码"easytalk", //MySQL连接时指定连接到的数据库0, //端口号NULL,//指定为套接字或者命名管道0);//标志,通常为0//printf("mysql_real_connect success\n");} /********************************注册函数,并将用户的账号信息储存到数据库** **************************/ void *ReadMsg(void * arg) {int num=0;static char buf_r[MAXSIZE]={0};int newfd=*(int *)arg;while(1){num=recv(newfd,buf_r,MAXSIZE,0);if(strncmp(buf_r,"quit",4)==0){exit(0);}printf("recv msg:%s\n",buf_r);} } void *SendMsg(void * arg) {int num=0;static char buf_s[MAXSIZE]={0};int newfd=*(int *)arg;while(1){num=send(newfd,buf_s,MAXSIZE,0);if(strncmp(buf_s,"quit",4)==0){exit(0);}printf("send msg:%s\n",buf_s);} }void Window() // 用户进入用户端界面 {printf("/ ******************************************************************************************** /\n");if(Loggingstatus==0){printf("/ **** 欢迎进入easytalk 当前为未登陆状态 ******** /\n");}else{printf("/ **** 欢迎用户%-20s 当前为已登陆状态 ******** /\n",USR.name);}printf("/ **** 请输入你想操作的操作码 ******** /\n");printf("/ **** 0.退出 ******** /\n");printf("/ **** 1.注册账号 ******** /\n");printf("/ **** 2.登陆账号 ******** /\n");printf("/ **** 3.退出当前登陆 ******** /\n");printf("/ ******************************************************************************************** /\n"); } int Register() {char nm[1024]={0};char ac[1024]={0};char ps1[1024]={0};char ps2[1024]={0};int count1=3;int count2=3;int count3=3;int tmprand=0;int len=0;int cs=0; int aclen=0; //scanf的返回值,错误返回-1int row=0;srand((unsigned)time(NULL));printf("创建账号需要用户名,账号,密码,其中用户名和密码手动输入,账号位随机分配8-10位\n"); a:printf("请输入用户名\n");cs=scanf("%s",nm);len=strlen(nm);if((cs==-1)||(len>20)||(len<8))//输入函数执行错误或者用户名长度不在8-20{count1--;if(count1==0){printf("输入用户名格式错误三次,即将退出注册功能\n");return -1;}printf("输入格式错误,请重试,你还剩余%d次机会,当机会为零将自动退出注册系统\n",count1);goto a;} b:printf("请输入密码\n");cs=scanf("%s",ps1);len=strlen(ps1);if((cs==-1)||(len>16)||(len<8))//第一次输入密码输入函数错误错误或者密码不在8-16位{count2--;if(count2==0){printf("输入密码格式错误三次,即将退出注册功能\n");return -2;}printf("输入密码格式错误,请重试,你还剩余%d次机会,当机会为0将自动退出注册系统\n",count2);goto b;}printf("请再次输入密码\n");cs=scanf("%s",ps2);len=strlen(ps2);if((cs==-1)||(strcmp(ps1,ps2)!=0)) //两次密码输入不一样或者输入错误{count2--;if(count2==0){printf("输入密码格式错误三次,即将退出注册功能\n");return -3;}printf("两次输入的密码不一致,请重试,你还剩余%d次机会,当机会为0将自动退出注册系统\n",count2);goto b;}do{aclen=rand()%3+8;//printf("aclen%d\n",aclen);int arr[aclen];for(int i=0;i<aclen;i++){int x=0;if(i==0){arr[i]=rand()%9+1+48;}else{arr[i]=rand()%10+48;}ac[i]=(char)arr[i];}sprintf(slc,"select * from usr where account='%s'",ac);int b=mysql_real_query(&mysql,slc,strlen(slc));if(b!=0){//printf("检测账号\n");perror("sql_real_query");}else{//printf("%d\n",b);}res=mysql_store_result(&mysql);row=mysql_num_rows(res);//if(row==1)mysql_free_result(res);}while(row==1); //do while 检测自动分配的账号是否重复,重复的话重新分配账号printf("分配的账号为%s\n",ac);sprintf(slc,"insert into usr values('%s','%s','%s')",nm,ac,ps1);//printf("%s\n",slc);int r1=mysql_real_query(&mysql,slc,strlen(slc));//将注册的账号信息插入到数据表int r2=0;//res=mysql_store_result(res);//mysql_free_result(res);//printf("insert%d\n",r1);if(r1!=0){perror("mysql_real_query");}else{char slc1[1024]="create table ";strcat(slc1,nm);strcat(slc1,ac);strcat(slc1,"(friends VARCHAR(20),sheilfriends VARCHAR(20),talkgroup VARCHAR(20))");//printf("%s\n",slc1);int slc1len=0;int i=0;while(((slc1[i]=='\0')&&(slc[i+1]=='\0')==0)){slc1len++;i++;}//slc1len=strlen(slc1);//printf("slc1len:%d",slc1len);//r2=mysql_real_query(&mysql,slc1,slc1len);//以用户名和账号共同创建个数据表,并储存好友列表,屏蔽好友列表和群列表r2=mysql_real_query(&mysql,slc1,strlen(slc1));//以用户名和账号共同创建个数据表,并储存好友列表,屏蔽好友列表和群列表//res=mysql_store_result(res);//mysql_free_result(res);}/*if(r2!=0){perror("mysql_real_query");}*///printf("%d\n",r2);if(r2!=0){perror("mysql_real_query");}if((r1==0)&&(r2==0)){printf("注册成功\n");}else{printf("注册失败\n");}return 0;} int Login() {char ac[10]={0};char ps[16]={0};char slc[1024]="select * from usr where(account='";printf("你已进入登陆系统\n");printf("请输入你的账号\n");scanf("%s",ac);strncat(slc,ac,strlen(ac));printf("请输入你的密码\n");scanf("%s",ps);strcat(slc,"' AND password='");strncat(slc,ps,strlen(ps));strcat(slc,"')");int ret=mysql_real_query(&mysql,slc,strlen(slc));res=mysql_store_result(&mysql);int row=mysql_num_rows(res);if(row==1){unsigned long *lenths=0;unsigned int i=0;MYSQL_ROW fetchrow;//num_fields = mysql_num_fields(result);fetchrow=mysql_fetch_row(res);strncpy(USR.name,fetchrow[0],strlen(fetchrow[0])); //修改当前程序用户信息,以便后续使用strncpy(USR.account,ac,strlen(ac));strncpy(USR.password,ps,strlen(ps));Loggingstatus=1;printf("登陆成功\n");return 0; }else{printf("登陆失败\n");}}
最后需要注意的是,这个只是客户端代码,我并没有在上面添加服务端的,但是运行这些代码时无上大雅,因为主要这篇就是来搞注册和登录的。编译时需要链接mysqlclient 及pthead,虽然线程功能没让他具体实现,但是要用得着之后,所以编译时后面跟随代码-lmysqlclient和-lphread
,然后已管理员模式运行可执行文件,执行时可直接执行,需要添加参数,一个是端口号,一个是IP地址。你可以运行时添加下面代码
sudo ./clie 8888 127.0.0.1
中间有些算法不算佳,因为中间调试的工程中,感觉有的算法可能更加,但是一直报错,甚至我调试半天,最后还是用了写较长的代码来实现。我的代码只是思路的参考和一些函数的具体应用,希望你们有哪些疑惑可以留言或者上网搜索,这样可以更加容易的进步。
超详细:实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。--注册和登录(但之后会连续更新内容,直至全部实现)相关推荐
- 2021.10.02超详细实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。(添加好友,删除好友、屏蔽好友、查看好友列表(针对数据库的操作))
这次主要对加好友.删除好友.屏蔽好友.查看好友列表功能(单纯的基于数据库操作),并且也对之前的代码做了部分的改动,为中间涉及全局变量问题. 对之前的改动(主要): 其中对结构体进行了改动和结构体全局变 ...
- Linux环境下——C语言聊天室项目
由于使用了多线程操作,客户端进入程序后请先随便注册一次用户后再进行使用. 本程序默认第一个用户即ID为1的用户为超级管理员. 由于线程阻塞,最后的踢人操作有阻塞,需要在被踢出在线链表后手动下线. 看了 ...
- Linux环境下,通过shell脚本实现一键部署MySQL,并支持多种类型
Linux环境下一键部署MySQL脚本,支持多种类型 前言 一.使用前须知 二.使用方法 三.shell脚本内容 总结 前言 MySQL是目前最流行的关系型数据库管理系统之一,属于 Oracle ...
- Linux环境下Python的安装过程
Linux环境下Python的安装过程 前言 一般情况下,Linux都会预装 Python了,但是这个预装的Python版本一般都非常低,很多 Python的新特性都没有,必须重新安装新一点的版本,从 ...
- hp ux安装mysql5.1.56_详细讲解Linux环境下MySQL5.1安装步骤
1.下载MySQL免安装版/二进制版软件(不用编译) 文件格式:MYSQL-VERSION-OS.tar.gz 2.创建MySQL组,建立MySQL用户并加入到mysql组中 (不同版本的Unix中, ...
- k8s简介以及linux环境下的详细安装步骤
k8s简介以及linux环境下的详细安装步骤 k8s是Kubernetes的简称,Kubernetes中间有8个单词,所以叫k8s,就是这么简单粗暴. 我们可以看到docker的图标是鲨鱼,k8s的图 ...
- linux下的python安装,linux环境下的python安装过程
一.下载python源码包 打开ubuntu下的shell终端,通过wget命令下载python源码包,如下图所示: wget https://www.python.org/ftp/python/3. ...
- Linux版本配置环境变量,如何linux环境下配置环境变量过程图解
jdk下载地址: 在linux环境下的root同级目录下配置software目录 将下载好的jdk上传到software文件夹里面 (我使用的操作软件是) 到software这个目录下. 输入命令:c ...
- Linux 环境下php5.6,如何正确安装微软Mssql驱动--详细教程
Linux 环境下,如何正确安装微软Mssql驱动 近日,由于公司业务的需要,需要用到PHP连接一个客户的Mssql服务器.于是,开始一番折腾之路.Baidu与Google之后,发现,网上说的各种方法 ...
最新文章
- Linux_SELinux使用
- 机器学习实战读书笔记(一)机器学习基础
- 洛谷P2698 [USACO12MAR]花盆Flowerpot
- SQLserver被js注入的全库替换SQL
- Spark-大规模数据处理计算引擎
- matplotlib导包
- 辽宁工业大学计算机复试经验,辽宁工业大学车辆工程考研经验
- 导入别的项目到我的eclipse上出现红色感叹号问题
- 吴恩达教授机器学习课程笔记【九】- k均值聚类算法
- pyhive ModuleNotFoundError: No module named ‘thrift‘
- C语言利用堆筛选前1000大元素
- 仿真软件测试基尔霍夫定律,标签:基尔霍夫定律
- TSP问题——GA(遗传算法)解法(附源代码)
- tp5微信公众号发送模板消息
- 转行学Java怎么样?Java培训机构有什么避雷的要点?
- 如何解锁CourseHero文档
- boot中jar包部署的方式读取classes下的文件
- 序列划分c语言,看懂了这些,你对缠论中的线段划分就基本掌握了!
- C语言上机报告例文,c语言上机实验报告_大一c语言上机实验报告_c语言实验报告怎么写...
- JS:来一盘紧张而又刺激的五子棋
热门文章
- cat6 万兆_CAT6以及CAT6A系统万兆测试方法
- windows server服务器性能监控
- 时间序列(time serie)分析系列之简介1
- python代码雨在桌面实现_今天七夕节,外面下着大雨,用Python的tkinter做一个下爱心雨的特效,发给妹子...
- java 类加载 apk_在Android的App中动态的加载Java类
- 这是最全的一篇!!!浏览器输入网址后发什么了什么?
- java jlabel对齐方式_怎么设置JLabel内容对齐方式
- [AHK]一键摘抄金句到Obsidian
- c语言中左移右移有什么作用,C语言中左移和右移运算符详细介绍
- Android DataBinding双向绑定原理