目录

1.基本功能以及使用注意事项

1.1基本介绍

2.服务器端

chatroom.c

2.1服务器初始化函数

2.2接受处理客户端请求连接

2.3线程函数

2.4线程函数内部处理客户端想执行的功能

2.5注册

2.6 数据库中插入函数

2.7创建在线用户链表

2.8 登录

2.9登录将用户插入链表

2.10 登录在数据库验证账户与密码

2.11在数据库中查找用户是否存在

2.12 私聊功能

2.13判断用户是否在线

2.14 修改密码

2.15 进行群聊

2.16退出群聊 将链表节点删除

2.17 查看在线用户

2.19 注销用户

2.20保存聊天记录

2.21 重复登录判断

2.22查看聊天记录

2.23 文件传输

2.24管理员用户申请

2.25 在数据库中管理员表中查看是否存在该管理员

2.26//向管理员表中插入数据

2.28 管理员登录

2.29//在root表中查看该用户是否为管理员

2.30/判断管理员用户与密码是否正确

2.32管理员踢人

2.33管理员禁言

2.34解除禁言

2.35 //获取管理员头像

2.36创建表

2.37执行sql语句函数

2.38//根据名字修改密码

2.39指定删除普通用户的中数据

2.40指定删除管理员表中的数据

2.41撤销管理员身份

2.42判断链表是否为空

chatroom.h

2.客户端

client.c

client.h


1.基本功能以及使用注意事项

基本功能
1.注册 2.登录 3.群聊 4私聊 5.快捷短语 6.注销用户 7.修改密码

8.保存聊天记录  9.查看聊天记录  10.查看在线用户  11.文件传输

12.管理员申请  13.管理员踢人  14.管理员专属头像 15.禁言用户

16.解除禁言 17.撤销管理员身份

1.1基本介绍

服务器采用tcp  多线程并发服务器

客户端采用两个线程 接受和发送线程    接受线程主要负责接受服务器发送过来的信号

写线程主要负责给服务器发送各种信号用于实现对应的功能,以及实现客户端与客户端之间的群聊或者私聊

注意事项:管理员不能踢出(禁言)管理员   只能对普通用户进行管理员操作

申请成为管理员必须先注册为普通用户

登录之前必须注册   不能重复登录(注册)

2.服务器端

chatroom.c

2.1服务器初始化函数

#include "chatroom.h"
int j; //用于线程号数组
int flag2 = 1;  //用于管理员禁言//服务器初始化
void Init()
{unsigned char ip_str[] = "192.168.91.151";unsigned char port_str[] = "8787";struct sockaddr_in serveraddr;socklen_t addrlen = sizeof(serveraddr);if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ERRLOG("socket");}//第二部:填充网络信息结构体serveraddr.sin_addr.s_addr =  inet_addr(ip_str);serveraddr.sin_port =  htons(atoi(port_str));serveraddr.sin_family = AF_INET;int opt = 1;//设置端口复用setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//第三步if((bind(sockfd,(struct sockaddr *)&serveraddr,addrlen)) == -1){ERRLOG("bind");}//第四步if((listen(sockfd,100)) == -1){ERRLOG("listen");}
}

2.2接受处理客户端请求连接

//接受处理客户端
void server(online_userlist *head)
{   thread_node node;//结构体新增变量node.head = head;clientlist c_client;struct sockaddr_in clientaddr;while(1){   socklen_t addrlen = sizeof(clientaddr);if((node.cfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen)) == -1){ERRLOG("accept");}printf("客户端连接成功\n");if(pthread_create(&pti1[j],NULL,myfun,(void*)&node) != 0){ERRLOG("pthread_create");}printf("服务线程测试\n");pthread_detach(pti1[j]);//为客户端进行服务j++;}close(sockfd);
}

2.3线程函数

void *myfun(void *arg)
{thread_node node;node = *((struct thread_node *)arg);while(1){messagehand(&node);}close(node.cfd);return NULL;
}

2.4线程函数内部处理客户端想执行的功能

//线程函数内部处理客户端想执行的功能
void messagehand(thread_node *node)
{clientlist c_client;//创建一个和客户端一样的结构体int bytes = recv(node->cfd,&c_client,sizeof(c_client),0); //0阻塞接受if(c_client.flag == 3 || c_client.flag == 5){insert_uprecord(node,&c_client);}if(bytes == -1){ERRLOG("recv\n");}else if(bytes == 0){exituser(node,&c_client);//printf("正在运行群聊函数\n");online_userlist *p = node->head;p=p->next;while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   char buf[50] = "\0";sprintf(buf,"[%s]:退出了",c_client.name);if(send(p->c_fd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}memset(buf,0,sizeof(buf));}p=p->next;}printf("%s:退出了\n",c_client.name);pthread_exit(NULL);}printf("接受的结构体名字:%s  功能标志:%d  密码:%s  私聊对象:%s  管理员标志: %d\n",c_client.name,c_client.flag,c_client.password,c_client.to_name,c_client.root);switch(c_client.flag){case 1:Register(node,&c_client);//注册用户printf("注册用户\n");        break;case 2:login(node,&c_client);//登录printf("用户登录功能\n");break;case 3:privatechat(node,&c_client); //私聊printf("私聊功能\n");break;case 4:changepassword(node,&c_client); //修改密码printf("修改密码\n");break;case 5:groupchat(node,&c_client);//进行群聊break;case 6:   //查看所有在线用户viewonline_user(node,&c_client);break;case 7://注销用户loguser(node,&c_client);break;case 8:Checkrecord(node);//查看聊天记录break;case 9:filetransfer(node,&c_client); //文件传输break;case 10:applyroot(node,&c_client); //申请管理员用户break;case 11:rootlogin(node,&c_client); //管理员登录break;case 12:rootkick(node,&c_client);  //管理员踢人break;case 13:silent(node,&c_client); //管理员禁言break;case 14:remsilent(node,&c_client); //管理员解除用户禁言break;case 15:cancelroot(node,&c_client); //取消管理员身份break;}}

2.5注册

//注册
void Register(thread_node *node,clientlist *c)
{printf("判断:%s 是否在数据库\n",c->name);int ret = searchtable(c->name); //在数据库中查找是否已经注册过printf("%d",ret);if(1 == ret){char arr[128] = {"账号已经存在,请重新注册"};if(send(node->cfd,arr,sizeof(arr),0) == -1){ERRLOG("send error");}return;}else{insert_updata(node,c);  //向数据库中插入数据}
}

2.6 数据库中插入函数

//向数据库中插入
void insert_updata(thread_node *node,clientlist *c)
{   printf("注册的名字%s\n",c->name);char *errmsg;sqlite3 *db =NULL;char sql[200] = "\0";memset(sql,0,sizeof(sql));//清空sqlsprintf(sql,"insert into user(name,password) values('%s','%s')",c->name,c->password);printf("数据库sql语句测试\n");execstate(sql);printf("\033[0;34m插入成功\033\n");char buf[100] = {"[系统日志]:注册成功...\n"};if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}
}

2.7创建在线用户链表

//在线用户链表
online_userlist* online_userlistcreate()
{online_userlist *head = (online_userlist *)malloc(sizeof(online_userlist));head->next = NULL;return head;
}

2.8 登录

void login(thread_node *node,clientlist *c)
{//判断是否注册if(0 == (searchtable(c->name))){char buf[32] = "该用户不存在,请注册...";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else if( 0 == (veridandpassword(c)))//密码错误{char buf[20] = "密码输入错误..";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else if(1 == (veridandpassword(c)))//返回值为1账号密码正确{   if(1 ==Repeatlogin(node,c))//判断重复登录{online_insertion(node,c);char buf[32] = "登录成功..";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}online_userlist *p = node->head;p=p->next;while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   char buf[50] = "\0";sprintf(buf,"[%s]:上线了",c->name);if(send(p->c_fd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}memset(buf,0,sizeof(buf));}p=p->next;}}else{return;}}
}

2.9登录将用户插入链表

void online_insertion(thread_node *node,clientlist *c)
{online_userlist *tmp=(online_userlist *)malloc(sizeof(online_userlist));tmp->next=NULL;online_userlist *p =node->head;//定义一个指针为表头strcpy(tmp->name,c->name);tmp->c_fd = node->cfd;printf("判断:%s,%d是否进行插入链表\n",tmp->name,tmp->c_fd);if(1==onlineempty(p)){tmp->next=p->next;//令新插入的结点指针指向下一个结点p->next=tmp;}else{while(p->next!=NULL&&strncmp(p->next->name , tmp->name,1)<0)//当目的name小于源name{p=p->next;}//当p->next指向的数据大于或者等于需要插入的数据是跳出循环//此事p->next  向的是第一个大于value的值tmp->next=p->next;p->next=tmp;  //然后将其插入大于value的值之前putchar(10);}printf("链表插入完成\n");
}

2.10 登录在数据库验证账户与密码

//验证账户与密码 正确返回1 错误返回0;
int veridandpassword(clientlist *p)
{   printf("已进入密码效验工作\n");int flag;sqlite3 *db = NULL;      //数据库的名字记得更换int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}printf("已打开数据库\n");char sql[200] = "\0";sprintf(sql,"select *from user;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;int ret1,ret2;printf("已进行遍历数据库\n");for(i = 0 ; i < nrow+1;i++){ret1 = (strcmp(result[i*ncolumn + 0],p->name));ret2 = (strcmp(result[i*ncolumn + 1],p->password));printf("正在进行账户密码效验\n");printf("账户:%s\n",result[i*ncolumn + 0]);printf("密码:%s\n",result[i*ncolumn + 1]);printf("密码:%s\n",p->password);if(0 == ret1 && 0 == ret2){flag = 1;break;}//0行就是第一行  第i行第j列}if(flag == 1){   printf("核对返回值为%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}else{flag = 0;sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}
}

2.11在数据库中查找用户是否存在

//在数据库查询用户 c存在返回1  不存在返回0
int searchtable(char *tablename)
{   int flag;sqlite3 *db = NULL;      int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}char sql[200] = "\0";sprintf(sql,"select *from user;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;for(i = 1 ; i < nrow+1;i++){int ret = strcmp(result[i*ncolumn + 0],tablename);if(ret == 0){   flag = 1;}//0行就是第一行  第i行第j列}if(flag == 1){   printf("数据库是否存在该用户%d存在\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}else{   flag = 0;printf("%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}
}

2.12 私聊功能

//私聊功能
void privatechat(thread_node *node,clientlist *c)
{   int flag = 0;printf("正在运行私聊函数\n");int to_cfd = Find_person(node,c);  //在线返回对方的文件描述符printf("私聊对象的文件描述符为%d\n",to_cfd);if(0 == to_cfd){char buf[32] = "对方不在线...";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else{   if(0 == flag){char buf1[128] = "\0";sprintf(buf1,"[系统日志]:正在与%s进行私聊",c->to_name);if(send(node->cfd,buf1,sizeof(buf1),0) == -1){ERRLOG("send error");}flag++;}printf("正在进行消息:%s转发\n",c->msg);char buf[128] = "\0";sprintf(buf,"[%s悄悄对你说了一句]:%s",c->name,c->msg);if(send(to_cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}}

2.13判断用户是否在线

//判断是否在线 在线返回对方文件描述符 不在线返回0
int Find_person(thread_node *node,clientlist *c)
{char s1[32]={0};strcpy(s1,c->to_name);//想跟s1进行私聊printf("正在判断%s是否在线\n",c->to_name);online_userlist *p = node->head;while(p!=NULL){   if(strcmp(s1,p->name) == 0 ) {   printf("%s在线\n",p->name);return p->c_fd;//私聊对象的文件描述符}p=p->next;}return 0;
}

2.14 修改密码

//修改密码
void changepassword(thread_node *node,clientlist *c)
{printf("用户%s将吧密码修改为%s",c->name,c->msg);Insert_updated_data(c->name,c->msg);char buf1[600] = "\0";sprintf(buf1,"[系统日志]:密码修改成功");if(send(node->cfd,buf1,sizeof(buf1),0) == -1){ERRLOG("send error");}printf("用户%s已成功修改密码",c->name);exituser(node,c);
}

2.15 进行群聊

//功能标志5  进行群聊
void groupchat(thread_node *node,clientlist *c)
{printf("正在运行管理员群聊函数\n");online_userlist *p = node->head;int ret;ret = onlineempty1(p);if(1 == ret){char buf1[128] = "\0";strcpy(buf1,"[系统日志]:当前没有别的用户在线");if(send(node->cfd,buf1,sizeof(buf1),0) == -1){ERRLOG("send error");}return;}printf("正在遍历链表给每个用户发送消息\n");printf("打印管路员标志%d\n",c->root);if(c->root == 1){   getheadflag(c);//获取管理员头像printf("打印管理员头像%s\n",c->emojio);p=p->next;while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   printf("打印文件描述符%d \n",p->c_fd);char buf[128] = "\0";sprintf(buf,"%s[%s]:%s",c->emojio,c->name,c->msg);if(send(p->c_fd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}memset(buf,0,sizeof(buf));}p=p->next;}}else{p=p->next;while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   printf("打印文件描述符%d \n",p->c_fd);char buf[100] = "\0";sprintf(buf,"[%s]:%s",c->name,c->msg);if(send(p->c_fd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}memset(buf,0,sizeof(buf));}p=p->next;}}
}

2.16退出群聊 将链表节点删除

//退出群聊 删除链表内的用户结点
void exituser(thread_node *node,clientlist *c)
{online_userlist *p = node->head;online_userlist *q;while(p->next!=NULL){if(strcmp(c->name,p->next->name)==0){q=p->next;p->next=p->next->next;free(q);q=NULL;printf("将%s踢出链表成功\n",c->name);return;}p=p->next;}
}

2.17 查看在线用户

//查看所有在线用户
void viewonline_user(thread_node *node,clientlist *c)
{online_userlist *p = node->head->next;int ret;ret = onlineempty(p);if(1 == ret){char buf1[128] = "\0";strcpy(buf1,"[系统日志]:当前没有别的用户在线");if(send(node->cfd,buf1,sizeof(buf1),0) == -1){ERRLOG("send error");}return;}while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   char buf[70] = {0};memset(buf,0,sizeof(buf));printf("%s当前在线3\n",p->name);sprintf(buf,"[%s]:当前在线",p->name);if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}sleep(1);}p=p->next;}
}

2.19 注销用户

//注销用户
void loguser(thread_node *node,clientlist *c)
{deleteuser(c);if(c->root == 1){deleteroot(c);}printf("注销用户删除数据库成功\n");char buf[32] = "注销成功..";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}exituser(node,c);printf("注销用户删除链表结点成功\n");
}

2.20保存聊天记录

//将聊天记录插入记录表(保存聊天记录)
void insert_uprecord(thread_node *node,clientlist *c)
{   printf("正在进行聊天记录存储\n");printf("聊天方式标志位:%d",c->flag);printf("聊天内容:%s",c->msg);time_t timep;struct tm *p;time (&timep);p=gmtime(&timep);char newtime[32] = {0};sprintf(newtime,"%d-%d-%d:%d:%d\n",1+p->tm_mon,p->tm_mday,8+p->tm_hour,p->tm_min,p->tm_sec);printf("获取时间%s\n",newtime);int cc=1;char *errmsg;sqlite3 *db =NULL;char sql[200] = "\0";memset(sql,0,sizeof(sql));//清空sqlpthread_mutex_lock(&mutex);sprintf(sql,"insert into record(time,name,record) values('%s','%s','%s')",newtime,c->name,c->msg);printf("数据库sql语句测试\n");execstate(sql);pthread_mutex_unlock(&mutex);printf("\033[0;34m聊天记录保存成功\033\n");}

2.21 重复登录判断

//重复登录判断
int Repeatlogin(thread_node *node,clientlist *c)
{   printf("正在判断是否重复登录\n");char s1[32]={0};strcpy(s1,c->name);//判断自己是否登录printf("正在判断%s是否在线\n",c->to_name);online_userlist *p = node->head;while(p!=NULL){   if(strcmp(s1,p->name) == 0 ) {   printf("%s在线,重复登录\n",p->name);char buf[32] = "您已在线,请勿重复登录";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}return 0;}p=p->next;}printf("未遍历到该用户,可以进行登录\n");return 1;
}

2.22查看聊天记录

//查看聊天记录
void Checkrecord(thread_node *node)
{printf("已进入聊天记录返回客户端工作\n");int flag;sqlite3 *db = NULL;      int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}printf("已打开数据库\n");char sql[200] = "\0";sprintf(sql,"select *from record;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;int ret1,ret2;printf("已进行遍历数据库\n");char buf[256] = {0};for(i = 1 ; i < nrow+1;i++){printf("正在将聊天记录发送给客户端\n");sprintf(buf,"%s\n[%s]:%s",result[i*ncolumn + 0],result[i*ncolumn + 1],result[i*ncolumn + 2]);printf("时间:%s\n",result[i*ncolumn + 0]);printf("用户:%s\n",result[i*ncolumn + 1]);printf("消息内容:%s\n",result[i*ncolumn + 2]);printf("%s",buf);if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}
}

2.23 文件传输

//文件传输
void filetransfer(thread_node *node,clientlist *c)
{MSG msg;printf("正在运行文件传输函数\n");int to_cfd = Find_person(node,c);  //在线返回对方的文件描述符printf("私聊对象的文件描述符为%d\n",to_cfd);char buf[256] = "\0";printf("***********11111\n");printf("%s",c->buf);printf("***********22222\n");strcpy(msg.text,c->buf);msg.num = c->num;if(0 == to_cfd){char buf1[32] = "对方不在线...";if(send(node->cfd,buf1,sizeof(buf),0) == -1){ERRLOG("send error");}}else{   while(flag2){   printf("正在向对方发送文件传输信号\n");if(send(to_cfd,"文件存在进行文件传输",N,0) == -1){ERRLOG("send error");}flag2 = 0;}if(strcmp(buf,"0") == 0){   printf("文件内容全部发送完成\n");flag2 = 1;}sleep(1);printf("本次向对方送的内容%s\n",msg.text);if(send(to_cfd,&msg,sizeof(msg),0) == -1){ERRLOG("send error");}printf("单次发送内容完成\n");memset(&msg,0,sizeof(msg));}

2.24管理员用户申请

//管理员用户的申请
void applyroot(thread_node *node,clientlist *c)
{printf("正在判断:%s 是否已经是管理员\n",c->name);int ret = searchtableroot(c->name);printf("%d",ret);if(1 == ret){char arr[128] = {"您已是管理员.."};if(send(node->cfd,arr,sizeof(arr),0) == -1){ERRLOG("send error");}return;}else{insert_updataroot(node,c);}
}

2.25 在数据库中管理员表中查看是否存在该管理员

//在数据库中管理员表中查看是否存在该管理员
int searchtableroot(char *tablename)
{   int flag;sqlite3 *db = NULL;      int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}char sql[200] = "\0";sprintf(sql,"select *from root;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;for(i = 1 ; i < nrow+1;i++){int ret = strcmp(result[i*ncolumn + 1],tablename);if(ret == 0){   flag = 1;}//0行就是第一行  第i行第j列}if(flag == 1){   printf("root表中该用户%d存在\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}else{   printf("root表中该用户%d不存在\n",flag);flag = 0;printf("%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}
}

2.26//向管理员表中插入数据

//向root表中插入数据
void insert_updataroot(thread_node *node,clientlist *c)
{printf("申请管理员的用户%s\n",c->name);char *errmsg;sqlite3 *db =NULL;char sql[200] = "\0";memset(sql,0,sizeof(sql));//清空sqlpthread_mutex_lock(&mutex);sprintf(sql,"insert into root(head,name,password) values('%s','%s','%s');",c->emojio,c->name,c->password);printf("将 头像:%s,名字;%s,密码:%s 插入表中\n",c->emojio,c->name,c->password);execstate(sql);pthread_mutex_unlock(&mutex);printf("\033[0;34m插入成功\033\n");char buf[100] = {"管理员申请成功"};if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}exituser(node,c);//注册完将其踢出链表让其进入初始界面从新登录return;
}

2.28 管理员登录

//管理员登录
void rootlogin(thread_node *node,clientlist *c)
{if(0 == (rootsearch(c->name))){char buf[32] = "该用户非管理员";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else if( 0 == (rootveridandpassword(c)))//密码错误{char buf[20] = "密码输入错误";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else if(1 == (rootveridandpassword(c)))//返回值为1账号密码正确{  if(rootRepeatlogin(node,c)){online_insertion(node,c);char buf[32] = "管理员登录成功..";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}online_userlist *p = node->head;p=p->next;while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环{   if(p->c_fd != 0){   char buf[70] = "\0";sprintf(buf,"%s[%s]:上线了",c->emojio,c->name);if(send(p->c_fd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}memset(buf,0,sizeof(buf));}p=p->next;}}else{return;}}
}

2.29//在root表中查看该用户是否为管理员

//在root表中查看该用户是否为管理员
int rootsearch(char *tablename)
{   int flag;sqlite3 *db = NULL;      int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}char sql[200] = "\0";sprintf(sql,"select *from root;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;for(i = 1 ; i < nrow+1;i++){int ret = strcmp(result[i*ncolumn + 1],tablename);if(ret == 0){   flag = 1;}//0行就是第一行  第i行第j列}if(flag == 1){   printf("数据库是否存在该用户%d存在\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}else{   flag = 0;printf("%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}
}

2.30/判断管理员用户与密码是否正确

//判断管理员用户与密码
int rootveridandpassword(clientlist *c)
{   printf("已进入管理员的密码效验工作\n");int flag;sqlite3 *db = NULL;      //数据库的名字记得更换int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}printf("已打开数据库\n");char sql[200] = "\0";sprintf(sql,"select *from root;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;int ret1,ret2;printf("已进行遍历数据库\n");for(i = 0 ; i < nrow+1;i++){ret1 = (strcmp(result[i*ncolumn + 1],c->name));ret2 = (strcmp(result[i*ncolumn + 2],c->password));printf("正在进行账户密码效验\n");printf("账户:%s\n",result[i*ncolumn + 1]);printf("密码:%s\n",result[i*ncolumn + 2]);printf("密码:%s\n",c->password);if(0 == ret1 && 0 == ret2){strcpy(c->emojio,result[i*ncolumn + 0]);printf("打印管理员存在数据库中的头像%s",result[i*ncolumn + 0]);printf("打印被赋值后的当前结构体头像%s",c->emojio);flag = 1;break;}//0行就是第一行  第i行第j列}if(flag == 1){   printf("核对返回值为%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}else{flag = 0;sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return flag;}
}

2.31//管理员重复登录判断

//管理员重复登录判断
int rootRepeatlogin(thread_node *node,clientlist *c)
{   printf("正在判断是否重复登录\n");char s1[32]={0};strcpy(s1,c->name);//判断自己是否登录printf("正在判断%s是否在线\n",c->to_name);online_userlist *p = node->head;while(p!=NULL){   if(strcmp(s1,p->name) == 0 ) {   printf("%s在线,重复登录\n",p->name);char buf[32] = "您已在线,请勿重复登录";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}return 0;}p=p->next;}printf("未遍历到该用户,可以进行登录\n");return 1;
}

2.32管理员踢人

//管理员踢人
void rootkick(thread_node *node,clientlist *c)
{int to_cfd = Find_person(node,c);  //在线返回对方的文件描述符printf("踢出对象的文件描述符为%d\n",to_cfd);if(0 == to_cfd){char buf[32] = "对方不在线...";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else{   if(searchtableroot(c->to_name) == 1){char buf[128] = "对方是管理员,无法进行操作";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}return;}else{char buf[128] = "管理员已将你踢出聊天室";if(send(to_cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}}
}

2.33管理员禁言

//管理员禁言
void silent(thread_node *node,clientlist *c)
{int to_cfd = Find_person(node,c);  //在线返回对方的文件描述符printf("禁言对象的文件描述符为%d\n",to_cfd);if(0 == to_cfd){char buf[32] = "对方不在线...";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else{if(searchtableroot(c->to_name) == 1){char buf[128] = "对方是管理员,无法进行操作";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}return;}else{char buf[128] = "管理员已将你禁言";if(send(to_cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}}
}

2.34解除禁言

//管理员解除禁言
void remsilent(thread_node *node,clientlist *c)
{int to_cfd = Find_person(node,c);  //在线返回对方的文件描述符printf("解除禁言对象的文件描述符为%d\n",to_cfd);if(0 == to_cfd){char buf[32] = "对方不在线...";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}else{char buf[128] = "管理员已将你解除禁言";if(send(to_cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}}
}

2.35 //获取管理员头像

//获取管理员头像
void getheadflag(clientlist *c)
{int flag;sqlite3 *db = NULL;      int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}char sql[200] = "\0";sprintf(sql,"select *from root;");char *errmsg;//定义指着指向错误信息char **result;//相当于二维数组  保存查询到的信息int nrow,ncolumn;//分别代表行和列ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);int j,i;int Index = ncolumn;for(i = 1 ; i < nrow+1;i++){int ret = strcmp(result[i*ncolumn + 1],c->name);if(ret == 0){   strcpy(c->emojio,result[i*ncolumn + 0]);flag = 1;printf("当前管理员头像%s\n",result[i*ncolumn + 0]);printf("c->emojio=%s\n",c->emojio);}//0行就是第一行  第i行第j列}if(flag == 1){   printf("管理员头像获取成功%s存在\n",c->emojio);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return;}else{   printf("管理员头像获取失败%d不存在\n",flag);flag = 0;printf("%d\n",flag);sqlite3_free_table(result);//释放数据库ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return;}
}

2.36创建表

//创建表  注意更换数据库
void createtable()
{sqlite3 *db = NULL;  //目标数据库int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){perror("sqlite3_open");exit(1);}char *errmsg;char sql[500] = "\0";strcpy(sql,"create table user (name text,password text);");ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);if(SQLITE_OK != ret){perror("sqlite3_exec:");printf("errmsg:%s\n",errmsg);exit(1);}ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite3_close:");exit(1);}return;
}

2.37执行sql语句函数

//执行sql语句函数
void execstate(char *sql)//执行SQL语句
{sqlite3 *db =NULL;int ret = sqlite3_open("user.db",&db);if(SQLITE_OK != ret){ERRLOG("sqlite3_open");}char *errmsg;ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);if(SQLITE_OK != ret){ERRLOG("sqlite3_exec");}ret = sqlite3_close(db);if(SQLITE_OK != ret){perror("sqlite_close:");exit(1);}
}

2.38//根据名字修改密码

//根据名字修改密码
void Insert_updated_data(char *name,char *password)
{char sql[500] = "\0";sprintf(sql,"update user set password = '%s' where name = '%s';",password,name);execstate(sql);}

2.39指定删除普通用户的中数据

//指定删除数据库里面的用户
void deleteuser(clientlist *c)
{   char sql[200] = "\0";memset(sql,0,sizeof(sql));//清空sqlsprintf(sql,"delete from user where name = '%s';",c->name);execstate(sql);printf("删除数据库内容成功\n");
}

2.40指定删除管理员表中的数据

//指定删除数据库里面的用户
void deleteroot(clientlist *c)
{   char sql[200] = "\0";memset(sql,0,sizeof(sql));//清空sqlsprintf(sql,"delete from root where name = '%s';",c->name);execstate(sql);printf("删除root内容成功\n");
}

2.41撤销管理员身份

//功能标志15 撤销管理员身份
void cancelroot(thread_node *node,clientlist *c)// 功能标志15 撤销管理员身份
{char buf[128] = "撤销成功";if(send(node->cfd,buf,sizeof(buf),0) == -1){ERRLOG("send error");}deleteroot(c);exituser(node,c);return;
}

2.42判断链表是否为空

//判断链表是否为空
int onlineempty(online_userlist *head)
{if (head->next==NULL){return  1 ;}else {return 0;}
}
//判断除自己是否还有别人在线
int onlineempty1(online_userlist *head)
{if (head->next->next==NULL){return  1 ;}else {return 0;}
}

chatroom.h

#ifndef _CHATROOM_H
#define _CAHTROOM_H
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <time.h>
#define N 256
#define ERRLOG(errmsg) do{\perror(errmsg);\printf("%s - %s - %d\n",__FILE__,__func__,__LINE__);\exit(1);\}while(0)#define MAXLINE 8192
pthread_mutex_t mutex;
typedef struct client
{int flag; //功能标志 注册标志1 登录2  私聊3 修改密码4 群聊界面5 查看在线用户6 注销用户7  查看聊天记录8  下载文件9char emojio[20];//头像保存char name[32];//姓名int  root;    //管理员标志char msg[50];//聊天内容char password[32];//密码char to_name[50];//聊天对象char buf[256];//文件内容 int num;
}clientlist;typedef struct file
{char text[N];int num;
}MSG;typedef struct online_user
{char name[32];//姓名int c_fd;struct  online_user*next;  //指针域
}online_userlist;typedef struct thread_node
{int cfd;online_userlist *head;
}thread_node;//在线用户链表
online_userlist* online_userlistcreate();int sockfd; pthread_t pti1[500];//线程号数组用于保存accept线程号void Init();//初始化 创建套接字等void server();//accept接受创建线程void *myfun(void *arg);//进入线程函数void messagehand(thread_node *node);//接受消息 判断功能标志位void insert_updata(thread_node *node,clientlist *c);//向数据库中插入int searchtable(char *tablename);//在数据库查询用户 存在返回1 不存在返回0void execstate(char *sql);//执行sql语句int veridandpassword(clientlist *p);//判断账户密码是否正确 正确返回1错误返回0void Register(thread_node *node,clientlist *c); //注册 判断是否已经存在 不存在进行插入数据库int onlineempty(online_userlist *head);//判断链表是否为空void login(thread_node *node,clientlist *c);//用户登录
//将登录用户添加至链表
void online_insertion(thread_node *node,clientlist *c);//登录用户插入链表int Find_person(thread_node *node,clientlist *c);判断是否在线 在线返回对方文件描述符void privatechat(thread_node *node,clientlist *c);//私聊void changepassword(thread_node *node,clientlist *c);void Insert_updated_data(char *name,char *password);//在数据库中按照名字修改密码void groupchat(thread_node *node,clientlist *c);//进行群聊int onlineempty1(online_userlist *head);//判断除了自己之外是否还要有别人在线void exituser(thread_node *node,clientlist *c);//将退出聊天室的用户删除结点void viewonline_user(thread_node *node,clientlist *c);//功能 标志查看所有在线用户void deleteuser(clientlist *c);//指定删除数据库内用void loguser(thread_node *node,clientlist *c);//注销用户void insert_uprecord(thread_node *node,clientlist *c);//将聊天记录保存至数据库int Repeatlogin(thread_node *node,clientlist *c);//判断是否重复登录void Checkrecord(thread_node *node);//查看聊天记录void filetransfer(thread_node *node,clientlist *c);//文件传输void applyroot(thread_node *node,clientlist *c);//申请管理员用户int searchtableroot(char *tablename);//判断该用户是否是已经是管理员void insert_updataroot(thread_node *node,clientlist *c);//向管理员表中插入管理员资料void rootlogin(thread_node *node,clientlist *c);//管理员登录int rootsearch(char *tablename);//在root表中查看该用户是否为管理员int rootveridandpassword(clientlist *p);//判断管理员密码void rootkick(thread_node *node,clientlist *c);//管理员踢人void silent(thread_node *node,clientlist *c);//管理员禁言void remsilent(thread_node *node,clientlist *c);//管理员解除禁言void getheadflag(clientlist *c);//获取管理员头像int rootRepeatlogin(thread_node *node,clientlist *c);//管理员重复登录void deleteroot(clientlist *c);//指定删除root表中的用户void cancelroot(thread_node *node,clientlist *c);//取消管理员身份#endif

2.客户端

client.c

#include "clientaddr.h"
unsigned char ip_str[] = "192.168.91.151";unsigned char port_str[] = "8787";
int flag1 = 0;
int flag2 = 1;void n_init()//客户端初始化框架
{clientfd2  = socket(AF_INET,SOCK_STREAM,0);if(clientfd2 == -1){perror("socket");return;}struct sockaddr_in clientaddr;clientaddr.sin_family = AF_INET;//协议族  IPV4clientaddr.sin_addr.s_addr = inet_addr(ip_str); /*in_addr_t inet_addr(const char *cp);将点分十进制字符串ip地址转化为网络序的无符号4字节整数ip地址*/int addrlen = sizeof(clientaddr);clientaddr.sin_port = htons(atoi(port_str));if(connect(clientfd2,(struct sockaddr *)&clientaddr,addrlen)==-1){perror("\033[0;31m无法连接到服务器\033[0m\n");ERRLOG("connect");}return;
}void selectfun()//进入页面选着功能
{       usleep(500*1000);system("clear");printf("\033[0;46m*===================================================================*\033[0m\n");printf("\033[0;46m|                                                                   |\033[0m\n");printf("\033[0;46m|*********************\033[0;33m欢迎来到防脱发研究中心\033[0;46m************************|\033[0m\n");printf("\033[0;46m|                                                      作者:爱码士  |\033[0m\n");printf("\033[0;46m|-------------------------------------------------------------------|\033[0m\n");printf("\033[0;46m|                                                                   |\033[0m\n");printf("\033[0;46m|                         \033[0;35m1.用户登录												

(C语言)即时通讯系统相关推荐

  1. Netty实战 IM即时通讯系统(七)数据传输载体ByteBuf介绍

    ## Netty实战 IM即时通讯系统(七)数据传输载体ByteBuf介绍 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向 ...

  2. php workman视频,PHP即时通讯系统单人多人聊天IM视频会议实时音视频红包功能

    PHP即时通讯系统单人多人聊天IM视频会议实时音视频红包功能 介绍 workman构建即时通讯,含单人多人聊天,IM视频会议,实时音视频以及红包转账功能 软件架构 软件架构说明 1.后台采用PHP的w ...

  3. GGTalk ——C#开源即时通讯系统

    http://www.cnblogs.com/justnow/ GGTalk --C#开源即时通讯系统 下载中心 GGTalk(简称GG)是可在广域网部署运行的QQ高仿版,2013.8.7发布GG V ...

  4. Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline

    Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向 ...

  5. Netty实战 IM即时通讯系统(十一)pipeline与channelHandler

    Netty实战 IM即时通讯系统(十一)pipeline与channelHandler 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端 ...

  6. Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息

    Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数据 ...

  7. Netty实战 IM即时通讯系统(九)实现客户端登录

    ## Netty实战 IM即时通讯系统(九)实现客户端登录 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数据传输载 ...

  8. Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码

    Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数 ...

  9. Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信

    ## Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 实战: 客户端和服务端双向通信 数据传输载 ...

最新文章

  1. 拿来主义——老外写的系统统计脚本
  2. 让XP恢复文件权限功能
  3. ubuntu安装与配置nfs服务器
  4. PyTorch基础-Tensor的属性,数据,运算-01
  5. HDU - 6625 three arrays (Trie+dfs)
  6. python timeit用法_十大Python开发技巧
  7. 论文浅尝 - ICLR2022 | OntoProtein:融入基因本体知识的蛋白质预训练
  8. Linux文件下载和上传工具lrzsz
  9. 深度学习笔记_ResNet
  10. 细粒度分类:Hierarchical Bilinear Pooling(HBP),分级双线性池化(一)
  11. SAT写作例子之Frank Lloyd Wright
  12. 2022质量员-土建方向-岗位技能(质量员)特种作业证考试题库及模拟考试
  13. Android应用盈利广告平台的嵌入方法详解
  14. 内存颗粒和闪存颗粒的区别_内存条怎么判断好坏? 内存颗粒的种类及其差别介绍...
  15. 数学与计算机学院女生节标语,女生节标语理学院
  16. Python:实现similarity search相似性搜索算法(附完整源码)
  17. 什么是苹果cms?苹果cms如何安装及使用?
  18. ORACLE updata是提示违反唯一约束条件
  19. 眼睛不可缺少的8大营养素
  20. 大数据技术十大核心原理

热门文章

  1. 干支记年法 在我国古代和近代,一直采用干支法纪年。它采用10天干和12地支配合,一个循环周期为60年。
  2. IT 人的纠结:去大公司还是去小公司?
  3. Java 支付对接之银联电子商务(海南)聚合二维码支付(POS 通插件 C 扫 B 业务)
  4. 使用 svg-sprite-loader、svgo-loader 优化 svg symbols
  5. Avizo加载图片闪退
  6. 计算机网络中各种命令的验证与使用
  7. PMP考试报名有什么硬性要求?你达标了吗?
  8. 编写Android Studio插件
  9. 基于ADS的简单射频电路调试
  10. 基于ADN8830 TEC控制器实现温度控制电路设计