一、项目介绍
实现功能:
用户注册、登录、悄悄话、群聊、私聊、查看在线用户、超级用户权限、踢人、禁言、 解禁、文件传输、查看聊天记录、忘记密码、注销用户
1、该项目采用C/S架构,通过线程池实现了服务器与多个客户端之间的通信
2、采用数据库Sqlite3在服务器端存储用户注册的昵称、姓名、密码、超级用户标志位
3、登录采用用户在线链表进行统一管理
4、创建一个数据库专门保存用户之间的聊天记录,分别存储发送方、接收方、聊天信息、聊天日期
5、超级用户独享踢人权限(只可将普通用户踢下线),禁言以及解禁普通用户的权限,
6、普通用户也可以申请成为超级用户,用户之间也可以进行文件的传输
7、线程池:优化服务器端、控制用户最大数量、防止造成例如请求过多而产生的崩溃现象
二、使用须知
注册用户为普通用户,可手动添加为管理员,但是必须在服务器同意或者拒绝
若被禁言,则为永久禁言,知道管理员解除禁言,或者重启服务器
若被管理员强制下线,退出聊天框重新进入即可
进行聊天时可以选择常用语或者手动输入
三、效果展示
聊天室功能演示
四、代码展示
服务器代码
server.h
#ifndef _SERVER_H_
#define _SERVER_H_
#include<sqlite3.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<time.h>
#include<signal.h>
#include<arpa/inet.h>
enum
{
REG, //注册
LOGIN, //登录
FORGET, //忘记密码
LOOKUSERS, //查看在线用户
PRIVATE, //私聊
GROUP, //群聊
ANONPRIVATE,//私聊(匿名聊天)
ANONGROUP, //群聊(匿名聊天)
EXCUSE, //禁言
ADMINISTRATOR, //申请管理员
OUTADMINISTRATOR, //取消管理员
WORLD, //解除禁言
KICK, //踢人
KILLUSER, //注销账户
LOOKCHATRECORD, //聊天记录
//FILE //传输文件
};
//在线用户链表
typedef struct OnlineLinkList
{
char id[100];
char name[32];
int cfd;
int root;
int forbid_flag;
struct OnlineLinkList *next; //指针域,为了能够操作后面结点
//所以指针的类型为当前结构体的类型
}OnlineLinkList;
typedef struct thread_node
{
int cfd;
OnlineLinkList *head;
sqlite3 *ppdb;
}thread_node;
//保存信息结构体
typedef struct Message
{
char id[32]; //账号
char myid[32]; //用于保存自己的id
char name[32]; //昵称
char passwd[32]; //密码
char secret[32]; //密保
char cmd[32]; //聊天方式
int cfd; //聊天对象
char msg[128]; //聊天内容
int root; //管理员标志
char chat[1024]; //聊天记录
char buffer[1024];
}Message;
//创建一个空链表
void CreateLink(OnlineLinkList **head);
//创建新的结点
void CreateNode(OnlineLinkList ** new_node);
//头插法插入数据
void InsertNodeHead(OnlineLinkList *head, OnlineLinkList * new_node);
//遍历用户注册信息
void Id(sqlite3 * ppdb ) ;
//线程函数
void *MyFun(void *arg);
//查找id是否存在
int FindId(sqlite3 * ppdb , Message *data);
//注册
void Register(thread_node * node , Message *data);
//向数据库中插入数据
void InsertData(sqlite3 *ppdb , Message *data);
//创建表
void CreatTable(sqlite3 *ppdb);
//收发消息
void MsgSendRecv(thread_node * cfd_node);
//查找账号和密保是否存在
int FindSecret(sqlite3 * ppdb , Message *data);
//更新数据库(更改密码)
void UpdateData(sqlite3 *ppdb , Message *data);
//登录
void Login(thread_node *cfd_node , Message *data);
//登录验证
int VerifyIdPassword(sqlite3 *ppdb , Message *data);
//群聊
void GroupChat(sqlite3 *ppdb , OnlineLinkList *head , Message *data);
//私聊
void PrivateChat(thread_node *node , Message *data);
//群聊(匿名聊天)
void AnonGroupChat(OnlineLinkList *head , Message *data , sqlite3 *ppdb);
//私聊(匿名聊天)
void AnonPrivateChat(thread_node *node , Message *data);
//查看在线用户
int LookOnlineUsers(thread_node * node);
//检查自己是否在线
int InspectOwnOnline(thread_node *node);
//检查账号是否重复登录
int RepeatLogin(thread_node *node , Message *data);
//申请管理员身份
void DaministerUser(thread_node *node);
//撤销管理员身份
void OutDaministerUser(thread_node *node);
//踢人
void KickUser(thread_node *node , char *ID);
//禁言
int ForbidWorld(thread_node *cfd_node,char *ID);
//解禁
int CancelForbidWorld(thread_node *cfd_node,char *ID);
//注销用户
void CancelUser(thread_node * node,Message *data);
//删除注册信息
void DeleteData(sqlite3 *ppdb,Message *data);
//删除注销用户在链表内的信息
void KillLinkListUser(thread_node *node,char * ID);
//创建第二张表用于保存聊天记录
void CreatTable2(sqlite3 *ppdb);
//向第二张表中插入聊天记录
void InsertChatData(sqlite3 *ppdb , char *chat);
//遍历聊天记录
void PrintChatRecord(sqlite3 *ppdb , thread_node *node);
void FileRecv(thread_node *node,Message *data);
//*********************************************
// 定长线程池实现
struct job //存放线程函数,和传参
{
void *(*func)(void *arg); //函数指针
void *arg;
struct job *next;
};
struct threadpool
{
int thread_num; //已开启线程池已工作线程
pthread_t *pthread_ids; // 薄脆线程池中线程id
struct job *head;
struct job *tail; // 任务队列的尾
int queue_max_num; //任务队列的最多放多少个
int queue_cur_num; //任务队列已有多少个任务
pthread_mutex_t mutex;
pthread_cond_t queue_empty; //任务队列为空
pthread_cond_t queue_not_emtpy; //任务队列不为空
pthread_cond_t queue_not_full; //任务队列不为满
int pool_close; //线程退出标志
};
void * threadpool_function(void *arg);//任务队列取数据 执行线程函数
struct threadpool * threadpool_init(int thread_num, int queue_max_num);
void threadpool_add_job(struct threadpool *pool, void *(*func)(void *), void *arg);//增加任务
void thread_destroy(struct threadpool *pool);
#endif
server.c
#include "server.h"
//管理员身份标志 等于1的时候是管理员身份 等于0的时候不是管理员身份
int root;
//创建一个空链表(保存在线用户)
void CreateLink(OnlineLinkList **head)
{
CreateNode(head);
(*head)->next = NULL; //初始化头指针指向空
}
//创建新的结点
void CreateNode(OnlineLinkList ** new_node)
{
//定义新的结点,在堆区开辟空间
*new_node = (OnlineLinkList *)malloc(sizeof(OnlineLinkList));
//判断新结点开辟新空间是否成功
if(NULL == new_node)
{
printf("malloc error!\n"); //打印错误信息
exit(-1); //异常退出
}
}
//头插法插入数据
void InsertNodeHead(OnlineLinkList *head, OnlineLinkList * new_node)
{
//新的结点指向第一个结点的地址
new_node->next = head->next;
//头结点指向第一个结点
head->next = new_node;
}
//客户端下线将cfd从链表中删除
void DeleteNode(thread_node *node)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
int cfd;
head = node->head;
cfd = node->cfd;
p1 = head->next;
p2 = head;
if(p1 == NULL)
{
}
else
{
//cfd判断下线
while(p1 != NULL && p1->cfd != cfd)
{
p2 = p1;
p1 = p1->next;
}
if(p1 == NULL)
{
printf("no such Message!\n");
}
else
{
p2->next = p1->next;
free(p1);
}
}
}
//线程函数
void *MyFun(void *arg)
{
thread_node node;
node = *((struct thread_node *)arg);
while(1)
{
//收发消息
MsgSendRecv(&node);
}
//关闭当前通信接口
close(node.cfd);
return NULL;
}
//查找id是否存在
int FindId(sqlite3 * ppdb , Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
return 1;
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
if(strcmp(result[Index] , data->id) == 0)
{
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
if(flag == 1)
{
sqlite3_free_table(result);
return -1;
}
sqlite3_free_table(result);
}
//注册
void Register(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//注册前查找是否被注册
if(-1 == FindId(node->ppdb , data))
{
char arr[128] = {"账号已经存在,请重新注册"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
else
{
//像数据库中插入数据
InsertData(node->ppdb,data);
}
}
//向数据库中插入数据
void InsertData(sqlite3 *ppdb,Message *data)
{
char str[128];
char *sql = str;
char *errmsg = NULL;
sprintf(sql,"insert into mytable(id,name,passwd,secret) values('%s','%s','%s','%s');",
data->id,data->name,data->passwd,data->secret);
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
char arr[100] = {"账号注册成功"};
send(data->cfd,arr,strlen(arr),0);
}
//创建表(用于保存注册用户的信息)
void CreatTable(sqlite3 *ppdb)
{
//创建表
char sq1[128] = {0};
sprintf(sq1,"create table if not exists mytable(id char,name char,passwd char,secret char);");
int ret = sqlite3_exec(ppdb,sq1,NULL,NULL,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_exec: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
}
//忘记密码
void ForgetSecret(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//更改密码前查询账号和密保是否有误
if(-1 == FindSecret(node->ppdb , data))
{
//更新数据库
UpdateData(node->ppdb,data);
}
else
{
char arr[128] = {"账号或密保错误\n"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
}
//查找id和密保是否存在
int FindSecret(sqlite3 * ppdb , Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
return -1;
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
int ret1 = strcmp(result[Index] , data->id);
int ret2 = strcmp(result[Index+3] , data->secret);
if( ret1== 0 && ret2 == 0)
{
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
if(flag == 1)
{
sqlite3_free_table(result);
return -1;
}
sqlite3_free_table(result);
}
//更新数据库(更改密码)
void UpdateData(sqlite3 *ppdb,Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"update mytable set passwd='%s' where id= '%s' ;",data->passwd, data->id);
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
char arr[100] = {"密码更改成功"};
send(data->cfd,arr,strlen(arr),0);
}
//检查账号是否重复登录
int RepeatLogin(thread_node *node , Message *data)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p= NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
//该id不在线
return 0;
}
else
{
//已经登录
return -1;
}
}
//登录
void Login(thread_node *node , Message *data)
{
if(-1 == RepeatLogin(node , data))
{
char arr[128] = {"您已经在线,无需重复登录"};
send(node->cfd,arr,strlen(arr),0);
return;
}
OnlineLinkList *new_node;
OnlineLinkList *head = node->head;
//记录cfd 传回成功
data->cfd = node->cfd;
if(-1 != FindId(node->ppdb,data))
{
char arr[128] = {"该账号不存在,请重新登录"};
send(data->cfd,arr,strlen(arr),0);
return;
}
else
{
//登录前查找账号跟密码是否正确
int ret = VerifyIdPassword(node->ppdb, data);
if(ret == -1)
{
char arr[128] = {"登录成功"};
send(data->cfd,arr,strlen(arr),0);
//创建新的节点
CreateNode(&new_node);
/*
//根据id查找name
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(node->ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(node->ppdb));
return ;
}
int Index = column;
char aa[128];
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
if(strcmp(result[Index] , data->id) == 0)
{
strcpy(aa,result[Index + 1]);
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
*/
//把该账户的id复制的链表里
strcpy(new_node->id,data->id);
new_node->cfd = data->cfd;
new_node->root = 0;
new_node->forbid_flag = 0;
//头插法插入新的数据
InsertNodeHead(head,new_node);
}
else
{
char arr[128] = {"账号或密码错误"};
send(data->cfd,arr,strlen(arr),0);
}
}
}
//验证登录的账号和密码
int VerifyIdPassword(sqlite3 *ppdb , Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
return -1;
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
int ret1 = strcmp(result[Index] , data->id);
int ret2 = strcmp(result[Index + 2] , data->passwd);
if( ret1 == 0 && ret2 == 0 )
{
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
if(flag == 1)
{
sqlite3_free_table(result);
return -1;
}
sqlite3_free_table(result);
}
//收发消息
void MsgSendRecv(thread_node * node)
{
int ret;
Message RecvInfo;
ret = recv(node->cfd,&RecvInfo,sizeof(RecvInfo),0);
if(ret == 0)
{
DeleteNode(node);
pthread_exit(NULL);
}
else
{
//注册
if(strcmp(RecvInfo.cmd,"REG") == 0)
{
Register(node,&RecvInfo);
}
//忘记密码
else if(strcmp(RecvInfo.cmd,"FORGET") == 0)
{
ForgetSecret(node,&RecvInfo);
}
//登录
else if(strcmp(RecvInfo.cmd,"LOGIN") == 0)
{
Login(node,&RecvInfo);
}
//查看在线用户
else if(strcmp(RecvInfo.cmd,"LOOKUSERS") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能查看在线用户,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
LookOnlineUsers(node);
}
}
//群聊
else if(strcmp(RecvInfo.cmd,"GROUP") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能群发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
GroupChat(node->ppdb ,node->head,&RecvInfo);
}
}
//私聊
else if(strcmp(RecvInfo.cmd,"PRIVATE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能私发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
PrivateChat(node,&RecvInfo);
}
}
//群聊(匿名聊天)
else if(strcmp(RecvInfo.cmd,"ANONGROUP") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能群发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
AnonGroupChat(node->head,&RecvInfo,node->ppdb);
}
}
//私聊(匿名聊天)
else if(strcmp(RecvInfo.cmd,"ANONPRIVATE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能私发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
AnonPrivateChat(node,&RecvInfo);
}
}
//申请管理员
else if(strcmp(RecvInfo.cmd,"ADMINISTRATOR") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
int i;
printf("账号%s正在申请管理员身份,是否同意\n",RecvInfo.id);
printf("1: 是 2: 否\n");
scanf("%d",&i);
if(i == 1)
{
char arr[100] = {"管理员身份申请成功"};
send(node->cfd,arr,strlen(arr),0);
//将管理员标志置位1 cfd查找
DaministerUser(node);
}
if(i == 2)
{
char arr[100] = {"不同意管理员身份申请"};
send(node->cfd,arr,strlen(arr),0);
}
}
//撤销管理员身份
else if(strcmp(RecvInfo.cmd,"OUTADMINISTRATOR") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
int i;
printf("账号%s正在申请取消管理员身份,是否同意\n",RecvInfo.id);
printf("1: 是 2: 否\n");
scanf("%d",&i);
if(i == 1)
{
char arr[100] = {"撤销管理员身份成功"};
send(node->cfd,arr,strlen(arr),0);
//将管理员标志置位1 cfd查找
OutDaministerUser(node);
}
if(i == 2)
{
char arr[100] = {"不同意撤销管理员身份申请"};
send(node->cfd,arr,strlen(arr),0);
}
}
//踢人
else if (strcmp(RecvInfo.cmd,"KICK") == 0)
{
KickUser(node,RecvInfo.id);
}
//禁言
else if(strcmp(RecvInfo.cmd,"EXCUSE") == 0)
{
int ret = ForbidWorld(node,RecvInfo.id);
if(0 == ret)
{
char arr[100] = {"该id用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else if(-1 == ret)
{
usleep(1);
char arr[100] = {"禁言成功"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
char arr[100] = {"对方是管理员,不可禁言"};
send(node->cfd,arr,strlen(arr),0);
}
}
//解禁
else if(strcmp(RecvInfo.cmd,"WORLD") == 0)
{
int ret = CancelForbidWorld(node,RecvInfo.id);
if(0 == ret)
{
char arr[100] = {"该id用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else if( 1 == ret)
{
char arr[100] = {"解禁成功"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
char arr[100] = {"该用户不需要解禁"};
send(node->cfd,arr,strlen(arr),0);
}
}
//注销用户
else if(strcmp(RecvInfo.cmd,"KILLUSER") == 0)
{
CancelUser(node,&RecvInfo);
}
//查看聊天记录
else if(strcmp(RecvInfo.cmd,"LOOKCHATRECORD") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能查看在线用户,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
PrintChatRecord(node->ppdb , node);
}
//传输文件
else if(strcmp(RecvInfo.cmd,"FILE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能传输文件,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
FileRecv(node , &RecvInfo);
}
}
}
void FileRecv(thread_node *node,Message *data)
{
if (-1 == InspectOwnOnline(node))
{
char arr[100] = {"你未在线,不能发文件"};
send(node->cfd,arr,strlen(arr),0);
return ;
}
int len;
OnlineLinkList *p = NULL;
p = node->head->next;
//寻找在线用户链表中的cfd与私聊的cfd是否一致
while(p != NULL && strcmp(p->id, data->id) != 0)
{
//printf("****%s\n",p->id);
p = p->next;
}
//判断是否在线
if(p == NULL)
{
//printf("client is not online!\n");
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
printf("正在接收中......\n");
char arr[100] = {"AAAAA"};
send(p->cfd,arr,strlen(arr),0);
usleep(30);
//向客户端发送文件
send(p->cfd,data->buffer,strlen(data->buffer),0);
}
}
//遍历用户注册信息
void Id(sqlite3 * ppdb )
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
printf("%s = %s ",result[j] , result[Index]);
Index++;
}
putchar(10);
}
}
//群聊
void GroupChat(sqlite3 *ppdb , OnlineLinkList *head , Message *data)
{
int length;
OnlineLinkList *p = NULL;
p = head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
char chat[1000] = {0};
strcpy(chat ,data->name);
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(ppdb,chat);
while(p != NULL)
{
//发给每一个客户端
strcpy(aa,data->name);
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
p = p->next;
}
}
//私聊
void PrivateChat(thread_node *node , Message *data)
{
int length;
OnlineLinkList *p = NULL;
p = node->head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
//寻找在线用户链表中的cfd与私聊中的cfd是否一致
while(p != NULL && strcmp(p->id , data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
strcpy(aa,data->name);
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
char chat[1000] = {0};
strcpy(chat ,data->name);
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(node->ppdb,chat);
}
}
//群聊(匿名聊天)
void AnonGroupChat(OnlineLinkList *head , Message *data , sqlite3 *ppdb)
{
int length;
OnlineLinkList *p = NULL;
p = head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
char chat[1000] = {0};
strcpy(chat ,"匿名消息");
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(ppdb,chat);
while(p != NULL)
{
//发给每一个客户端
strcpy(aa,"匿名消息");
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
p = p->next;
}
}
//私聊(匿名聊天)
void AnonPrivateChat(thread_node *node , Message *data )
{
int length;
OnlineLinkList *p = NULL;
p = node->head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
//寻找在线用户链表中的cfd与私聊中的cfd是否一致
while(p != NULL && strcmp(p->id , data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
strcpy(aa,"匿名消息");
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
char chat[1000] = {0};
strcpy(chat ,"匿名消息");
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(node->ppdb,chat);
}
}
//查看在线用户
int LookOnlineUsers(thread_node * node)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p = NULL;
p = head->next;
char bb[128];
if(p == NULL)
{
return 0; //当前无用户在线
}
while(p != NULL)
{
memset(bb,0,sizeof(bb));
strcpy(bb,p->id);
send(node->cfd,bb,strlen(bb),0);
p = p->next;
usleep(4);
}
return 1;
}
//检查自己是否在线
int InspectOwnOnline(thread_node *node)
{
OnlineLinkList *head= NULL;
head = node->head;
OnlineLinkList *p= NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return -1;
}
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
//自己不在线
return -1;
}
else
{
//自己已经登录
return 0;
}
}
//申请管理员身份
void DaministerUser(thread_node *node)
{
OnlineLinkList *p = NULL;
OnlineLinkList *head = NULL;
head = node->head;
p =head->next;
if(p == NULL)
{
return;
}
else
{
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
return;
}
else
{
p->root = 1;
return;
}
}
}
//撤销管理员身份
void OutDaministerUser(thread_node *node)
{
OnlineLinkList *p = NULL;
OnlineLinkList *head = NULL;
head = node->head;
p =head->next;
if(p == NULL)
{
return;
}
else
{
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
return;
}
else
{
p->root = 0;
return;
}
}
}
//踢人
void KickUser(thread_node *node,char * ID)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
char *id = ID;
head = node->head;
p1 = head->next;
p2 = head;
if(p1 == NULL)
{
char arr[100] = {"该用户不在群组,踢人失败"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
//cfd判断下线
while(p1 != NULL && strcmp(p1->id,id) != 0)
{
p2 = p1;
p1 = p1->next;
}
if(p1 == NULL)
{
char arr[100] = {"该用户不存在"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
if(p1->root != 1)
{
p2->next = p1->next;
//踢人
free(p1);
char arr[100] = {"对方已被强制下线"};
send(node->cfd,arr,strlen(arr),0);
memset(arr,0,sizeof(arr));
char *ar;
ar = "你已经被管理员强制下线";
send(p1->cfd,ar,strlen(ar),0);
}
else
{
char arr[100] = {"对方是管理员,无权强制对方下线"};
send(node->cfd,arr,strlen(arr),0);
}
}
}
}
//禁言
int ForbidWorld(thread_node *cfd_node,char *ID)
{
OnlineLinkList *head = NULL;
head = cfd_node->head;
OnlineLinkList *p = NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,ID) !=0 )
{
p = p->next;
}
if(p == NULL)
{
//该用户不在线
return 0;
}
else
{
if(p->root == 1)
{
//对方是管理员不可禁言
return 1;
}
else
{
p->root = 1;
char arr[100] = {"你已经被管理员禁言"};
send(p->cfd,arr,strlen(arr),0);
return -1;
}
}
}
//解禁
int CancelForbidWorld(thread_node *cfd_node,char *ID)
{
OnlineLinkList *head = NULL;
head = cfd_node->head;
OnlineLinkList *p = NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,ID) !=0 )
{
p = p->next;
}
if(p == NULL)
{
//该用户不在线
return 0;
}
else
{
char arr[100] = {"你已经被管理员解禁"};
p->forbid_flag = 0;
send(p->cfd,arr,strlen(arr),0);
usleep(1);
}
}
//注销用户
void CancelUser(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//注销账户前查询账号和密保是否有误
if(-1 == FindSecret(node->ppdb , data))
{
//删除注册信息
DeleteData(node->ppdb,data);
KillLinkListUser(node,data->id);
}
else
{
char arr[128] = {"账号或密保错误\n"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
}
//删除注册信息
void DeleteData(sqlite3 *ppdb,Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"delete from mytable where id = '%s';",data->id);
char **result;
int row,column;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
char arr[100] = {"注销成功"};
send(data->cfd,arr,strlen(arr),0);
}
//删除注销用户在链表内的信息
void KillLinkListUser(thread_node *node,char * ID)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
char *id = ID;
head = node->head;
p1 = head->next;
p2 = head;
if(p1 == NULL)
{
}
else
{
//cfd判断下线
while(p1 != NULL && strcmp(p1->id,id) != 0)
{
p2 = p1;
p1 = p1->next;
}
if(p1 == NULL)
{
char arr[100] = {"该用户不存在"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
p2->next = p1->next;
free(p1);
}
}
}
//创建第二张表用于保存聊天记录
void CreatTable2(sqlite3 *ppdb)
{
//创建表
char sql[128] = {0};
sprintf(sql , "create table if not exists chat(chat char);");
int ret = sqlite3_exec(ppdb,sql,NULL,NULL,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_exec: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
}
//向第二张表中插入聊天记录
void InsertChatData(sqlite3 *ppdb , char *chat)
{
char str[128];
char *sql = str;
char *errmsg = NULL;
sprintf(sql,"insert into chat(chat) values('%s');", chat);
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
sprintf(sql,"insert into chat(chat) values('%s');", "\n");
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
}
//遍历聊天记录
void PrintChatRecord(sqlite3 *ppdb , thread_node *node)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p = NULL;
p = head->next;
char sql[128] = {0};
//sprintf(sql , "selete *from mytable;");
sprintf(sql,"select *from chat");
char **result;
int row,column;
int ret = sqlite3_get_table(ppdb,sql,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
int Index = column;
char chat[1000] = {0};
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
strcpy(chat , result[Index]);
send(node->cfd , chat ,strlen(chat), 0);
Index++;
}
}
if(row == 0) //当聊天记录为0行的时候聊天记录为空
{
strcpy(chat , "当前还没有聊天记录");
send(node->cfd , chat ,strlen(chat), 0);
return;
}
else
{
return;
}
}
//***************************************************
//线程池
void * threadpool_function(void *arg) //任务队列取数据 执行任务
{
struct threadpool *pool = (struct threadpool *)arg;
struct job *pjob = NULL;
while (1)
{
//我访问时别人不能让问 访问任务队列10个一个一个来
pthread_mutex_lock(&(pool->mutex));
while(pool->queue_cur_num == 0)
{
//当目前任务队列没有任务 等待任务队列不为空的条件被置位(添加任务处成功过来唤醒)
pthread_cond_wait(&(pool->queue_not_emtpy), &(pool->mutex));
//线程要结束时 退出
if (pool->pool_close == 1)
{
pthread_exit(NULL);
}
}
pjob = pool->head; //将对头任务拿出去处理
pool->queue_cur_num--; //任务数量减一个
if (pool->queue_cur_num != pool->queue_max_num)
{
//如果任务不满 不满条件唤醒
pthread_cond_broadcast(&(pool->queue_not_full));
}
if (pool->queue_cur_num == 0)
{
//当前任务队列没有任务
pool->head = pool->tail = NULL;
//当任务队列为空时 唤醒空条件,去销毁线程池
pthread_cond_broadcast(&(pool->queue_empty));
}
else
{
pool->head = pjob->next; //处理完一个 队头向后移动一个
}
pthread_mutex_unlock(&(pool->mutex));
(*(pjob->func))(pjob->arg);//让线程执行任务队列里的任务
free(pjob);//执行完释放
pjob = NULL;
}
}
struct threadpool * threadpool_init(int thread_num, int queue_max_num)
{
struct threadpool *pool = (struct threadpool *)malloc(sizeof(struct threadpool));
// malloc
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool->pool_close = 0; //线程退出标志 0不退出
pool->head = NULL;
pool->tail = NULL;
pthread_mutex_init(&(pool->mutex), NULL);
pthread_cond_init(&(pool->queue_empty), NULL);
pthread_cond_init(&(pool->queue_not_emtpy), NULL);
pthread_cond_init(&(pool->queue_not_full), NULL);
pool->thread_num = thread_num;
pool->pthread_ids = (pthread_t *)malloc(sizeof(pthread_t) * thread_num);//乘 线程数量
// malloc
for (int i = 0; i < pool->thread_num; i++)
{
//创建线程
pthread_create(&pool->pthread_ids[i], NULL, (void *)threadpool_function, (void *)pool);
}
return pool;
}
void threadpool_add_job(struct threadpool *pool, void *(*func)(void *), void *arg)
{
pthread_mutex_lock(&(pool->mutex));
//队列满
while (pool->queue_cur_num == pool->queue_max_num)
{
//阻塞等待 不满条件发生
//队列任务满 不得添加
pthread_cond_wait(&pool->queue_not_full, &(pool->mutex));
}
//定义函数链表
struct job *pjob = (struct job *)malloc(sizeof(struct job));
//malloc
pjob->func = func;
pjob->arg = arg;
pjob->next = NULL;
// pjob->func(pjob->arg);
if (pool->head == NULL) //队列为空
{
pool->head = pool->tail = pjob; //队头队尾指向链表
//唤醒 告诉别人任务队列不为空
pthread_cond_broadcast(&(pool->queue_not_emtpy));
}
else //队尾向后移1个
{
pool->tail ->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num++; //队列任务加1
pthread_mutex_unlock(&(pool->mutex));
}
void thread_destroy(struct threadpool *pool)
{
pthread_mutex_lock(&(pool->mutex));
while (pool->queue_cur_num != 0)
{
//等任务队列为空 才能销毁 阻塞等待 空条件
pthread_cond_wait(&(pool->queue_empty),&(pool->mutex));
}
pthread_mutex_unlock(&(pool->mutex));
//为空 唤醒不满条件 看有没有阻塞的线程
pthread_cond_broadcast(&(pool->queue_not_full));
//pthread cond broadcast(&( pool->queue_not_empty));
//任务队列为空时 置为1 告诉其他线程要退出了
pool->pool_close = 1;
//回收线程资源
for (int i = 0; i < pool->thread_num; i++)
{
//每次都唤醒 不唤醒 阻塞无法执行 线程释放
pthread_cond_broadcast(&(pool->queue_not_emtpy));
// pthread_cancel(pool->pthread_ids[i]); //有系统调用,才能销毁掉;有bug
printf("thread exit!\n");
pthread_join(pool->pthread_ids[i], NULL);
}
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_emtpy));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthread_ids);
//再次,释放每个节点
struct job *temp;
while(pool->head != NULL)
{
temp = pool->head;
pool->head = temp->next;
free(temp);
}
free(pool);
printf("destroy finish!\n");
}
main.c
#include "server.h"
int main(int argc, char **argv)
{
printf("正在启动服务器\n");
sleep(1);
system("clear");
int sockfd;
int ret;
//打开或者创建数据库
sqlite3 *ppdb;
ret = sqlite3_open("stu.db",&ppdb);
if(ret != SQLITE_OK)
{
printf("sqlite3 open: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
printf("SOCKET3 INIT SUCCESS!\n");
//创建表(用于保存注册用户的信息)
CreatTable(ppdb);
//创建第二张表用于保存聊天记录
CreatTable2(ppdb);
//遍历用户注册信息
Id(ppdb);
OnlineLinkList *head; //id cfd next
OnlineLinkList *new_node;
thread_node node; //cfd head id pdb
CreateLink(&head);
//结构体指向链表
node.head = head;
//结构体指向数据库
node.ppdb = ppdb;
sockfd = socket(AF_INET,SOCK_STREAM,0); //IPV4 流式套接字 具体协议类型
if(-1 == sockfd)
{
perror("socket");
exit(-1);
}
printf("SOCKET INIT SUCCESS!\n");
int opt = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); //设置地址可以被重复绑定
struct sockaddr_in server_addr; //保存服务器信息
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET; // 地址族 IPV4
server_addr.sin_addr.s_addr = htons(INADDR_ANY); //ip地址
//server_addr.sin_addr.s_addr = inet_addr("192.168.12.131");
server_addr.sin_port = 8888; //网络字节序端口
//绑定信息
ret = bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(-1 == ret)
{
perror("bind");
exit(-1);
}
//设置监听序列
ret = listen(sockfd,10);
if(-1 == ret)
{
perror("listen");
return -1;
}
printf("等待客户端进行连接.........\n");
struct sockaddr_in Message_addr; //用于保存客户端信息
int length = sizeof(Message_addr);
//线程池初始化
struct threadpool *pool = threadpool_init(10, 100);
while(1)
{
//阻塞等待客服端连接
int ret = accept(sockfd,(struct sockaddr *)&Message_addr, &length);
if(-1 == ret)
{
perror("accept");
exit(-1);
}
printf("接收到客户端的连接 %d\n",ret);
node.cfd = ret; //cfd head id
//为每一个客户端创建新的线程
//pthread_t tid;
// //创建线程 将cfd与head传给线程函数
// int fd = pthread_create(&tid,NULL,(void *)MyFun,(void *)&node);
// if(fd != 0)
// {
// perror("pthread_create");
// return -1;
// }
//往线程池 任务队列 放任务
threadpool_add_job(pool, (void *)MyFun,(void *)&node);
}
close(sockfd);
thread_destroy(pool);
return 0;
}
makefile
run:server.c main.c
gcc *.c -o run -lsqlite3 -lpthread
客户端代码展示
client.h
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include <sqlite3.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <arpa/inet.h>
#include <time.h>
#include<signal.h>
int sockfd; //客户端套接字socket
enum
{
REG, //注册
LOGIN, //登录
FORGET, //忘记密码
LOOKUSERS, //查看在线用户
PRIVATE, //私聊
GROUP, //群聊
ANONPRIVATE,//私聊(匿名聊天)
ANONGROUP, //群聊(匿名聊天)
REPLY, //回复
EXCUSE, //禁言
ADMINISTRATOR, //申请管理员
OUTADMINISTRATOR, //取消管理员
WORLD, //解除禁言
KICK, //踢人
KILLUSER, //注销账户
//FILE, //传输文件
};
//保存信息结构体
typedef struct Message
{
char id[32]; //账号
char myid[32]; //用于保存自己的id
char name[32]; //昵称
char passwd[32]; //密码
char secret[32]; //密保
char cmd[32]; //聊天方式
int cfd; //聊天对象
char msg[128]; //聊天内容
int root; //管理员标志
char chat[1024]; //聊天记录
char buffer[1024];
}Message;
//聊天室功能能选择界面
void menu();
//常用语功能界面
void menu1();
//常用语
char *PhrasalVerbs(int *select);
//写线程
void *write_thread(void * arg);
//读线程
void *read_thread(void * arg);
//修改退出聊天室的方式
void Close(int signum);
void file_from(int sockfd);
void file_recv(char buffer[]);
#endif
client.c
#include "client.h"
//管理员身份标志 等于1的时候是管理员身份 等于0的时候不是管理员身份
int root = 0;
//禁言标志 等于1是被禁言 等于0的时候没有被禁言
int forbid_flag = 0;
//修改退出聊天室的方式
void Close(int signum)
{
printf("请正确退出聊天室\n");
}
//聊天室功能能选择界面
void menu()
{
printf("***********聊*************天*************室**************\n\n");
printf(" 1.登录 \n\n");
printf(" 2.注册 \n\n");
printf(" 3.匿名聊天 \n\n");
printf(" 4.私聊 \n\n");
printf(" 5.群聊 \n\n");
printf(" 6.踢人 \n\n");
printf(" 7.禁言 \n\n");
printf(" 8.解除禁言 \n\n");
printf(" 9.忘记密码 \n\n");
printf(" 10.查看在线用户 \n\n");
printf(" 11.请求管理员身份 \n\n");
printf(" 12.撤销管理员身份 \n\n");
printf(" 13.文件传输 \n\n");
printf(" 14.查看聊天记录 \n\n");
printf(" 15.注销账户 \n\n");
printf(" 16.退出聊天室 \n\n");
printf("*********************************************************\n");
}
//常用语功能界面
void menu1()
{
printf("*******常***********用************语*******\n\n");
printf(" 1.中国人不骗中国人 \n\n");
printf(" 2.花有重开日,人无再少年 \n\n");
printf(" 3.YYDS \n\n");
printf(" 4.大哥,别卷了 \n\n");
printf(" 5.我躺好了 \n\n");
printf(" 6.摆烂了 \n\n");
printf(" 7.干饭人 \n\n");
printf(" 8.哒咩 \n\n");
printf(" 9.芭比Q了 \n\n");
printf(" 10.人生无常,大肠包小肠 \n\n");
printf(" 11.gg \n\n");
printf(" 12.emo了 \n\n");
printf(" 13.破大防了,家银们 \n\n");
printf(" 14.杀伤力不大,侮辱性极强 \n\n");
printf(" 15.啊,对对对对 \n\n");
printf(" 16.牛蛙兄弟 \n\n");
printf(" 17.人家想要贴贴 \n\n");
printf(" 18.普信女 \n\n");
printf(" 19.你个老六 \n\n");
printf(" 20.这里有老六 \n\n");
printf(" 输入其他编号切换手动输入信息 \n\n");
printf("********************************************\n");
}
//常用语
char *PhrasalVerbs(int *select)
{
char *world;
world = (char *)malloc(32);
if(*select == 1)
{
strcpy(world,"中国人不骗中国人");
return world;
}
if(*select == 2)
{
strcpy(world,"花有重开日,人无再少年");
return world;
}
if(*select == 3)
{
strcpy(world,"YYDS");
return world;
}
if(*select == 4)
{
strcpy(world,"大哥,别卷了");
return world;
}
if(*select == 5)
{
strcpy(world,"我躺好了");
return world;
}
if(*select == 6)
{
strcpy(world,"摆烂了");
return world;
}
if(*select == 7)
{
strcpy(world,"干饭人");
return world;
}
if(*select == 8)
{
strcpy(world,"哒咩");
return world;
}
if(*select == 9)
{
strcpy(world,"芭比Q了");
return world;
}
if(*select == 10)
{
strcpy(world,"人生无常,大肠包小肠");
return world;
}
if(*select == 11)
{
strcpy(world,"gg");
return world;
}
if(*select == 12)
{
strcpy(world,"emo了");
return world;
}
if(*select == 13)
{
strcpy(world,"破大防了,家银们");
return world;
}
if(*select == 14)
{
strcpy(world,"杀伤力不大,侮辱性极强");
return world;
}
if(*select == 15)
{
strcpy(world,"啊,对对对对");
return world;
}
if(*select == 16)
{
strcpy(world,"牛蛙兄弟");
return world;
}
if(*select == 17)
{
strcpy(world,"人家想要贴贴");
return world;
}
if(*select == 18)
{
strcpy(world,"普信女");
return world;
}
if(*select == 19)
{
strcpy(world,"你个老六 ");
return world;
}
if(*select == 20)
{
strcpy(world,"这里有老六");
return world;
}
else
{
strcpy(world,"NO");
return world;
}
}
//读线程
void *read_thread(void * arg)
{
char receive[128];
int length;
struct Message node;
node = *((struct Message *)arg);
sockfd = node.cfd;
while(1)
{
memset(receive, 0, sizeof(receive));
length = recv(sockfd,receive,100,0);
if(length == 0)
{
pthread_exit(NULL);
}
receive[length] = '\0';
if(strcmp(receive,"管理员身份申请成功") == 0)
{
printf("%s\n",receive);
root = 1;
}
else if(strcmp(receive,"撤销管理员身份成功") == 0)
{
printf("%s\n",receive);
root = 0;
}
else if(strcmp(receive,"你已经被管理员禁言") == 0)
{
printf("%s\n",receive);
forbid_flag = 1;
}
else if(strcmp(receive,"你已经被管理员解禁") == 0)
{
printf("%s\n",receive);
forbid_flag = 0;
}
else if(strcmp("AAAAA",receive) == 0)
{
printf("接收文件中....\n");
char buffer[1024];
memset(buffer,0,sizeof(buffer));
int file_len = recv(sockfd,buffer,1024,0);
if(-1 == file_len)
{
perror("recv");
exit(-1);
}
printf("file_len = %d\n",file_len);
buffer[file_len] = '\0';
//printf("buffer = %s\n",buffer);
file_recv(buffer);
printf("接收文件成功\n");
}
else
{
printf("%s\n" , receive);
}
}
pthread_exit(NULL);
}
//写线程
void *write_thread(void * arg)
{
char sendline[128];
Message message;
int a; //判断是否注销账户
int a1; //判断是否匿名聊天方式
int a2; //判断是否使用常用语
int select;
sockfd = *((int *)arg);
while(1)
{
system("clear");
menu();
memset(&select,0,sizeof(int));
scanf("%d",&select);
getchar();
switch (select)
{
//登录
case 1: //登录
printf("账号:\n");
scanf("%s",message.id);
getchar();
strcpy(message.myid,message.id);
printf("密码:\n");
scanf("%s",message.passwd);
getchar();
strcpy(message.cmd,"LOGIN");
printf("正在登录,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//注册
case 2:
printf("id:\n");
scanf("%s",message.id);
getchar();
printf("昵称:\n");
scanf("%s",message.name);
getchar();
printf("密码:\n");
scanf("%s",message.passwd);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
strcpy(message.cmd,"REG");
printf("正在注册,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//匿名聊天
case 3:
if(forbid_flag == 0)
{
printf("请选择匿名聊天的聊天方式\n");
printf("1 : 匿名私聊 2 : 匿名群聊 \n");
scanf("%d", &a1);
getchar();
//匿名私聊
if(a1 == 1)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"ANONPRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if (a2 == 2)
{
printf("请输入消息:\n");
memset(sendline,0,sizeof(sendline));
fgets(sendline,128,stdin);
strcpy(message.cmd,"ANONPRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
//匿名群聊
if(a1 == 2)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"ANONGROUP");
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if(a2 == 2)
{
printf("请输入消息:\n");
memset(sendline, 0, sizeof(sendline));
fgets(sendline,100,stdin);
strcpy(message.cmd,"ANONGROUP");
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//私聊
case 4:
if(forbid_flag == 0)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号:");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"PRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.name,message.myid);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if(a2 == 2)
{
printf("请输入消息:\n");
memset(sendline,0,sizeof(sendline));
fgets(sendline,128,stdin);
strcpy(message.cmd,"PRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.name,message.myid);
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//群聊
case 5:
if(forbid_flag == 0)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"GROUP");
strcpy(message.name,message.myid);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if(a2 == 2)
{
printf("请输入消息:\n");
memset(sendline, 0, sizeof(sendline));
fgets(sendline,100,stdin);
strcpy(message.name,message.myid);
strcpy(message.cmd,"GROUP");
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//踢人
case 6:
if(root == 1)
{
printf("请输入你要强制下线用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"KICK");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//禁言
case 7:
if(root == 1)
{
//msg_text.admin_flag = admin_flag;
printf("需要禁言用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"EXCUSE");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//解除禁言
case 8:
if(root == 1)
{
printf("需要解禁言用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"WORLD");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//忘记密码
case 9:
printf("请输入id:\n");
scanf("%s",message.id);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
printf("请输入新密码:\n");
scanf("%s",message.passwd);
getchar();
strcpy(message.cmd,"FORGET");
printf("正在更改密码,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//查看在线人数
case 10:
strcpy(message.cmd,"LOOKUSERS");
send(sockfd,&message,sizeof(message),0);
sleep(1);
break;
//申请管理员身份
case 11:
if(root == 1)
{
printf("你已经是管理员\n");
}
else
{
strcpy(message.cmd,"ADMINISTRATOR");
send(sockfd,&message,sizeof(message),0);
printf("正在等待服务器响应...........\n");
}
sleep(2);
system("clear");
break;
//取消管理员身份
case 12:
if(root == 0)
{
printf("你还不是管理员\n");
}
else
{
strcpy(message.cmd,"OUTADMINISTRATOR");
send(sockfd,&message,sizeof(message),0);
printf("正在等待服务器响应...........\n");
}
sleep(1);
system("clear");
break;
//注销账户
case 15:
printf("你确定要注销账户吗?\n");
printf("1 : 确定 2 : 取消 \n");
scanf("%d", &a);
if(a == 1)
{
printf("请输入id:\n");
scanf("%s",message.id);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
strcpy(message.cmd,"KILLUSER");
printf("正在注销账户,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
}
else
{
break;
}
break;
//查看聊天记录
case 14:
strcpy(message.cmd,"LOOKCHATRECORD");
send(sockfd,&message,sizeof(message),0);
break;
//退出聊天室
case 16:
system("clear");
//sleep(1);
printf("退出成功!\n");
printf("欢迎下次使用!\n");
exit(-1);
break;
//文件传输
case 13:
file_from(sockfd);
break;
default:
printf("选择有误!请重新输入!\n");
break;
}
}
}
void file_from(int sockfd)
{
char sendline[100];
struct Message msg_text;
int from_fd;
int bytes_read;
char *from_ptr;
char filename_path[1024];
strcpy(msg_text.cmd,"FILE");
printf("请输入传输对象的id:\n");
scanf("%s",msg_text.id);
printf("请输入文件路径\n");
scanf("%s",filename_path);
if((from_fd = open(filename_path,O_RDONLY)) == -1)
{
perror("open error!\n");
printf("没发现此文件\n");
exit(1);
}
while(1)
{
//************
memset(msg_text.buffer,0,sizeof(msg_text.buffer));
//printf("buffer = %s\n",msg_text.buffer);
bytes_read = read(from_fd,msg_text.buffer,1024);
if((bytes_read == -1))
{
perror("read error!\n");
exit(1);
}
if(bytes_read == 0)
{
break;
}
send(sockfd,&msg_text,sizeof(msg_text),0);
sleep(3);
}
close(from_fd);
}
void file_recv(char buffer[])
{
int to_fd;
char filename_path[1024];
char * to_ptr;
int bytes_write;
if((to_fd = open("1.txt",O_APPEND | O_CREAT | O_WRONLY , 0655)) == -1)
{
perror("open error!\n");
exit(1);
}
to_ptr = buffer;
bytes_write = write(to_fd,to_ptr,strlen(buffer));
if((bytes_write == -1))
{
perror("write error!\n");
exit(1);
}
close(to_fd);
}
main.c
#include "client.h"
int main(int argc, char const *argv[])
{
int ret;
//注册信号函数
//进行信号捕捉,将SIGINT信号的处理方式改成自己的处理方式
//去执行我自己的功能
if(signal(SIGINT,Close) == SIG_ERR)
{
perror("signal");
return -1;
}
pthread_t tid_read;
pthread_t tid_write;
//创建套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
//向服务器发起连接
struct sockaddr_in server_addr; //保存服务器信息
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET; //地址族 ipv4
server_addr.sin_port = 8888; //网络字节序端口
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //ip地址
//server_addr.sin_addr.s_addr = inet_addr("192.168.124.131");
ret = connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(-1 == ret)
{
perror("connect");
return -1;
}
ret = pthread_create(&tid_read,NULL,(void *)read_thread,(void *)&sockfd);
ret = pthread_create(&tid_write,NULL,(void *)write_thread,(void *)&sockfd);
//阻塞等待回收指定线程
pthread_join(tid_read,NULL);
pthread_join(tid_write,NULL);
close(sockfd);
return 0;
}
makefile
run:client.c main.c
gcc *.c -o run -lpthread