Tcp C/S架构实现聊天室(数组管理在线用户)(服务器)
2017-01-24 22:49
399 查看
这几天一直在做聊天室,昨天把它做完了,但是服务器是用结构体数组实现的,虽说有些复杂化了,但也是我最开始想到的一种方法,并将之实现了,今天开始做链表来实现管理在线用户,感觉比结构体数组简洁多了。
/*********************************************************************
File Name: tcp_net_socket.h
Author: date:
Description:
Fuction List:
********************************************************************/
#ifndef TCP_NET_SOCKET_H_
#define TCP_NET_SOCKET_H_
#include <stdio.h>
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <termios.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SERV_PORT 9000
#define reg 1 //注册
#define log 2 //登录
#define group_chat 5 //群聊
#define group_result -6 //群聊接受
#define private_chat 10 //私聊
#define file_transfer 15 //文件传输对象
#define file_confire 80 //文件询问确认接受
#define file_yes 81 //接受
#define file_no 82 //不接受
#define file_select 18 //文件选择
#define file_recv 83 //文件接收
#define online_member 20 //查看在线人数
#define online_result 21 //在线人数结果
#define expression 25 //表情
#define e_select 26 //发送表情选择
#define phrases 29 //常用语
#define p_select 31 //发送常用语选择
#define motto 35 //个性签名
#define like 40 //点赞
#define Vip 38 //开会员
#define Shutup 45 //禁言
#define Shutuped 46 //被禁言
#define lifted 50 //解禁
#define kick 55 //踢人
#define kicked 56 //被踢下线
#define exit 100 //退出
#define existing_acc -1 //账号已存在
#define logged_acc -2 //账号已登录
#define error -3 //账号或密码错误
#define error1 -4 //重复登录
#define log_success 3 //登录成功
#define reg_success 4 //注册成功
#define Send_success 6 //发送信息成功
#define Send_error 7 //发送信息失败
#define send_msg 8 //发送信息
#define recv_msg 9 //接受信息
#define send_result 11 //发送结果
#define Exit 12 //退出
#define kick_fail 16 //踢人失败
#define kick_success 17 //踢人成功
#define like_success 13 //点赞成功
#define file_success 14 //文件传输成功
#define motto_change 19 //更改个性签名
#define forget 60 //忘记密码
extern int tcp_init();
extern int tcp_accept(int sfd);
extern int tcp_connet();
extern void signalhandler(void);
extern int mygetch();
extern int gettime();
extern char* nowtime();
#endif
10202
>
/*********************************************************************
File Name: tcp_net_socket.h
Author: date:
Description:
Fuction List:
********************************************************************/
#ifndef TCP_NET_SOCKET_H_
#define TCP_NET_SOCKET_H_
#include <stdio.h>
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <termios.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SERV_PORT 9000
#define reg 1 //注册
#define log 2 //登录
#define group_chat 5 //群聊
#define group_result -6 //群聊接受
#define private_chat 10 //私聊
#define file_transfer 15 //文件传输对象
#define file_confire 80 //文件询问确认接受
#define file_yes 81 //接受
#define file_no 82 //不接受
#define file_select 18 //文件选择
#define file_recv 83 //文件接收
#define online_member 20 //查看在线人数
#define online_result 21 //在线人数结果
#define expression 25 //表情
#define e_select 26 //发送表情选择
#define phrases 29 //常用语
#define p_select 31 //发送常用语选择
#define motto 35 //个性签名
#define like 40 //点赞
#define Vip 38 //开会员
#define Shutup 45 //禁言
#define Shutuped 46 //被禁言
#define lifted 50 //解禁
#define kick 55 //踢人
#define kicked 56 //被踢下线
#define exit 100 //退出
#define existing_acc -1 //账号已存在
#define logged_acc -2 //账号已登录
#define error -3 //账号或密码错误
#define error1 -4 //重复登录
#define log_success 3 //登录成功
#define reg_success 4 //注册成功
#define Send_success 6 //发送信息成功
#define Send_error 7 //发送信息失败
#define send_msg 8 //发送信息
#define recv_msg 9 //接受信息
#define send_result 11 //发送结果
#define Exit 12 //退出
#define kick_fail 16 //踢人失败
#define kick_success 17 //踢人成功
#define like_success 13 //点赞成功
#define file_success 14 //文件传输成功
#define motto_change 19 //更改个性签名
#define forget 60 //忘记密码
extern int tcp_init();
extern int tcp_accept(int sfd);
extern int tcp_connet();
extern void signalhandler(void);
extern int mygetch();
extern int gettime();
extern char* nowtime();
#endif
10202
>
/********************************************************************* File Name: tcp_net_socket.c Author: date: Description: Fuction List: int tcp_init() //用于初始化操作 int tcp_accept(int sfd) //用于服务器的接收 int tcp_connect(const char* ip) //用于客户端的连接 void signalhandler(void) //用于信号处理,让服务器在按下Ctrl+c或Ctrl+\时不会退出 ********************************************************************/ #include "tcp_net_socket.h" //用于初始化操作 int tcp_init() { int sfd = socket(AF_INET, SOCK_STREAM, 0); //创建套接字 if(sfd == -1) { perror("socket"); return -1; } int ret; struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(struct sockaddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(SERV_PORT); serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(sfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr)); if(ret == -1) { perror("bind"); return -1; } ret = listen(sfd,10); //监听它,并设置允许最大的连接数为10个 if(ret == -1) { perror("listen"); close(sfd); return -1; } return sfd; } //用于服务器的接收 int tcp_accept(int sfd) { struct sockaddr_in clientaddr; memset(&clientaddr, 0, sizeof(struct sockaddr)); int addrlen = sizeof(struct sockaddr); //sfd接受客户端的连接,并创建新的socket为new_fd,将请求连接的客户端的ip、port保存在结构体clientaddr中 int new_fd = accept(sfd, (struct sockaddr*)&clientaddr, &addrlen); if(new_fd == -1) { perror("accept"); close(sfd); return -1; } printf("%s %d success connet...\n", inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port)); return new_fd; } //用于客户端的连接 int tcp_connect(const char* ip) { int ret; int sfd = socket(AF_INET, SOCK_STREAM, 0); //申请新的socket if(sfd == -1) { perror("socket"); return -1; } struct sockaddr_in serveraddr; memset(&serveraddr, 0,sizeof(struct sockaddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(SERV_PORT); serveraddr.sin_addr.s_addr = inet_addr(ip); ret = connect(sfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr)); //将sfd连接至指定的服务器网络地址 serveraddr if(ret == -1) { perror("connect"); close(sfd); return -1; } return sfd; } //用于信号处理,让服务器在按下Ctrl+c或Ctrl+\时不会退出 void signalhandler(void) { sigset_t sigSet; sigemptyset(&sigSet); sigaddset(&sigSet,SIGINT); sigaddset(&sigSet,SIGQUIT); sigprocmask(SIG_BLOCK,&sigSet,NULL); } //用于将密码数字转换为* int mygetch( ) { struct termios oldt, newt; int ch; tcgetattr( STDIN_FILENO, &oldt ); newt = oldt; newt.c_lflag &= ~( ICANON | ECHO ); tcsetattr( STDIN_FILENO, TCSANOW, &newt ); ch = getchar(); tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); return ch; } //获取当前时间 int gettime() { time_t rawtime; struct tm * timeinfo; time ( &rawtime ); timeinfo = localtime ( &rawtime ); printf ( "%s",asctime (timeinfo) ); } //获取当前时间 指针 用于消息记录 char* nowtime() { time_t rawtime; struct tm * timeinfo; time ( &rawtime ); timeinfo = localtime ( &rawtime ); return asctime (timeinfo); }
#include "tcp_net_socket.h" struct send { int socket; char account[20]; //账号 char passward[20]; //密码 char name[20]; //名字 char moto[256]; //个性签名 int likes; //点赞数 char question[100]; //密保问题 char answer[100]; //密保答案 char toname[20]; //接收人 int cmd; //提取操作符 char buff[2048]; //发送、接收文件 消息 int state; //是否处于禁言状态 int vip; //是否是会员 char e_s; //确认发送的表情 char p_s; //确认发送的常用语 char file[2048]; //发送文件存的数据 char file_name[30]; //文件名 }; struct state { int flag[50]; //判断用户有没有登录 1为登录 0未登录 struct send user[50]; }; struct recv { char from_name[20]; //发信人 char name[20]; //名字 char moto[256]; //个性签名 int result; //返回操作结果 int online_num; //在线人数 char num[20][20]; //在线人名 char cuff[2048]; //发送、接收文件 消息 int cmd_back; //提取操作符 int likes; //点赞数 int state; //是否处于禁言状态 int vip; //是否是会员 int msg_back; //确认是接收方还是发送方 char e_s; //确认发送的表情 char p_s; //确认发送的常用语 char file[2048]; //发送文件存的数据 char file_name[30]; //文件名 char question[100]; //密保问题 char answer[100]; //密保答案 char passward[20]; //密码 }; struct send userInfo; struct recv userBack; struct state userMge; pthread_mutex_t mutex; sqlite3 * db = NULL; //基础信息存放数据库 int temp = 0; int i; //保存用户 void save_user() { pthread_mutex_lock(&mutex); int ret; char *errmsg = NULL; char auff[200] = {0}; char cuff[200] = {0}; sprintf(auff, "insert into save_user values('%s','%s','%s','%s',%d,%d)",userInfo.account, userInfo.passward, userInfo.name, userInfo.moto, userInfo.likes, userInfo.vip); ret = sqlite3_exec(db, auff, NULL, NULL, &errmsg); if(ret != SQLITE_OK) { printf("insert fail:%d(%s)\n", ret, errmsg); userBack.result = existing_acc; //账号已存在 pthread_mutex_unlock(&mutex); return; } printf("sqlite save_user insert success...\n"); sprintf(cuff,"insert into question values('%s','%s','%s','%s')", userInfo.account, userInfo.passward, userInfo.question, userInfo.answer); ret = sqlite3_exec(db, cuff, NULL, NULL, &errmsg); if(ret != SQLITE_OK) { printf("insert fail:%d(%s)\n", ret, errmsg); pthread_mutex_unlock(&mutex); return; } userBack.result = reg_success; //注册成功 userBack.state = userInfo.state; pthread_mutex_unlock(&mutex); } //登录检查表和客户端发来的数据对比 void deal_log(int cfd) { pthread_mutex_lock(&mutex); char **resultp = NULL; int nrow; int ncolumn; int ret; int i; char *errmsg = NULL; char cuff[200]; userInfo.socket = cfd; sprintf(cuff, "select account,passward,name,moto,likes,vip from save_user where account = '%s' and passward = '%s'", userInfo.account, userInfo.passward); ret = sqlite3_get_table(db, cuff, &resultp, &nrow, &ncolumn, &errmsg); if (ret != SQLITE_OK) { printf ("log error : %d(%s)!\n", ret, errmsg); return; } if(nrow == 1) { userBack.result = log_success; //登录成功 strcpy(userBack.name, resultp[8]); strcpy(userBack.moto, resultp[9]); userBack.likes = *(resultp[10]) - 48; userBack.vip = *(resultp[11]) - 48; printf("%s logged success...\n", resultp[8]); for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, resultp[8]) == 0) { userBack.result = error1; //重复登录 return; } } for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1) { continue; } userMge.flag[i] = 1; userMge.user[i].socket = cfd; strcpy(userMge.user[i].name, resultp[8]); break; } } else { userBack.result = error; //账号密码错误 } pthread_mutex_unlock(&mutex); } //忘记密码 void deal_forget(int cfd) { pthread_mutex_lock(&mutex); char auff[100]; char **resultp = NULL; int nrow; int ncolumn; int ret; char *errmsg = NULL; sprintf(auff, "select passward, question, answer from question where account = '%s'", userInfo.account); ret = sqlite3_get_table(db, auff, &resultp, &nrow, &ncolumn, &errmsg); if(ret != SQLITE_OK) { printf("select error fail:%d(%s)\n", ret, errmsg); } strcpy(userBack.passward, resultp[3]); strcpy(userBack.question, resultp[4]); strcpy(userBack.answer, resultp[5]); ret = write(cfd, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return; } pthread_mutex_unlock(&mutex); } //处理私聊请求 int deal_pchar(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { temp = 1; strcpy(userBack.cuff, userInfo.buff); strcpy(userBack.from_name, userInfo.name); userBack.msg_back = recv_msg; ret = write(userMge.user[i].socket,&userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return; } printf("%s send a message to %s...\n", userInfo.name, userInfo.toname); break; } } if(temp == 0) { userBack.msg_back = send_result; userBack.result = Send_error; ret = write(cfd,&userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return; } printf("%s send error...\n", userInfo.name); } pthread_mutex_unlock(&mutex); } //处理踢人请求 int deal_kick(int cfd) { pthread_mutex_lock(&mutex); int temp = 0; int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { temp = 1; strcpy(userBack.from_name,userInfo.name); userBack.msg_back = kick; ret = write(userMge.user[i].socket,&userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } } } if(temp) { userBack.msg_back = kick_success; ret = write(cfd,&userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } } else { userBack.msg_back = kick_fail; ret = write(cfd,&userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } } pthread_mutex_unlock(&mutex); } //处理点赞请求 int deal_like(int cfd) { pthread_mutex_lock(&mutex); int ret; int i; char *errmsg = NULL; char **resultp = NULL; int nrow; int ncolumn; char cuff[200]; sprintf(cuff, "select likes from save_user where name = '%s'", userInfo.name); ret = sqlite3_get_table(db, cuff, &resultp, &nrow, &ncolumn, &errmsg); if(ret != SQLITE_OK) { printf("select fail:%d(%s)\n", ret, errmsg); return -1; } sprintf(cuff, "update save_user set likes = %d where name = '%s'",*(resultp[1]) - 47, userInfo.name); ret = sqlite3_exec(db, cuff, NULL, NULL, &errmsg); if(ret != SQLITE_OK) { printf("update fail:%d(%s)\n", ret, errmsg); return -1; } for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { userMge.user[i].likes = *(resultp[1]) - 47; userBack.likes = *(resultp[1]) - 47; userBack.msg_back = like_success; strcpy(userBack.from_name,userInfo.name); ret = write(userMge.user[i].socket, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } break; } } printf("like success...\n"); pthread_mutex_unlock(&mutex); } //处理禁言请求 int deal_Shutup(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { userBack.state = Shutup; strcpy(userBack.from_name, userInfo.name); userBack.msg_back = Shutup; ret = write(userMge.user[i].socket, &userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return; } break; } } printf("shutup success...\n"); pthread_mutex_unlock(&mutex); } /* //处理解禁要求 int deal_lifted(int cfd) { pthread_mutex_lock(&mutex); int ret; printf("解禁\n"); for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userInfo.name, userMge.user[i].name) == 0) {printf("解禁成功\n"); userMge.user[i].state = 0; userBack.state = 0; userBack.msg_back = lifted; ret = write(cfd, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } break; } } pthread_mutex_unlock(&mutex); } */ //处理查看在线人数 int deal_member(int cfd) { pthread_mutex_lock(&mutex); int ret; userBack.online_num = 0; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1) { userBack.online_num++; strcpy(userBack.num[i],userMge.user[i].name); } } userBack.msg_back = online_member; ret = write(cfd,&userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } printf("show online_member success...\n"); pthread_mutex_unlock(&mutex); } //处理群聊请求 int deal_groupchat(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && userMge.user[i].socket != cfd) { userBack.msg_back = group_chat; strcpy(userBack.cuff,userInfo.buff); strcpy(userBack.from_name, userInfo.name); ret = write(userMge.user[i].socket,&userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } } } printf("%s group_chat success...\n"); pthread_mutex_unlock(&mutex); } //处理发送表情 int deal_expression(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { strcpy(userBack.from_name, userInfo.name); userBack.e_s = userInfo.e_s; userBack.msg_back = e_select; ret = write(userMge.user[i].socket, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } printf("send expression success...\n"); break; } } pthread_mutex_unlock(&mutex); } //处理发送常用语 int deal_phrases(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { strcpy(userBack.from_name,userInfo.name); userBack.p_s = userInfo.p_s; userBack.msg_back = p_select; ret = write(userMge.user[i].socket, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } printf("send phrases success...\n"); break; } } pthread_mutex_unlock(&mutex); } //处理文件传输 int deal_file_transfer(int cfd) { pthread_mutex_lock(&mutex); int ret; for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.toname) == 0 && cfd != userMge.user[i].socket) { userBack.msg_back = file_transfer; strcpy(userBack.from_name, userInfo.name); strcpy(userBack.file, userInfo.file); strcpy(userBack.file_name, userInfo.file_name); ret = write(userMge.user[i].socket, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return -1; } } } pthread_mutex_unlock(&mutex); } //服务器响应请求 void *server_requst(int cfd) { int ret; char *errmsg = NULL; while(read(cfd, &userInfo, sizeof(userInfo))) { switch (userInfo.cmd) { case (reg): //注册 { save_user(db); printf("%s insert success...!\n", userInfo.name); ret = write(cfd, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return; } break; } case (log): //登陆 { deal_log(cfd); ret = write(cfd, &userBack, sizeof(userBack)); if(ret == -1) { perror("write"); return; } break; } case (forget): //忘记密码 { deal_forget(cfd); break; } case (group_chat): //群聊 { deal_groupchat(cfd); break; } case (private_chat): //私聊 { deal_pchar(cfd); break; } case (file_transfer): //文件传输 { deal_file_transfer(cfd); break; } case (online_member): //查看在线人数 { deal_member(cfd); break; } case (expression): //表情 { deal_expression(cfd); break; } case (phrases): //常用语 { deal_phrases(cfd); break; } case (like): //点赞 { deal_like(cfd); break; } case (Vip): //注册vip { char guff[200]; sprintf(guff, "update save_user set vip = %d where name = '%s'", userInfo.vip, userInfo.name); ret = sqlite3_exec(db,guff,NULL,NULL,&errmsg); if(ret == -1) { printf("update fail:%d(%s)\n", ret, errmsg); return; } userBack.vip = 1; userBack.msg_back = 0; ret = write(cfd, &userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return; } break; } case (Shutup): //禁言 { deal_Shutup(cfd); break; } /* case (lifted): //解禁 { deal_lifted(cfd); break; } */ case (kick): //踢人 { deal_kick(cfd); break; } case (Exit): //退出 { for(i = 0; i < 50; i++) { if(userMge.flag[i] == 1 && strcmp(userMge.user[i].name, userInfo.name) == 0) { userMge.flag[i] = 0; } } ret = write(cfd, &userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return; } printf("%s log out...\n"); break; } case (motto_change): { char xcf[60]; sprintf(xcf, "update save_user set moto = '%s' where name = '%s'",userInfo.moto,userInfo.name); ret = sqlite3_exec(db,xcf,NULL,NULL,&errmsg); if(ret == -1) { printf("update fail:%d(%s)\n", ret, errmsg); return; } strcpy(userBack.moto, userInfo.moto); ret = write(cfd, &userBack,sizeof(userBack)); if(ret == -1) { perror("write"); return; } break; } default : { break; } } } } int main() { int sfd; int ret; sfd = tcp_init(); pthread_mutex_init(&mutex, NULL); // signalhandler(); char *errmsg = NULL; ret = sqlite3_open("user.db",&db); if(ret != SQLITE_OK) { perror("open user.db error"); return -1; } printf("open user.db success...\n"); ret = sqlite3_exec(db, "create table if not exists save_user(account TEXT, passward TEXT, name TEXT, moto TEXT, likes INTEGER, vip INTEGER, primary key(account))",NULL,NULL,&errmsg); if(ret != SQLITE_OK) { printf("create fail:%d(%s)\n", ret, errmsg); return -1; } printf("create save_user success...\n"); ret = sqlite3_exec(db, "create table if not exists question(account TEXT, passward TEXT, question TEXT, answer TEXT, primary key(account))", NULL, NULL, &errmsg); if(ret !=SQLITE_OK) { printf("creat fail:%d(%s)\n",ret,errmsg); return -1; } for(i = 0; i < 50; i++) { userMge.flag[i] = 0; } pthread_t clientid; while(1) { int cfd; cfd = tcp_accept(sfd); if(cfd == -1) { perror("accept"); return -1; } ret = pthread_create(&clientid, NULL, (void*)server_requst, (void*)cfd); if(ret == -1) { perror("pthread create"); return -1; } pthread_detach(clientid); // 线程分离 pthread_join(clientid, NULL); } sqlite3_close(db); pthread_mutex_destroy(&mutex); return 0; }
相关文章推荐
- Tcp C/S架构实现聊天室(数组管理在线用户)(客户端)
- Tcp C/S架构实现聊天室(链表管理在线用户)(客户端)
- Aspx实现在线用户管理
- 用PHP实现Ftp用户的在线管理
- 第五天 CSDN (复习、聊天室、实现在线用户列表)
- 2016.11.29 activiti实战--第19章--统一身份管理(含自定义用户与数组的实现)
- 用PHP实现Ftp用户的在线管理的代码
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(九) 之 用 Redis 实现用户在线离线状态消息处理(一)
- 用PHP实现Ftp用户的在线管理
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(八) 之 用 Redis 实现用户在线离线状态消息处理
- 用PHP实现Ftp用户的在线管理
- 用PHP实现Ftp用户的在线管理
- 2016.11.29 activiti实战--第19章--统一身份管理(含自定义用户与数组的实现)
- web系统在线用户管理实现
- 在线聊天室的实现(2)--基于Netty 4.x的Echo服务器实现
- Linux学习日志day2 Part1——搭建freeIPA服务器实现用户管理以及SSH服务远程登录
- 用PHP实现Ftp用户的在线管理
- 用PHP实现Ftp用户的在线管理的代码
- asp.net2.0中的用户管理如何在SQL Server2000 实现