目录
注意
相比较第一个版本 第二个版本将服务器的多线程并发服务器改成了epoll+线程池处理
客户端依旧运用的是上个版本的客户端
一 .项目功能
目前该项目实现基本功能有:
基本功能:
1.注册 2.登录 3.群聊 4快捷短语 5.保存聊天记录 6.查看聊天记录
7.文件传输 8.敏感词汇判断 9.私聊 10.离线消息发送
11.查看在线用户 12.管理员申请 13.管理员专属头像 14.禁言用户
15.解除禁言 16.管理员踢人 17.修改密码 18.注销用户 19.撤销管理员身份
二.项目简介
epoll
1.相比较: select内部使用数组实现,poll是链表。他们需要做内核区到用户区的转换,还需要做数据拷贝,因此效率低
2.epoll不需要做内核区到用户区的转换,因为数据存在共享内存中。epoll维护的树在共享内存中,内核区和用户区去操作共享内存,因此不需要区域转换,也不需要拷贝操作。
线程池
(1)降低销毁资源:重复利用线程池中已经存在的线程,减少了线程的创建和消亡造成的性能开销。
(2)提高了相应速率:当任务到达时,任务可以不需要等到线程创建就能够执行。
(3)防止服务器过载:形成内存溢出,或者cpu耗尽。
(4)提高线程的可管理性:线程时稀缺资源,若无限的创建线程,不仅会消耗资源,还会降低系统的稳定性,使用线程池可以统一的分配,调优和监控
三.项目演示
即使通讯系统项目
四.项目功能基本介绍
## 三 .项目具体介绍
### 1.注册
```
注册有重复注册判断 在数据库 普通用户表中遍历 如果存在该用户则 停止注册
将注册的信息保存在通信协议中 服务器收到注册的功能标志 调用注册函数 将注册的信息插入数据库的普通用户表中;
```
#### 2.登录
```
用户登录
用一个链表保存在线用户 链表里包含用户的文件描述符 负责与用户之间通信
登录成功将会给所有在线用户发送自己已上线;
登录第一步在数据库中遍历该用户是否注册 只有注册了才能进行登录操作 第一步是遍历链表是否已经登录如果已经在别处登录 将不能进行登录操作.
```
### 3.群聊
```
因为登录过程中所有的用户的文件描述符 都会保存在在线链表中 群聊直接遍历在线用户链表
对每个文件描述符对应的用户发送消息
群聊的第一步 首先判断除了自己以外是否还有别的用户在线
第二步才是遍历链表 对除了自己的每个在线用户发送消息
```
### 4.快捷短语
```
在发送消息之前 判断 所要发送的消息 是否有对应的快捷短语 如果有快捷短语 则将快捷短语strcpy 至通信协议
聊天内容的字符串数组 进行发送
如果没有找对对应的快捷消息 则正常发送
```
### 5.聊天记录的保存以及查看
```
在服务器有一个recv函数循环的阻塞接受 客户端发来的结构体 也就是通信协议 每一次的信息传递都要经过这个函数
只用在recv函数下面 运用数据库相关的操作将 发送信息的用户 发送时间 以及消息内容存储至数据库中
查看聊天记录遍历数据库内容 将遍历的内容以固定格式保存在buf 发送给客户端;
```
### 6.文件传输
```
使用read函数 从打开的文件中循环读取内容 将内容与读取到的字节数
保存在通信协议中内容保存专用于文件传输的字符串数组中,将读取到的字节数一并传输 为了写入时 按读取的字节数进行写入
发送给服务器,再由服务器进行转发到对应的客户端 为了防止粘包现象采用传输结构体
```
### 7.敏感词判断
```
敏感词判断 数据中有一张表保存了各种敏感词汇 当客户端进行发送消息时 服务器遍历数据库判断消息是否为敏感词汇
一旦是敏感词汇 返回客户端 发送失败信号 并停止将敏感词汇转发给其他客户端
```
## 8.私聊
```
私聊第一步就是 服务器遍历在线用户链表 判断私聊对象是否在线 不在线返回给客户端 不在线消息
私聊和群聊相似 当客户端发送私聊信号时 服务器遍历链表 遍历出私聊对象 再将消息内容转发给对应的客户端
```
### 9.离线消息的发送
```
当私聊不在线是私聊信息会保存在数据库的 离线消息表中 当对方上线时 立即将消息发送给对方
```
### 10.查看在线用户
```
查看在线用户 和群聊相似 都是一个遍历链表的过程 当客户端发送查看在线用户的信号给 服务器
服务器在进行遍历在线链表将每遍历一个用户 将其用户名按固定格式 发送给 需要查看在线用户的用户
```
## 管理员用户的相关操作
### 11.申请称为管理员设置管理员头像
```
只有先成为普通用户以后 在能申请管理员 管理员有专属的管理员头像
申请成管理员后必需重新以管理员身份进行登录
在群聊过程中 管理员自带头像
客户端发送申请管理员信号以后 服务器 会将管理员用户信息 姓名密码头像等
保存在数据库的root表中
管理员用户 不可以重复申请
```
### 12.管理员禁言用户
### 13.解除禁言
```
管理员禁言 客户端给服务器发送一个 禁言信号 以及禁言对象 服务器首先判断发送禁言指令的是不是管理员 在对方是否在线 其次判断对方是不是管理员
管理员不能对管理员进行踢人 或者 禁言操作
如果对方是普通用则给对方发送禁言信号 这里采用的是全局变量flag 客户端收到禁言信号时flag至0 失去通信权限
收到解禁信号 flag至1 恢复通信权限
```
### 14.管理员踢人
```
管理员踢人 和禁言一样判断是否在线 在判断是否为管理员 如果为普通用户 则向对应的客户端发送退出聊天室指令
客户端收到退出指令以后自主结束整个进程 同时服务器将在线用户链表中的该用户踢出链表 并给所有在线用户发送消息
```
#### 15.修改密码
```
没有设置密保 直接根据名字进行修改密码 修改密码以后需要从新登录
客户端发送 修改密码信号 以及新密码 至服务器 服务器调用封装好的数据库函数 执行update更新数据库的语句 根据名字对密码进行更新
update user set password = '%s' where name = '%s'
```
### 16.注销用户
```
注销用户和修改密码类似
首先进行账户密码判断 账户密码正确才能进行注销用户
数据库找到关于注销名字的记录 然后删除这条记录 同时服务器将在线用户链表中的该用户踢出链表;
delete from user where name = '%s'
```
#### 17.撤销管理员身份
```
注销用户和注销用户类似
首先进行账户密码判断 账户密码正确才能进行撤销root
数据库找到关于撤销名字的记录 然后删除这条记录 同时服务器将在线用户链表中的该用户踢出链表;
delete from user where name = '%s'
```
退出聊天室 结束整个进程
四.代码部分
server.c
#include "server.h"
int flag2 = 1; //发送文件信号
extern struct pthreadpool *pool ;
extern online_userlist *head;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int socket_init()
{
unsigned char ip_str[] = "192.168.91.151";
unsigned char port_str[] = "8787";
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("socket error");
return -1;
}
//设置套接字端口属性为端口释放后可以重复使用
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
//将套接字进行IP与端口的绑定
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); //清空内存
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port_str)); //端口和ip要改成大端模式
addr.sin_addr.s_addr = inet_addr(ip_str);
int ret = bind(sockfd, (struct sockaddr *)&(addr), sizeof(addr));
if (ret == -1)
{
perror("bind error");
printf("绑定套接字失败.\n");
return -1;
}
//监听
ret = listen(sockfd, 20);
if (ret == -1)
{
perror("listen error");
printf("监听套接字失败.\n");
return -1;
}
printf("客户机已经开启,等待用户连接中.....\n");
return sockfd;
}
// epoll操作
int mypoll(int sockfd)
{
int N = sizeof(clientlist);//MSg为通信协议 计算通信协议的大小
struct sockaddr_in clientaddr;//创建被填充的网络信息结构体
socklen_t addrlen = sizeof(clientaddr); //计算被网络信息结构体的大小
//第一步:创建epoll对象
int epfd = epoll_create(2000); //创建内核事件表 最大保存文件描述符个数为2000
//如果用epoll_create1一般参数为0 每添加一个自动增加结点保存文件描述符
if (-1 == epfd)//成功返回非负数的文件描述符 失败返回-1
{
ERRLOG("epoll");
}
//epoll树上挂的就是下面这个结构体
struct epoll_event ev, events[2000] = {0};//事件初始化 读 写 异常等
ev.events = EPOLLIN; //监听sockfd可读
ev.data.fd = sockfd;
//操作epoll内核时间表
// 参数一create生成的专用文件描述符 二操作类型 增 删 改 三 关联的文件描述符 四epoll_event结构体也就是上面的结构体
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
if (-1 == ret)
{//成功返回0 失败返回-1
printf("%s\n", strerror(errno));
return -1;
}
//accept接受客户端连接
int i;
int acceptfd = 0;
while (1)
{
clientlist agreement;
//Msg msg;//通信协议
//参数分析 参数一 create返回值 参数二结点结构体(传出参数) 参数三事件表的最大存储个数 参数四 阻塞方式永久阻塞0是立即返回
int num = epoll_wait(epfd, events, 2000, -1);//等待IO事件发生
if (-1 == num)
{
printf("epoll_wait() failed\n");
return -1;
}//成功:返回准备好的文件描述符的个数,如果没有准备号的,则返回0 错误:返回 -1
for (i = 0; i < num; i++)
{
if (events[i].data.fd == sockfd) //表示有客户端发起链接
{
if ((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
{
ERRLOG("accept error");
continue;
}
//为新的文件描述符注册事件
printf("accept fd %d\n", acceptfd);
ev.events = EPOLLIN; //监听sockfd可读
ev.data.fd = acceptfd;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfd, &ev);
if (-1 == ret)
{
ERRLOG("epoll_ctl");
}
printf("客户端%s:%d 连接了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}
else //有客户端发消息
{
//Myarg arg;
Myarg* arg = (Myarg *)malloc(sizeof(Myarg));
memset(arg, 0, sizeof(Myarg));
if (events[i].events & EPOLLIN) //如果事件是可读的
{
memset(&agreement, 0, sizeof(agreement));
ret = recv(events[i].data.fd, &agreement, N, 0);
if(agreement.flag == 3 || agreement.flag == 5)
{
insert_uprecord(&agreement);
}
if (ret == -1)
{
ERRLOG("recv");
}
else if (0 == ret)
{
ev.events = EPOLLIN; //监听sockfd可读
ev.data.fd = events[i].data.fd;
int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev); //客户端退出,注销事件
if (-1 == ret)
{
ERRLOG("epoll_ctl");
}
printf("退出的客户端fd=%d\n",events[i].data.fd);
Offlineprocessing(events[i].data.fd);//掉线处理函数
close(events[i].data.fd);
}
else
{
// //信息拷贝
arg->msg.flag=agreement.flag;
arg->msg.root=agreement.root;
arg->msg.num=agreement.num;
strcpy(arg->msg.name, agreement.name);
strcpy(arg->msg.to_name, agreement.to_name);
strcpy(arg->msg.msg, agreement.msg);
printf("agreement.msg = %s\n",agreement.msg);
strcpy(arg->msg.emojio, agreement.emojio);
strcpy(arg->msg.password, agreement.password);
strcpy(arg->msg.buf, agreement.buf);
arg->fd = events[i].data.fd;
ThreadAddJob(pool, work, (void *)arg);//任務添加到任務隊列
}
}
}
}
}
}
//线程函数从任务队列去任务
void *ThreadRun(void *arg)
{
struct pthreadpool *pool = (struct pthreadpool *)arg;
struct job *pjob = NULL;//任务队列指针
while (1)
{
pthread_mutex_lock(&pool->mutex);
if (pool->m_QueueCurNum == 0)
{
printf("当前任务队列为空,线程%ld阻塞等待任务到来!\n", pthread_self());
pthread_cond_wait(&pool->m_QueueNotEmpty, &pool->mutex);
}
pjob = pool->head;//指针指向任务队列的头部
pool->m_QueueCurNum--;//当前任务数量--
if (pool->m_QueueCurNum != pool->m_QueueMaxNum)//如果当前任务队列没有满
{
pthread_cond_signal(&pool->m_QueueNotFull); //每次线程取出一个任务之后,都要唤醒线程去添加任务
}
if (pool->m_QueueCurNum == 0)//如果当前任务对列没有任务
{
pool->head = pool->rear = NULL;
pthread_cond_broadcast(&pool->m_QueueEmpty); //当通知任务队列添加任务无果后,发送条件变量,通知其销毁线程
}
else
{
pool->head = pool->head->next;
}
pthread_mutex_unlock(&pool->mutex);
pjob->func(pjob->arg);//运行任务函数
free(pjob);
pjob = NULL;
}
}
//参数分析 参数1线程池线程数量 参数二 任务队列最大任务数
struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum) //初始化線程池 創建線程池
{
struct pthreadpool *pool = (struct pthreadpool *)malloc(sizeof(struct pthreadpool)); //创建一个线程池初始化的结构体
pool->m_QueueCurNum = 0;//当前任务队列的任务数
pool->m_QueueMaxNum = QueueMaxNum;//任务队列的最大最大任务数
pool->head = NULL;
pool->rear = NULL;
pool->m_threadNum = ThreadNum;//已经开启的线程数量也就是创建的线程数量
pool->m_pthreadIDs = (pthread_t *)malloc(sizeof(pthread_t) * ThreadNum);//保存已开启的线程IO
//动态数组名 保证后续指针的移动合法
pthread_mutex_init(&pool->mutex, NULL);//初始化一把锁
pthread_cond_init(&pool->m_QueueEmpty, NULL);//任务队列为空条件
pthread_cond_init(&pool->m_QueueNotEmpty, NULL);//任务队列不为空的条件
pthread_cond_init(&pool->m_QueueNotFull, NULL);//任务队列不满的条件
for (int i = 0; i < ThreadNum; i++)
{
pthread_create(&pool->m_pthreadIDs[i], NULL, ThreadRun, pool);
}
return pool;
}
//將任務函數添加到任務隊列
void ThreadAddJob(struct pthreadpool *pool, void *(*func)(void *arg), void *arg)//
{
pthread_mutex_lock(&pool->mutex);
if (pool->m_QueueCurNum == pool->m_QueueMaxNum)
{
printf("任务队列已满,挂起等待线程执行完毕。。。\n");
pthread_cond_wait(&pool->m_QueueNotFull, &pool->mutex);
}
struct job *pjob = (struct job *)malloc(sizeof(struct job)); //创建任务队列
pjob->func = func;//任务函数通过任务传参传入任务函数
pjob->arg = arg;//任务函数的参数
// pjob->func(pjob->arg);
pjob->next = NULL;
if (pool->head == NULL)//如果任务队列 里面没有任务
{
pool->head = pool->rear = pjob;//那么将任务函数放置头结点处
pthread_cond_broadcast(&pool->m_QueueNotEmpty); //添加任务后,唤醒任意一个线程开始执行任务
}
else
{
pool->rear->next = pjob;//如果任务队列有任务 就将新的任务函数放在任务队的末尾
pool->rear = pjob;//尾指针指向最后一盒任务函数
}
pool->m_QueueCurNum++;//当前任务数量++
pthread_mutex_unlock(&pool->mutex);
}
//銷毀線程池
void ThreadDestroy(struct pthreadpool *pool)
{
pthread_mutex_lock(&pool->mutex);
while (pool->m_QueueCurNum != 0)
{
printf("阻塞等待销毁线程。。。\n");
pthread_cond_wait(&pool->m_QueueEmpty, &pool->mutex);
}
printf("任务结束,线程%ld被销毁\n", pthread_self());
pthread_mutex_unlock(&pool->mutex);
pthread_cond_broadcast(&pool->m_QueueNotEmpty);
pthread_cond_broadcast(&pool->m_QueueNotFull);
int i;
for (i = 0; i < pool->m_threadNum; i++)
{
pthread_join(pool->m_pthreadIDs[i], NULL);
}
pthread_mutex_destroy(&pool->mutex);
pthread_cond_destroy(&pool->m_QueueEmpty);
pthread_cond_destroy(&pool->m_QueueNotEmpty);
pthread_cond_destroy(&pool->m_QueueNotFull);
free(pool->m_pthreadIDs);
struct job *tmp;
while (pool->head != NULL)
{
tmp = pool->head;
pool->head = pool->head->next;
free(tmp);
}
free(pool);
}
//任务工作函数
void *work(void *arg)
{
Myarg *workarg = (Myarg *)arg;
printf("work: %d\n", workarg->fd);
printf("msg: %s\n",workarg->msg.msg);
printf("msg: %d\n",workarg->msg.flag);
switch(workarg->msg.flag)
{
case REGISTER:
Register(workarg,&workarg->msg);//注册用户
printf(" 注册用户\n");
break;
case LOGIN:
login(workarg,&workarg->msg);//登录
printf("用户登录功能\n");
break;
case PRIVATE:
privatechat(workarg,&workarg->msg); //私聊
printf("私聊功能\n");
break;
case CHPASSWORD:
changepassword(workarg,&workarg->msg); //修改密码
printf("修改密码\n");
break;
case GROUPCHAT:
groupchat(workarg,&workarg->msg);//进行群聊
break;
case VIEWUSER: //查看所有在线用户
viewonline_user(workarg,&workarg->msg);
break;
case LOGUSER://注销用户
loguser(workarg,&workarg->msg);
break;
case CHECKRECORD:
Checkrecord(workarg);//查看聊天记录
break;
case FILETRAN:
filetransfer(workarg,&workarg->msg); //文件传输
break;
case APPLYROOT:
applyroot(workarg,&workarg->msg); //申请管理员用户
break;
case ROOTLOGIN:
rootlogin(workarg,&workarg->msg); //管理员登录
break;
case ROOTKICK:
rootkick(workarg,&workarg->msg); //管理员踢人
break;
case SILENT:
silent(workarg,&workarg->msg); //管理员禁言
break;
case REMSILENT:
remsilent(workarg,&workarg->msg); //管理员解除用户禁言
break;
case CANCELROOT:
cancelroot(workarg,&workarg->msg); //取消管理员身份
break;
default:
break;
}
free(arg);
}
//在线用户链表
online_userlist* online_userlistcreate()
{
online_userlist *head = (online_userlist *)malloc(sizeof(online_userlist));
head->next = NULL;
return head;
}
//注册部分
void Register(Myarg *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->fd,arr,sizeof(arr),0) == -1)
{
ERRLOG("send error");
}
return;
}
else
{
insert_updata(node,c); //向数据库中插入数据
}
}
//向数据库中插入注册数据
void insert_updata(Myarg *node,clientlist *c)
{
printf("注册的名字%s\n",c->name);
char *errmsg;
sqlite3 *db =NULL;
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
sprintf(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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
//在数据库查询用户 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;
}
}
//执行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);
}
}
//登录部分
void login(Myarg *node,clientlist *c)
{
//判断是否注册
if(0 == (searchtable(c->name)))
{
char buf[32] = "该用户不存在,请注册...";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
else if( 0 == (veridandpassword(c)))//密码错误
{
char buf[20] = "密码输入错误..";
if(send(node->fd,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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
online_userlist *p = 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;
}
sleep(2);
sendOfflinemessage(node,c);
}
else
{
return;
}
}
}
//重复登录判断
int Repeatlogin(Myarg *node,clientlist *c)
{
printf("正在判断是否重复登录\n");
char s1[32]={0};
strcpy(s1,c->name);//判断自己是否登录
printf("正在判断%s是否在线\n",c->to_name);
online_userlist *p = head;
while(p!=NULL)
{
if(strcmp(s1,p->name) == 0 )
{ printf("%s在线,重复登录\n",p->name);
char buf[32] = "您已在线,请勿重复登录";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
return 0;
}
p=p->next;
}
printf("未遍历到该用户,可以进行登录\n");
return 1;
}
//登录插入链表
void online_insertion(Myarg *node,clientlist *c)
{
online_userlist *tmp=(online_userlist *)malloc(sizeof(online_userlist));
tmp->next=NULL;
online_userlist *p =head;//定义一个指针为表头
strcpy(tmp->name,c->name);
tmp->c_fd = node->fd;
printf("判断:%s,%d是否进行插入链表\n",tmp->name,tmp->c_fd);
if(1==onlineempty())
{printf("1");
tmp->next=p->next;//令新插入的结点指针指向下一个结点
p->next=tmp;
}
else
{printf("2");
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");
}
//验证账户与密码 正确返回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;
}
}
//判断链表是否为空
int onlineempty()
{
if (head->next==NULL)
{
return 1 ;
}
else
{printf("kong3\n");
return 0;
}
}
//判断除自己是否还有别人在线
int onlineempty1()
{
if (head->next->next==NULL)
{
return 1 ;
}
else
{
return 0;
}
}
//私聊部分
void privatechat(Myarg *node,clientlist *c)
{
int flag = 0;
printf("正在运行私聊函数\n");
int to_cfd = Find_person(c); //在线返回对方的文件描述符
printf("私聊对象的文件描述符为%d\n",to_cfd);
if(1 == sensitiveword(c->msg))
{
printf("该消息存在敏感词\n");
char buf2[128] = "\0";
sprintf(buf2,"[系统日志]:你的消息中存在敏感词汇,无法发送");
if(send(node->fd,buf2,sizeof(buf2),0) == -1)
{
ERRLOG("send error");
}
return;
}
if(0 == to_cfd)
{
saveOfflinemessage(c);
char buf[32] = "对方不在线...";
if(send(node->fd,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->fd,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");
}
}
}
//判断是否在线 在线返回对方文件描述符 不在线返回0
int Find_person(clientlist *c)
{
char s1[32]={0};
strcpy(s1,c->to_name);//想跟s1进行私聊
printf("正在判断%s是否在线\n",c->to_name);
online_userlist *p = head;
while(p!=NULL)
{
if(strcmp(s1,p->name) == 0 )
{ printf("%s在线\n",p->name);
return p->c_fd;//私聊对象的文件描述符
}
p=p->next;
}
return 0;
}
//判断是否为敏感词 存在返回1 不存在返回0
int sensitiveword(char *sensitive)
{
printf("正在进行敏感词判断\n");
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 sensitive;");
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++)
{
printf("数据库遍历结果%s\n",result[i*ncolumn + 0]);
int ret = strcmp(result[i*ncolumn + 0],sensitive);
if(ret == 0)
{
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;
printf("数据库遍历返回值%d\n",flag);
sqlite3_free_table(result);//释放数据库
ret = sqlite3_close(db);
if(SQLITE_OK != ret)
{
perror("sqlite3_close:");
exit(1);
}
return flag;
}
}
//离线消息保存至表中
void saveOfflinemessage(clientlist *c)
{
time_t timep;
struct tm *p;
time (&timep);
p=gmtime(&timep);
char newtime[32] = {0};
memset(newtime,0,32);
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);
char *errmsg;
sqlite3 *db =NULL;
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
sprintf(sql,"insert into message(name,toname,message,time) values('%s','%s','%s','%s')",c->name,c->to_name,c->msg,newtime);
printf("数据库sql语句测试\n");
execstate(sql);
printf("\033[0;34m插入成功\033\n");
}
//修改密码部分
void changepassword(Myarg *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->fd,buf1,sizeof(buf1),0) == -1)
{
ERRLOG("send error");
}
printf("用户%s已成功修改密码",c->name);
exituser(c);
}
//根据名字修改密码
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);
}
//退出群聊 删除链表内的用户结点
void exituser(clientlist *c)
{
online_userlist *p = 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;
}
}
//群聊部分
void groupchat(Myarg *node,clientlist *c)
{
printf("正在运行管理员群聊函数\n");
online_userlist *p = head;
int ret;
ret = onlineempty1(p);
if(1 == ret)
{
char buf1[128] = "\0";
strcpy(buf1,"[系统日志]:当前没有别的用户在线");
if(send(node->fd,buf1,sizeof(buf1),0) == -1)
{
ERRLOG("send error");
}
return;
}
printf("正在遍历链表给每个用户发送消息\n");
printf("打印管理员标志%d\n",c->root);
if(1 == sensitiveword(c->msg))
{
printf("该消息存在敏感词%s\n",c->msg);
char buf2[128] = "\0";
sprintf(buf2,"[系统日志]:你的消息中存在敏感词汇,无法发送");
if(send(node->fd,buf2,sizeof(buf2),0) == -1)
{
ERRLOG("send error");
}
return;
}
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(strcmp(p->name,c->name) != 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(strcmp(p->name,c->name) != 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;
}
}
}
//获取管理员头像
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;
}
}
//查看所有在线用户
void viewonline_user(Myarg *node,clientlist *c)
{
online_userlist *p = head->next;
int ret;
ret = onlineempty(p);
if(1 == ret)
{
char buf1[128] = "\0";
strcpy(buf1,"[系统日志]:当前没有别的用户在线");
if(send(node->fd,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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
sleep(1);
}
p=p->next;
}
}
//注销用户
void loguser(Myarg *node,clientlist *c)
{
deleteuser(c);
if(c->root == 1)
{
deleteroot(c);
}
printf("注销用户删除数据库成功\n");
char buf[32] = "注销成功..";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
exituser(c);
printf("注销用户删除链表结点成功\n");
}
//指定删除数据库里面的用户
void deleteuser(clientlist *c)
{
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
sprintf(sql,"delete from user where name = '%s';",c->name);
execstate(sql);
printf("删除数据库内容成功\n");
return;
}
//指定删除数据库里面的root用户
void deleteroot(clientlist *c)
{
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
sprintf(sql,"delete from root where name = '%s';",c->name);
execstate(sql);
printf("删除root内容成功\n");
return;
}
//查看聊天记录部分
//将聊天记录插入记录表(保存聊天记录)
void insert_uprecord(clientlist *c)
{
printf("正在进行聊天记录存储\n");
printf("聊天方式标志位:%d",c->flag);
printf("聊天内容:%s",c->msg);
if(1 == sensitiveword(c->msg))
{
return;
}
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);
char *errmsg;
sqlite3 *db =NULL;
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
pthread_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");
}
//查看聊天记录
void Checkrecord(Myarg *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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
}
//管理员申请部分
//管理员用户的申请
void applyroot(Myarg *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->fd,arr,sizeof(arr),0) == -1)
{
ERRLOG("send error");
}
return;
}
else
{
insert_updataroot(node,c);
}
}
//在数据库中管理员表中查看是否存在该管理员
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;
}
}
//向root表中插入数据
void insert_updataroot(Myarg *node,clientlist *c)
{
printf("申请管理员的用户%s\n",c->name);
char *errmsg;
sqlite3 *db =NULL;
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
pthread_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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
exituser(c);//注册完将其踢出链表让其进入初始界面从新登录
return;
}
//管理员登录部分
//管理员登录
void rootlogin(Myarg *node,clientlist *c)
{
if(0 == (searchtableroot(c->name)))
{
char buf[32] = "该用户非管理员";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
else if( 0 == (rootveridandpassword(c)))//密码错误
{
char buf[20] = "密码输入错误";
if(send(node->fd,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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
online_userlist *p = 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;
}
}
}
//判断管理员用户与密码
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;
}
}
//管理员重复登录判断
int rootRepeatlogin(Myarg *node,clientlist *c)
{
printf("正在判断是否重复登录\n");
char s1[32]={0};
strcpy(s1,c->name);//判断自己是否登录
printf("正在判断%s是否在线\n",c->to_name);
online_userlist *p = head;
while(p!=NULL)
{
if(strcmp(s1,p->name) == 0 )
{ printf("%s在线,重复登录\n",p->name);
char buf[32] = "您已在线,请勿重复登录";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
return 0;
}
p=p->next;
}
printf("未遍历到该用户,可以进行登录\n");
return 1;
}
//文件传输部分
//文件传输
void filetransfer(Myarg *node,clientlist *c)
{
MSG msg;
printf("正在运行文件传输函数\n");
int to_cfd = Find_person(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->fd,buf1,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
sleep(2);
}
else
{
while(flag2)
{
printf("正在向对方发送文件传输信号\n");
if(send(to_cfd,"文件存在进行文件传输",M,0) == -1)
{
ERRLOG("send error");
}
flag2 = 0;
}
if(c->num == 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));
}
return;
}
//管理员功能部分
//管理员踢人
void rootkick(Myarg *node,clientlist *c)
{
int to_cfd = Find_person(c); //在线返回对方的文件描述符
printf("踢出对象的文件描述符为%d\n",to_cfd);
if(0 == to_cfd)
{
char buf[32] = "对方不在线...";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
else
{
if(searchtableroot(c->to_name) == 1)
{
char buf[128] = "对方是管理员,无法进行操作";
if(send(node->fd,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");
}
}
}
}
//管理员禁言
void silent(Myarg *node,clientlist *c)
{
int to_cfd = Find_person(c); //在线返回对方的文件描述符
printf("禁言对象的文件描述符为%d\n",to_cfd);
if(0 == to_cfd)
{
char buf[32] = "对方不在线...";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
else
{
if(searchtableroot(c->to_name) == 1)
{
char buf[128] = "对方是管理员,无法进行操作";
if(send(node->fd,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");
}
}
}
}
//管理员解除禁言
void remsilent(Myarg *node,clientlist *c)
{
int to_cfd = Find_person(c); //在线返回对方的文件描述符
printf("解除禁言对象的文件描述符为%d\n",to_cfd);
if(0 == to_cfd)
{
char buf[32] = "对方不在线...";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
else
{
char buf[128] = "管理员已将你解除禁言";
if(send(to_cfd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}
}
//撤销管理员身份
void cancelroot(Myarg *node,clientlist *c)// 功能标志15 撤销管理员身份
{
char buf[128] = "撤销成功";
if(send(node->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
deleteroot(c);
exituser(c);
return;
}
//离线消息的保存与发送
//离线消息发送
void sendOfflinemessage(Myarg *node,clientlist *c)
{
if(searchtuser(c->name) == 1)
{
sqlite3 *db = NULL;
int ret = sqlite3_open("user.db",&db);
if(SQLITE_OK != ret)
{
perror("sqlite3_open");
exit(1);
}
char buf[128] = {0};
char sql[200] = "\0";
memset(buf,0,128);
memset(sql,0,200);
sprintf(sql,"select *from message;");
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)
{
printf("正在将离线消息发送给客户端\n");
sprintf(buf,"%s\n[%s对你说了一句]:%s",result[i*ncolumn + 3],result[i*ncolumn + 0],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->fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
}//0行就是第一行 第i行第j列
}
deleteumessage(c);
sqlite3_free_table(result);//释放数据库
ret = sqlite3_close(db);
if(SQLITE_OK != ret)
{
perror("sqlite3_close:");
exit(1);
}
}
else
{
return;
}
}
//在数据库中查找是否有该用户的离线消息 存在返回1 不存在返回0
int searchtuser(char *username)
{
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 message;");
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],username);
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;
}
}
//指定删除数据库里面的离线消息
void deleteumessage(clientlist *c)
{
char sql[200] = "\0";
memset(sql,0,sizeof(sql));//清空sql
sprintf(sql,"delete from message where toname = '%s';",c->name);
execstate(sql);
printf("删除数据库消息成功\n");
}
//创建表 注意更换数据库
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;
}
//异常掉线处理
void Offlineprocessing(int fd)
{
char buf2[20] = "\0";
online_userlist *p = head;
online_userlist *q;
online_userlist *p1 = head;
while(p->next!=NULL)
{
if(p->next->c_fd==fd)
{
strcpy(buf2,p->next->name);
q=p->next;
p->next=p->next->next;
free(q);
q=NULL;
printf("将%s踢出链表成功\n",buf2);
break;
}
p=p->next;
}
char buf[50] = "\0";
memset(buf,0,sizeof(buf));
sprintf(buf,"[%s]:退出了",buf2);
printf("退出信息拷贝确认");
p1=p1->next;
while(p1!=NULL)//如果用p->next!=NULL 当p在最后一个结点是 p->next=NULL 无法进入循环
{ printf("客户退出遍历链表测试\n");
if(send(p1->c_fd,buf,sizeof(buf),0) == -1)
{
ERRLOG("send error");
}
p1=p1->next;
}
}
server.h
#ifndef SERVER_H_
#define SERVER_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <sqlite3.h>
#define M 256
#define REGISTER 1//注册
#define LOGIN 2 //登录
#define PRIVATE 3 //私聊
#define CHPASSWORD 4 //修改密码
#define GROUPCHAT 5 //群聊
#define VIEWUSER 6 //查看在线用户
#define LOGUSER 7 //注销用户
#define CHECKRECORD 8 //查看聊天记录
#define FILETRAN 9 //文件传输
#define APPLYROOT 10 //管理员注册
#define ROOTLOGIN 11 //管理员登录
#define ROOTKICK 12 //管理员踢人
#define SILENT 13 //管理员禁言
#define REMSILENT 14 //解除禁言
#define CANCELROOT 15 //撤销管理员身份
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
//1通信协议
typedef struct client
{
char emojio[20]; //头像保存
char name[32]; //姓名
char msg[50]; //聊天内容
char password[32]; //密码
char to_name[50]; //聊天对象
char buf[256]; //文件内容
int num; //文件读取大小
int root; //管理员标志
int flag; //功能标志 注册标志1 登录2 私聊3 修改密码4 群聊界面5 查看在线用户6 注销用户7 查看聊天记录8 下载文件9
}clientlist;
//文件传输使用的结构体
typedef struct file
{
char text[M];
int num;
}MSG;
//在线用户链表
typedef struct online_user
{
char name[32];//姓名
int c_fd;
struct online_user*next; //指针域
}online_userlist;
//线程池
typedef struct Myarg
{
clientlist msg;
int fd;
}Myarg;
//任务队列
struct job{
void*(*func)(void *arg);
void *arg; //给回调函数传参使用
struct job *next;
};
struct pthreadpool//线程池的初始化
{
int m_threadNum; //已开启的线程的数量
pthread_t *m_pthreadIDs; //保存线程池中线程的ID
struct job *head; //任务队列的头
struct job *rear; //任务队列的尾
int m_QueueMaxNum; //任务队列的最大数
int m_QueueCurNum; //目前任务队列的任务数
pthread_mutex_t mutex;
pthread_cond_t m_QueueEmpty; //任务队列为空的条件
pthread_cond_t m_QueueNotEmpty; //任务队列不为空的条件
pthread_cond_t m_QueueNotFull; //任务队列为空的条件
};
//线程函数
void *work(void *arg);
//函数声明
void *ThreadRun(void *arg);
//初始化线程池
struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum);
//添加任务队列
void ThreadAddJob(struct pthreadpool *pool, void *(*func)(void *arg), void *arg);
//销毁线程池
void ThreadDestroy(struct pthreadpool *pool);
//初始化
int socket_init();
//epoll函数
int mypoll(int sockfd);
//在线用户链表
online_userlist* online_userlistcreate();
//注册
void Register(Myarg *node,clientlist *c);
//向数据库中插入注册数据
void insert_updata(Myarg *node,clientlist *c);
//在数据库中查询用户
int searchtable(char *tablename);
//执行sql语句函数
void execstate(char *sql);
//登录部分
void login(Myarg *node,clientlist *c);
//登录插入链表
void online_insertion(Myarg *node,clientlist *c);
//核验密码和账户是否正确
int veridandpassword(clientlist *p);
//重复登录判断
int Repeatlogin(Myarg *node,clientlist *c);
//判断除自己以外是否还有别人在线
int onlineempty1();
//判断链表是否为空
int onlineempty();
//私聊部分
void privatechat(Myarg *node,clientlist *c);
//判断是否在线 在线返回对方文件描述符 不在线返回0
int Find_person(clientlist *c);
//判断是否为敏感词 存在返回1 不存在返回0
int sensitiveword(char *sensitive);
//离线消息保存至表中
void saveOfflinemessage(clientlist *c);
//修改密码部分
void changepassword(Myarg *node,clientlist *c);
//根据名字修改密码
void Insert_updated_data(char *name,char *password);
//退出聊天室 删除链表内的用户结点
void exituser(clientlist *c);
//群聊部分
void groupchat(Myarg *node,clientlist *c);
//获取管理员头像
void getheadflag(clientlist *c);
//查看所有在线用户
void viewonline_user(Myarg *node,clientlist *c);
//注销用户
void loguser(Myarg *node,clientlist *c);
//掉线处理
void Offlineprocessing(int fd);
//将聊天记录保存
void insert_uprecord(clientlist *c);
//指定删除数据库的数据
void deleteuser(clientlist *c);
//指定删除数据库里面的root用户
void deleteroot(clientlist *c);
//查看聊天记录
void Checkrecord(Myarg *node);
//管理员用户的申请
void applyroot(Myarg *node,clientlist *c);
//在数据库中管理员表中查看是否存在该管理员
int searchtableroot(char *tablename);
//向root表中插入数据
void insert_updataroot(Myarg *node,clientlist *c);
//管理员登录
void rootlogin(Myarg *node,clientlist *c);
//判断管理员用户与密码
int rootveridandpassword(clientlist *c);
//管理员重复登录判断
int rootRepeatlogin(Myarg *node,clientlist *c);
//文件传输
void filetransfer(Myarg *node,clientlist *c);
//管理员踢人
void rootkick(Myarg *node,clientlist *c);
//管理员禁言
void silent(Myarg *node,clientlist *c);
//管理员解除禁言
void remsilent(Myarg *node,clientlist *c);
//撤销管理员身份
void cancelroot(Myarg *node,clientlist *c);
//离线消息发送
void sendOfflinemessage(Myarg *node,clientlist *c);
//在数据库中查找是否有该用户的离线消息 存在返回1 不存在返回0
int searchtuser(char *tablename);
//指定删除数据库里面的离线消息
void deleteumessage(clientlist *c);
//异常掉线处理
void Offlineprocessing(int fd);
//char *exit1user(int fd);
#endif
mian.c
#include "server.h"
online_userlist* head = NULL; //在线用户列表
struct pthreadpool *pool;
extern struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum);//初始化线程池
int main()
{
head = online_userlistcreate(); //创建在线用户链表
int sockfd = socket_init();
pool = InitPthreadPool(1, 10);//1个线程对10个任务
mypoll(sockfd);
return 0;
}
客户端
client.c
#include "clientaddr.h"
unsigned char ip_str[] = "192.168.91.151";
unsigned char port_str[] = "8787";
int flag1 = 0;
int flag2 = 1;
int flag3 = 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;//协议族 IPV4
clientaddr.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[1;43;33m作者:ZJN \033[0;46m |\033[0m\n");
printf("\033[0;46m| 版本:epoll+线程池 |\033[0m\n");
printf("\033[0;46m|-------------------------------------------------------------------|\033[0m\n");
printf("\033[0;46m| |\033[0m\n");
printf("\033[0;46m| \033[0;35m1.用户登录💩\033[0;46m |\033[0m\n");
printf("\033[0;46m| |\033[0m\n");
printf("\033[0;46m| \033[0;32m2.帐号注册👾\033[0;46m |\033[0m\n");
printf("\033[0;46m| |\033[0m\n");
printf("\033[0;46m| \033[0;33m3.管理员登录🐯\033[0;46m |\033[0m\n");
printf("\033[0;46m| |\033[0m\n");
printf("\033[0;46m| \033[0;31m 0:退出聊天室🐴\033[0;46m |\033[0m\n");
printf("\033[0;46m| |\033[0m\n");
printf("\033[0;46m*===================================================================*\033[0m\n");
printf("\n");
int num;
scanf("%d",&num);
getchar();
switch (num)
{
case 0:
exit_room();//退出聊天室
break;
case 1:
system("clear");
printf("\033[0;31m正在进行用户登录..\033[0m\n");
system("./zjn");
user_login();//登录
break;
case 2:
system("clear");
printf("\033[0;31m正在进行账号注册\033[0m\n");
user_register();//返回注册页面
selectfun();
break;
case 3:
system("clear");
printf("\033[0;31m正在进行管理员登录\033[0m\n");
rootlogin();
putchar(10);
break;
case 4:
system("clear");
printf("\033[0;31m正在退出聊天室\033[0m\n");
exit_room();
putchar(10);
break;
default:
system("clear");
printf("\033[0;31m对不起输入错误请从新输入..\033[0m\n");
selectfun();
return;
}
}
void funcinto()//第二阶段选择进入
{
system("clear");
printf("\033[34m \033[0;36m<1:私聊功能>🐱\033[0;34m \033[0m\n");
putchar(10);
printf("\033[34m \033[0;36m🐒<2:进入群聊>\033[0;34m \033[0m\n");
putchar(10);
printf("\033[34m \033[0;36m<3:修改密码>🐶\033[0;34m \033[0m\n");
putchar(10);
printf("\033[0;31m \033[0;36m🐩<4:查看当前在看用户>\033[0;34m \033[0m\n");
putchar(10);
printf("\033[34m \033[0;31m<5:注销用户>🦒\033[0;34m \033[0m\n");
putchar(10);
printf("\033[33m \033[0;33m🐴<6:申请称为管理员>\033[0;34m \033[0m\n");
putchar(10);
printf("\033[34m \033[0;33m🐆<0:退出聊天室>\033[0;34m \033[0m\n");
int num;
scanf("%d",&num);
getchar();
switch (num)
{
case 0:
printf("\033[0;31m正在退出聊天室..\033[0m\n");
exit_room();
putchar(10);
break;
case 1:
system("clear");
printf("\033[0;31m正在进入私聊功能..\033[0m\n");
privatechat();
funcinto();
break;
case 2:
system("clear");
printf("\033[0;31m正在入群聊功能\033[0m\n");
groupchat();
funcinto();
break;
case 3:
system("clear");
printf("\033[0;31m正在进行修改密码\033[0m\n");
changpassword();
selectfun();
putchar(10);
break;
case 4:
system("clear");
printf("\033[0;31m正在查看在线用户\033[0m\n");
viewonline_user();
sleep(3);
funcinto();
break;
case 5:
system("clear");
printf("\033[0;31m正在进行注销用户\033[0m\n");
deleteuser();
sleep(1);
selectfun();
putchar(10);
break;
case 6:
system("clear");
printf("\033[0;31m正在申请成为管理员\033[0m\n");
applyroot();
putchar(10);
break;
default:
system("clear");
printf("\033[0;31m对不起输入错误请从新输入..\033[0m\n");
funcinto();
return;
}
}
void user_register()//功能标志1 用户注册
{
ssize_t ret;
char password[50];
system("clear");
printf("\033[0;31m请输入你的名字..\033[0m\n");
scanf("%s",ts.name);
system("clear");
printf("\033[0;31m请输入你的密码\033[0m\n");
system("stty -echo");
scanf("%s",password);
system("stty echo");
system("clear");
printf("\033[0;31m请再次确认你的密码\033[0m\n");
system("stty -echo");
scanf("%s",ts.password);
system("stty echo");
if(strcmp(password,ts.password) == 0)
{
system("./zjn");
ts.flag = 1;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
return;
}
else
{
printf("\033[0;31m输入密码不一致请从新输入...\033[0m\n");
sleep(1);
user_register(ts);
}
}
void user_login()//功能标志 2用户登录
{
ssize_t ret;
system("clear");
printf("\033[0;31m请输入你的名字..\033[0m\n");
scanf("%s",ts.name);
system("clear");
printf("\033[0;31m请输入你的密码\033[0m\n");
system("stty -echo");
scanf("%s",ts.password);
sleep(1);
system("stty echo");
ts.flag = 2; //用户登录标志位
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
sleep(1);
if(1==flag1)
{
flag1 = 0;
selectfun();
}
if(0 == flag1)
{
funcinto();
}
return;
}
void privatechat()// 功能标志3 用户私聊
{
ssize_t ret;
char to_name[50];
system("clear");
printf("\033[0;31m请输入你想私聊的对象..\033[0m\n");
scanf("%s",ts.to_name);
sleep(1);
system("clear");
ts.flag = 3; //私聊标志为3
char buf[200] = "\0";
char *buf2;
system("clear");
printf("\033[0;31m请输入聊天内容..\033[0m\n"); //循环聊天记得加循环
printf("\033[0;31m <输入Q!退出私聊>\033[0m\n");
printf("\033[0;31m <输入W!查看快捷短语>\033[0m\n");
printf("\033[0;31m [当前用户]:%s\033[0m\n",ts.name);
while(1)
{
scanf("%s",buf);
if(strcmp(buf,"Q!") == 0)
{
system("clear");
return;
}
if(strcmp(buf,"W!") == 0)
{
phrase();
continue;
}
if(flag2)
{
if((buf2 = selectphrase(buf)) == NULL)
{
strcpy(ts.msg,buf);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
else
{
strcpy(ts.msg,buf2);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
}
}
return;
}
void changpassword()//功能标志4 修改密码
{
ssize_t ret;
char newpassword[50];
char password[50];
system("clear");
printf("\033[0;31m请输入你的新密码\033[0m\n");
system("stty -echo");
scanf("%s",newpassword);
system("stty echo");
system("clear");
printf("\033[0;31m请再次确认你的密码\033[0m\n");
system("stty -echo");
scanf("%s",password);
system("stty echo");
if(strcmp(password,newpassword) == 0)
{ strcpy(ts.msg,newpassword);
system("./zjn");
ts.flag = 4;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
return;
}
else
{
printf("\033[0;31m输入密码不一致请从新输入...\033[0m\n");
sleep(1);
changpassword();
}
}
void groupchat() //功能标志5 群聊 显示群聊界面
{
int num = 0;
ssize_t ret;
system("./zjn");
ts.flag = 5; //私聊标志为5
char buf[200] = "\0";
char *buf2;
system("clear");
printf("\033[0;31m请输入聊天内容..\033[0m\n"); //循环聊天记得加循环
printf("\033[0;31m <输入Q!退出群聊>\033[0m\n");
printf("\033[0;36m <输入W!查看快捷短语>\033[0m\n");
printf("\033[0;36m <输入C!查看聊天记录>\033[0m\n");
printf("\033[0;36m <输入T!进行文件传输>\033[0m\n");
printf("\033[0;31m [当前用户]:%s\033[0m\n",ts.name);
while(1)
{
scanf("%s",buf);
if(strcmp(buf,"Q!") == 0)
{
system("clear");
return;
}
if(strcmp(buf,"W!") == 0)
{
phrase();
printf("\033[0;32m请输入1继续群聊..\033[0m\n");
scanf("%d",&num);
if(1 == num)
{
break;
}
else
{
printf("\033[0;32m输入错误\033[0m\n");
}
}
if(strcmp(buf,"C!") == 0)
{
checkrecord();
printf("\033[0;32m请输入1继续群聊..\033[0m\n");
scanf("%d",&num);
if(1 == num)
{
break;
}
else
{
printf("\033[0;32m输入错误\033[0m\n");
}
}
if(strcmp(buf,"T!") == 0)
{
filetransfer();
sleep(1);
break;
}
if(flag2)
{
if((buf2 = selectphrase(buf)) == NULL)
{
strcpy(ts.msg,buf);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
else
{
strcpy(ts.msg,buf2);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
}
}
groupchat();
}
void viewonline_user()//功能标志6 查看所有在线用户
{
ts.flag = 6;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
return;
}
void deleteuser() //功能标志 7 注销用户
{
char password[30] = {0};
char name[30] = {0};
printf("\033[0;31m 请认真确认你的用户信息\033[0m\n");
printf("\033[0;31m<输入姓名>\033[0m\n");
scanf("%s",name);
system("clear");
printf("\033[0;31m<输入密码>\033[0m\n");
scanf("%s",password);
int ret1,ret2;
ret1 = strcmp(name,ts.name);
ret2 = strcmp(password,ts.password);
if(0 == ret1 && 0 == ret2)
{
ts.flag = 7;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
}
void phrase()//快捷短语打印
{
printf("\033[34m \033[0;36m🐒<快捷短语大全>\033[0;34m \033[0m\n");
putchar(10);
printf("\033[34m \033[0;36m1.<听说jsetc给分配对象?😍>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m2.<😅>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m3.<😘>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m4.<🤨>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m5.<😁>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m6.<真男人从不食💩,只食💨>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m7.<👾>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m8.< 嘟嘟嘟!! 🚋 The car is coming>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m9.<就这么点👌?>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m10.< 来🍇🍈🍊🍋多吃点,别让人看扁 >\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m11.<顺便给你分配个👩🍳>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m12.< Other people's money is my belongings😛 >\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m13.<Are you a dog?🐶>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m14.<你是个什么🐮🐎>\033[0;34m \033[0m\n");
printf("\033[34m \033[0;36m15.<一个巴掌拍不响但是加上一个🍑可以>\033[0;34m \033[0m\n");
}
char *selectphrase(char *num)//快捷短语选择
{
char *buf;
buf = (char *)malloc(32);
if(strcmp(num,"1.")==0)
{
strcpy(buf,"听说jsetc给分配对象?😍");
return buf;
}
if(strcmp(num,"2.")==0)
{
strcpy(buf,"😅");
return buf;
}
if(strcmp(num,"3.")==0)
{
strcpy(buf,"😘");
return buf;
}
if(strcmp(num,"4.")==0)
{
strcpy(buf,"🤨");
return buf;
}
if(strcmp(num,"5.")==0)
{
strcpy(buf,"😁");
return buf;
}
if(strcmp(num,"6.")==0)
{
strcpy(buf,"真男人从不食💩,只食💨");
return buf;
}
if(strcmp(num,"7.")==0)
{
strcpy(buf,"嘟嘟嘟!! 🚋 The car is coming");
return buf;
}
if(strcmp(num,"8.")==0)
{
strcpy(buf,"就这么点👌?");
return buf;
}
if(strcmp(num,"9.")==0)
{
strcpy(buf,"来🍇🍈🍊🍋多吃点,别让人看扁");
return buf;
}
if(strcmp(num,"10.")==0)
{
strcpy(buf,"顺便给你分配个👩🍳");
return buf;
}
if(strcmp(num,"11.")==0)
{
strcpy(buf,"Other people's money is my belongings😛");
return buf;
}
if(strcmp(num,"12.")==0)
{
strcpy(buf,"Are you a dog?🐶");
return buf;
}
if(strcmp(num,"13.")==0)
{
strcpy(buf,"你是个什么🐮🐎");
return buf;
}
if(strcmp(num,"14.")==0)
{
strcpy(buf,"一个巴掌拍不响但是加上一个🍑可以");
return buf;
}
if(strcmp(num,"15.")==0)
{
strcpy(buf,"👾");
return buf;
}
return NULL;
}
void checkrecord()//功能标志8 查看聊天记录
{
ts.flag = 8;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
void filetransfer()//功能标志9 文件传输
{
clientlist ts1;
strcpy(ts1.name,ts.name);
ts1.flag = 9;
char filename[128];
printf("\033[0;31m <请输入要传输的文件名>\033[0m\n");
scanf("%s",filename);
printf("\033[0;31m <请输入要传输的对象>\033[0m\n");
scanf("%s",ts1.to_name);
int bytes;
int fd;
if((fd = open(filename,O_RDONLY)) == -1)
{
perror("open error!\n");
printf("未找到此文件");
return;
}
printf("打开文件成功\n");
while(flag3)
{
memset(ts1.buf,0,sizeof(ts1.buf));
bytes = read(fd,ts1.buf,256);
if(bytes == -1)
{
perror("read");
return;
}
ts1.num = bytes;
sleep(1);
send(clientfd2,&ts1,sizeof(ts1),0);
if(bytes == 0)
{
strcpy(ts1.buf,"0");
send(clientfd2,&ts1,sizeof(ts1),0);
break;
}
}
flag3 = 1;
close(fd);
return;
}
void applyroot()//功能标志 10 管理员申请 包含头像的选择
{ int num;
putchar(10);
printf("\033[5;;35m尊敬的管理员请选择您的专属头像\033[0m\n");
putchar(10);
printf("\033[33m\033[0;33m 1.👽 2.👹 3.👻 4.🐉 5.😈 6.🐸 7.🐱 8.👺 \033[0;34m \033[0m\n");
putchar(10);
printf("\033[33m\033[0;33m 9.🐯 10.🐆 11.🐴 12.🐋 13.😹 14.🐮 15.🙀\033[0;34m \033[0m\n");
scanf("%d",&num);
if(1 == num)
{
strcpy(ts.emojio,"👽");
}
if(2 == num)
{
strcpy(ts.emojio,"👹");
}
if(3 == num)
{
strcpy(ts.emojio,"👻");
}
if(4 == num)
{
strcpy(ts.emojio,"🐉");
}
if(5 == num)
{
strcpy(ts.emojio,"😈");
}
if(6 == num)
{
strcpy(ts.emojio,"🐸");
}
if(7 == num)
{
strcpy(ts.emojio,"🐱");
}
if(8 == num)
{
strcpy(ts.emojio,"👺");
}
if(9 == num)
{
strcpy(ts.emojio,"🐯");
}
if(10 == num)
{
strcpy(ts.emojio,"🐆");
}
if(11 == num)
{
strcpy(ts.emojio,"🐴");
}
if(12 == num)
{
strcpy(ts.emojio,"🐋");
}
if(13 == num)
{
strcpy(ts.emojio,"😹");
}
if(14 == num)
{
strcpy(ts.emojio,"🐮");
}
if(15 == num)
{
strcpy(ts.emojio,"🙀");
}
ts.flag = 10;
ts.root = 1;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
sleep(1);
system("clear");
if(flag1 == 2)
{ flag1 = 0;
selectfun();
}
else
{
printf("\033[33m \033[0;33m申请管理员失败\033[0;34m \033[0m\n");
sleep(1);
//system("clear");
funcinto();
}
}
void rootlogin()//功能标志11 管理员登录 登录成功直接进入管理员聊天界面
{
ssize_t ret;
system("clear");
printf("\033[0;31m请输入你的名字..\033[0m\n");
scanf("%s",ts.name);
system("clear");
printf("\033[0;31m请输入你的密码\033[0m\n");
system("stty -echo");
scanf("%s",ts.password);
sleep(1);
system("stty echo");
ts.flag = 11; //用户登录标志位
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
sleep(1);
if(3 == flag1)
{
flag1 = 0;
selectfun();
}
if(0 == flag1)
{
rootgroupchat();
}
return;
}
void rootgroupchat() //管理员群聊界面 显示群聊界面
{
usleep(500*1000);
int num = 0;
ts.root = 1;
ssize_t ret;
ts.flag = 5;
char buf[200] = "\0";
char *buf2;
system("clear");
printf("\033[0;31m请输入聊天内容..\033[0m\n"); //循环聊天记得加循环
printf("\033[0;31m <输入Q!退出聊天室>\033[0m\n");
printf("\033[0;36m <输入W!查看快捷短语>\033[0m\n");
printf("\033[0;36m <输入C!查看聊天记录>\033[0m\n");
printf("\033[0;36m <输入T!进行文件传输>\033[0m\n");
printf("\033[0;36m <输入L!查看当前在线人数>\033[0m\n");
printf("\033[0;36m <输入K!踢人>\033[0m\n");
printf("\033[0;36m <输入F!禁言>\033[0m\n");
printf("\033[0;36m <输入U!解除禁言>\033[0m\n");
printf("\033[0;36m <输入E!撤销管理员身份>\033[0m\n");
printf("\033[0;33m [管理员]😇:%s\033[0m\n",ts.name);
while(1)
{
scanf("%s",buf);
if(strcmp(buf,"Q!") == 0)//退出群聊
{
system("clear");
exit_room();
return;
}
if(strcmp(buf,"E!") == 0)//取消管理员身份
{
system("clear");
cancelroot();
return;
}
if(strcmp(buf,"W!") == 0)//查看快捷短语
{
phrase();
continue;
}
if(strcmp(buf,"C!") == 0)//查看聊天记录
{
checkrecord();
printf("\033[0;32m请输入1继续群聊..\033[0m\n");
scanf("%d",&num);
if(1 == num)
{
break;
}
else
{
printf("\033[0;32m输入错误\033[0m\n");
}
}
if(strcmp(buf,"T!") == 0)//文件传输
{
filetransfer();
break;
}
if(strcmp(buf,"L!") == 0)//查看在线用户
{
rootviewonline_user();
printf("\033[0;32m请输入1继续群聊..\033[0m\n");
scanf("%d",&num);
if(1 == num)
{
break;
}
else
{
printf("\033[0;32m输入错误\033[0m\n");
}
}
if(strcmp(buf,"K!") == 0)//踢人
{
rootkick();
break;
}
if(strcmp(buf,"F!") == 0)//禁言
{
silent();
break;
}
if(strcmp(buf,"U!") == 0)//解除禁言
{
remsilent();
break;
}
if((buf2 = selectphrase(buf)) == NULL)
{
strcpy(ts.msg,buf);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
continue;
}
else
{
strcpy(ts.msg,buf2);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
}
rootgroupchat();
}
void rootviewonline_user()//管理员标志6 查看所有在线用户
{
ts.flag = 6;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
ts.flag = 5;
return;
}
void rootkick()//功能标志12 踢出群聊
{
ts.root = 1;
ssize_t ret;
system("clear");
ts.flag = 12;
printf("\033[0;31m请输入你想踢出群聊的对象..\033[0m\n");
scanf("%s",ts.to_name);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
sleep(1);
rootgroupchat();
return;
}
void silent()//功能标志13 禁言
{
ts.root = 1;
ssize_t ret;
system("clear");
ts.flag = 13;
printf("\033[0;31m请输入你想禁言的对象..\033[0m\n");
scanf("%s",ts.to_name);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
sleep(1);
rootgroupchat();
return;
}
void remsilent()//功能标志14 解除禁言
{
ts.root = 1;
ssize_t ret;
system("clear");
ts.flag = 14;
printf("\033[0;31m请输入你想解除禁言的对象..\033[0m\n");
scanf("%s",ts.to_name);
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
rootgroupchat();
return;
}
void cancelroot()//功能标志15 取消管理员身份
{
char password[30] = {0};
char name[30] = {0};
printf("\033[0;31m 请认真确认你的用户信息\033[0m\n");
printf("\033[0;31m<输入姓名>\033[0m\n");
scanf("%s",name);
system("clear");
printf("\033[0;31m<输入密码>\033[0m\n");
scanf("%s",password);
int ret1,ret2;
ret1 = strcmp(name,ts.name);
ret2 = strcmp(password,ts.password);
if(0 == ret1 && 0 == ret2)
{
ts.flag = 15;
int ret;
ret =send(clientfd2,&ts,sizeof(clientlist),0);
if(ret == -1)
{
ERRLOG("send");
}
}
sleep(1);
selectfun();
return;
}
void *recv_thread(void *arg)//接受线程函数
{
// int ret = 1;
int fd;
MSG msg;
//int num;
char buf[256] = {0};
int length;
clientfd2 = *((int *)arg);
while(1)
{
memset(buf,0,sizeof(buf));
length = recv(clientfd2,buf,sizeof(buf),0);
if (length == 0)
{
printf("服务器宕机!!\n");
exit(1);
pthread_exit(NULL);
}
buf[length] = '\0';
printf("\033[5;;36m%s\033[0m\n",buf);
if(strcmp(buf,"该用户不存在,请注册...") == 0)
{
flag1 = 1;
}
if(strcmp(buf,"对方不在线...") == 0)
{
flag3 = 0;
}
if(strcmp(buf,"密码输入错误..") == 0)
{
flag1 = 1;
}
if(strcmp(buf,"您已在线,请勿重复登录") == 0)
{
flag1 = 1;
}
if(strcmp(buf,"管理员申请成功") == 0)
{
flag1 = 2;
}
if(strcmp(buf,"该用户非管理员") == 0)
{
flag1 = 3;
}
if(strcmp(buf,"密码输入错误") == 0)
{
flag1 = 3;
}
if(strcmp(buf,"管理员已将你踢出聊天室") == 0)
{
exit(1);
}
if(strcmp(buf,"管理员已将你禁言") == 0)
{
flag2 = 0;
}
if(strcmp(buf,"管理员已将你解除禁言") == 0)
{
flag2 = 1;
}
if(strcmp(buf,"文件存在进行文件传输") == 0)
{
printf("正在接受文件\n");
if((fd = open("1.txt",O_WRONLY | O_CREAT | O_TRUNC,0664)) == -1)
{
ERRLOG("open error");
}
int ret;
while((ret = recv(clientfd2,&msg,sizeof(msg),0)) != -1)
{
//printf("ret= %d\n",ret);
if(strcmp(msg.text,"0") == 0)
{
printf("\033[0;36m文件接受完成\033[0m\n");
//sleep(1);
close(fd);
break;
}
else
{
write(fd,msg.text,msg.num);
}
memset(&msg,0,sizeof(msg));
}
}
putchar(10);
}
//pthread_exit(NULL);
}
void *send_thread(void *arg)//发送线程函数
{
selectfun();
}
void exit_room()//退出聊天室
{
exit(0);
}
client.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>
#define N 256
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n",__FILE__,__func__,__LINE__);\
exit(1);\
}while(0)
int clientfd2;//客户端sockfd
typedef struct file //文件传输结构体
{
char text[N];
int num;
}MSG;
typedef struct client
{
char emojio[20]; //头像保存
char name[32]; //姓名
char msg[50]; //聊天内容
char password[32]; //密码
char to_name[50]; //聊天对象
char buf[256]; //文件内容
int num; //文件读取大小
int root; //管理员标志
int flag; //功能标志 注册标志1 登录2 私聊3 修改密码4 群聊界面5 查看在线用户6 注销用户7 查看聊天记录8 下载文件9
}clientlist;
clientlist ts;
void funcinto();//第二阶段选择进入
void recvmessage();//消息接受
void n_init();//初始化客户端
void selectfun();//进入目录选择功能
void user_login();//用户登录
void user_register();//用户注册
void privatechat();//私聊
void *recv_thread(void *arg);//客户端接受消息线程函数
void *send_thread(void *arg);//客户端发送消息函数
void changpassword(); //修改密码
void groupchat(); //群聊
void viewonline_user();//查看所有在线用户
void deleteuser();//注销用户
void phrase();//快捷短语
char *selectphrase(char *num);//快捷短语的选择
void checkrecord();//查看聊天记录
void filetransfer();//文件传输
void applyroot();//申请成为管理员
void rootlogin();//管理员登录
void rootgroupchat();//管理员群聊界面
void rootkick();//踢出群聊
void silent();//管理员禁言
void remsilent();//解除禁言
void rootviewonline_user();//管理员查看在线用户
void cancelroot();//取消管理员身份
void root_login();//管理员登录
void exit_room();//退出聊天室
main.c
#include "clientaddr.h"
int main(int argc, char const *argv[])
{
n_init();
int ret;
pthread_t tid_recv;
pthread_t tid_send;
ret = pthread_create(&tid_recv,NULL,(void *)recv_thread,(void *)&clientfd2);
ret = pthread_create(&tid_send,NULL,(void *)send_thread,(void *)&clientfd2);
pthread_join(tid_recv,NULL);
pthread_join(tid_send,NULL);
close(clientfd2);
return 0;
}