聊天室主要实现以下功能,分为一级界面和二级界面,下面附有源代码
1.首先是服务器代码
server.h代码
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include<arpa/inet.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include<signal.h>
#include <netinet/in.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<signal.h>
#include<pthread.h>
#include<sys/time.h>
#include<sys/select.h>
#include<poll.h>
#include<sqlite3.h>
#define PORT 11111
#define MaxNum 1024 //最多允许1000客户端同时登录
#define SUCCESS 1
#define FALIURE -1
#define Client_Size 1024 //同时连接客户端的最大人数同时连接客户端的最大人数
struct message
{
char name[20]; //姓名
int id; //id
char password[20]; //密码
int age; //年龄
int cmd; //协议
char code[20]; //密保
char sex[10]; //性别
char message[200]; //要传的信息
char toname [20]; //要传的人的姓名
char filename[20]; //文件名字
char file_message[1024]; //文件内容
char online_name[1024]; //用户在线姓名
};
enum
{
REGISTER,//注册
LOGIN, //登录
LOGIN_REPEAT, //重复登录
PRIVATE, //私聊
GROUP, //群聊
REPLAY, //回复
REPEAT, //重复
NONENTITY, //用户不存在
SEND, //发送数据
FAIL, //发送数据失败
SILENT, //禁言
SILENT_FAILURE, //禁言失败
BAN, //你已被禁言标志
REMOVE_BAN, //禁言解除
ADMINISTER, //管理员
ADMINISTER_FAILURE, //管理员申请失败返回标志
VIP_REPEAT, //vip重复申请标识符
NORMAL_USER, //普通用户标识符
REMOVE, //解除禁言
REMOVE_FAILURE, //解除禁言失败
FORCE, //强制下线
FORCE_SUCCESS, //返回给客户端表示踢人成功
EXIT, //退出
FILE_SEND, //发送文件
VIEW, //聊天记录
ONLINE, // 查看在线用户
};
struct online
{
char name[20];
int cfd;
struct online*next;
};
/**
* @brief 初始化服务端套接字
*
* @param socket
* @return ** int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int Socket_Init(int* sockfd);
//void fds_init(struct pollfd*fds);//将文件描述符集全部初始化-1
/**
* @brief 创建一个数据库,并在数据库中创建一个表,用来保存用户信息
*
* @return int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int Sqlite_Init();
int Sqlite_Init2(); //用于保存用户之间的聊天记录
/**
* @brief 用户注册接收函数
*
* @return int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int client_register(int clientfd,struct message*user);
int my_callback(void *para,int columncount,char**columvalue,char**columname);//回调函数,用来判定用户是否已经存在
/**
* @brief 用户登录处理函数函数
*
* @return int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int client_login(int clientfd,struct message*user);
/**
* @brief 私聊函数
*
* @return int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int client_private(int clientfd,struct message*user); //私聊函数
/**
* @brief 私聊函数
*
* @return int
* 成功返回 SUCCESS;
* 失败返回 FAILURE;
*/
int client_group(int clientfd,struct message*user); //群聊函数
int Applying_Administrator(int clientfd,struct message*user);//管理员申请
int user_silent(int clientfd,struct message*user); //用户禁言函数
int Remove_banned(int clientfd,struct message*user);//解除禁言
int Forced_offline(int clientfd,struct message*user);//用户强制下线函数
int user_exit();//用户主动退出,并关闭客户端
int file_transfer(int clientfd,struct message*user);//文件传输
int Viewing_Chat_History(int clientfd,struct message*user); //查看聊天记录
int Viewing_Online_Users(int clientfd,struct message*user); //查看在线用户
main.c代码
#include"server.h"
struct message user;
extern sqlite3 *ppdb;
extern sqlite3 *db;
int read_ret;
int main(int argc, char const *argv[])
{
signal(SIGPIPE,SIG_IGN);//忽略SIGPIPE信号,防止服务器向客户端发送消息时,客户端已经关闭,导致服务器异常终止
//创建一个数据库用来存储用户名信息
if(Sqlite_Init()!=SUCCESS)
{
printf("Sqlite_Init is error\n");
exit(1);
}
else
{
printf("Sqlite_Init is success\n");
}
//创建一个数据库用于保存用户之间的聊天记录
if(Sqlite_Init2()!=SUCCESS)
{
printf("Sqlite_Init2 is error\n");
exit(1);
}
else
{
printf("Sqlite_Init2 is success\n");
}
//服务器架构选择poll+tcp
int sockfd;//创建服务器端套接字
int clientfd;//用于接受客户端文件描述符
struct pollfd fds[Client_Size];//同时连接客户端的最大人数
for(int i=0;i<Client_Size;i++)
{
fds[i].fd=-1;
}
//fds_init(fds);//全部初始化为-1
if(Socket_Init(&sockfd)==SUCCESS) //初始化套接字
{
printf("sockfet_init success\n");
}
else
{
printf("socket_init failure\n");
}
fds[0].fd=sockfd;
fds[0].events=POLLIN;
struct sockaddr_in client_addr;
while(1)
{
//开始监听服务器套接字,有客户端连接时,sockfd会发生变化
int Connect_Num=poll(fds,Client_Size,-1);
if(fds[0].events==fds[0].revents)
{
socklen_t addr_len=sizeof(struct sockaddr);
clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&addr_len);
//clientfd:连接描述符:每连接一个客户端成功,就会生成一个文件描述符,只要知道这个描述符,就可以与对应客户通信
if(clientfd<0)//防止没有客户端连接报错,非阻塞形式,连续看一下
{
//errno==EAGAIN,因为没有读到数据而报错
if(errno != EAGAIN && errno != EWOULDBLOCK && errno!=EINTR)
{
perror("accept error!");
exit(1);
}
continue;
}
printf("connect client:ip=%s port=%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
for(int i=0;i<Client_Size;i++)
{
if(fds[i].fd==-1)
{
fds[i].fd=clientfd;
fds[i].events=POLLIN;
break;
}
}
if(--Connect_Num<=0)
{
continue;
}
}
for(int i=1;i<Client_Size;i++)
{
if(fds[i].fd!=-1)
{
if(fds[i].events==fds[i].revents)
{
clientfd=fds[i].fd;
memset(&user,0,sizeof(user));
read_ret=recv(clientfd,&user,sizeof(user),0);
if(read_ret<0)
{
perror("read error!");
fds[i].fd=-1;
}
if(read_ret==0)
{
printf("client is close\n");
fds[i].fd=-1;
}
if(read_ret>0)
{
//printf("已成功收到数据\n");
switch (user.cmd)
{
case REGISTER:
read_ret=client_register(clientfd,&user);//执行注册函数
if(read_ret!=SUCCESS)
{
printf("注册失败\n");
}
else
{
printf("注册成功\n");
}
break;
case LOGIN:
read_ret=client_login(clientfd,&user);//执行登录函数
break;
case PRIVATE:
read_ret=client_private(clientfd,&user);//执行私聊函数
break;
case GROUP:
read_ret=client_group(clientfd,&user);//执行群聊函数
break;
case ADMINISTER:
Applying_Administrator(clientfd,&user);//管理员申请
break;
case SILENT:
user_silent(clientfd,&user);//用户禁言函数
break;
case REMOVE:
Remove_banned(clientfd,&user);//用户解除禁言函数
break;
case FORCE:
Forced_offline(clientfd,&user);//用户强制下线函数
break;
case FILE_SEND:
file_transfer(clientfd,&user);//文件发送函数
break;
case VIEW:
Viewing_Chat_History(clientfd,&user); //查看聊天记录
break;
case ONLINE:
Viewing_Online_Users(clientfd,&user); //查看在线用户
break;
case EXIT:
user_exit(clientfd,&user);//用户主动退出,并关闭客户端,删除链表中对应姓名
break;
default:
break;
}
}
if(--Connect_Num<=0)
{
break;
}
}
}
}
}
return 0;
}
server.c代码
#include"server.h"
sqlite3 *ppdb;
sqlite3 *db;
int flag=0;
struct online *head = NULL;
void* insert_Node(struct online* Node) //头插,将cfd和姓名插入
{
Node->next = head;
head=Node;
return head;
}
int Socket_Init(int* sockfd)
{
//socket:创建套接字,通过参数传递socket地址
//初始化结构体struct sockaddr_in
//bind:绑定IP地址和端口号
//调用listen,函数转换成被动监听描述符(等待连接)
*sockfd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0);
//将套接字设置成非阻塞
struct sockaddr_in server_addr;
server_addr.sin_family=AF_INET;//地址组协议,IPV4协议
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=inet_addr("192.168.152.130");
int opt = 1;
setsockopt(*sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置可重复绑定使用一个端口号
int ret=bind(*sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));//bind:绑定IP地址和端口号
if(ret<0)
{
perror("bind error!");
exit(1);
}
else
{
printf("bind is successfully\n");
}
//监听
if(listen(*sockfd,MaxNum)<0)
{
perror("listen is error!");
exit(1);
}
return SUCCESS;
}
int Sqlite_Init() //用于保存用户信息
{
//打开或者创建数据库
int ret=sqlite3_open("stu.db",&ppdb);
if(ret!=SQLITE_OK)
{
printf("sqlite3 open:%s\n",sqlite3_errmsg(ppdb));
return FALIURE;
}
//创建表
char sql[128]={0};
sprintf(sql,"create table if not exists stu(id int,name text,sex text,password text,code text,age integer,VIP integer,flag integer);");
ret=sqlite3_exec(ppdb,sql,NULL,NULL,NULL);
if(ret!=SQLITE_OK)
{
printf("sqlite3_exec2:%s\n",sqlite3_errmsg(ppdb));
return FALIURE;
}
return SUCCESS;
}
int Sqlite_Init2() //用于保存用户之间的聊天记录
{
//打开或者创建数据库
int ret=sqlite3_open("stu2.db",&db);
if(ret!=SQLITE_OK)
{
printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
return FALIURE;
}
//创建表
char sql[128]={0};
sprintf(sql,"create table if not exists stu2(datetime numeric,name text,toname text,message text);");
ret=sqlite3_exec(db,sql,NULL,NULL,NULL);
if(ret!=SQLITE_OK)
{
printf("sqlite3_exec2:%s\n",sqlite3_errmsg(ppdb));
return FALIURE;
}
return SUCCESS;
}
int my_callback(void *para,int columncount,char**columvalue,char**columname)//数据库查询的回调函数
{
flag=1;
return 0;
}
int client_register(int clientfd,struct message*user)
{
//首显是遍历数据库,查看数据库,用户名是否已经存在,如果不存在,插入用户数据
printf("已进入注册界面\n");
char*errmsg;
char sql[1024];
memset(sql,0,sizeof(sql));
sprintf(sql,"select *from