第一部分:总览
项目名称:菜鸡聊天室
操作系统:linux 软件:vscode 实现方式:tcp+多线程 数据存储方式:sqlite3数据库
这个聊天室项目的第一版本,基本实现了同时进行多个客户端的注册、登录、会员申请、禁言等基本功能,具体功能如下图:
图(一)
在这个版本的聊天室中,主要分为服务器和客户端两个部分,服务器中主要负责接收客户端传过来的结构体,再将操作之后的结构体传回给对应的客户端。客户端则主要负责发送信息,并且做出对应的判断和处理操作。
下面是实现效果图(分别是会员登录和普通用户登录)
左边是服务器右边是客户端
下面是代码部分:
结构体:
#pragma once//为了避免同一个头文件被包含(include)多次
struct message
{
int action;//标志位
int vip;//是否为vip
int flags;//标志位
int talk;//是否可以说话
char name[20];//姓名
char passwd[20];//密码
char friend[20];//好友姓名
char msg[100];//消息内容
char io_name[20];//文件名
};
typedef struct message Msg;
服务器端:
main.c 文件
主要负责服务器tcp的链接,创建一个线程函数负责进行之后对传过来的结构体进行处理
#include"serever.h"
#include"Msg.h"
int c1=0;
int c2=0;
int c3=0;
int c4=0;
sqlite3 *ppdb;
sqlite3 *ppdb2;
long sockfd;
FILE *fp;
int main(int argc, char const *argv[])
{
signal(SIGINT,serever_exit);
createsqlite();
createsqlite_chatting_records();
socklen_t c_size;
long cfd;
int r_n;
int w_n;
pthread_t id;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if((sockfd=socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0))<0)
{
perror("socket");
exit(1);
}
printf("socket success!\n");
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(8888);//端口号
//s_addr.sin_addr.s_addr=inet_addr("192.168.193.129");
s_addr.sin_addr.s_addr=htons(INADDR_ANY);//绑定任意网卡
int opt=1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置套接字可以重复使用套接字
if(bind(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))!=0)
{
perror("bind");
exit(1);
}
printf("bind success!\n");
if(listen(sockfd,20)!=0)
{
perror("listen");
exit(1);
}
printf("listen success!\n");
while(1)
{
c_size =sizeof(struct sockaddr_in);
cfd=accept(sockfd,(struct sockaddr*)&c_addr,&c_size);
if(cfd<0)
{
if(errno!=EAGAIN&&errno !=EWOULDBLOCK&&errno!=EINTR)//保存系统调用产生的错误值
{
perror("accept");
exit(1);
}
continue;
}
printf("info client:ip=%s,port=%d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
if(pthread_create(&id,NULL,thread_read,(void*)cfd)<0)//创建线程
{
perror("thread error");
exit(1);
}
}
return 0;
}
以下是头文件
#ifndef _client_H_
#define _client_H_
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/un.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>
#include<unistd.h>
#include <sqlite3.h>
#include<pthread.h>
#include<sys/stat.h>
#include <fcntl.h>
pthread_mutex_t mutex ;
struct online
{
int cfd;
char name[20];
struct online *next;
};
void *insert_link(struct online*new_user);
int find_fd(char *friend);//私聊时查看好友是否在链表内(是否在线)
char*find_self(int cfd);//查看在链表内的自己的名称(是否在线)
int callback1(void *arg,int ncolumn,char**f_value,char**f_name);//检查姓名
int callback2(void *arg,int ncolumn,char**f_value,char**f_name);//检查密码
int callback3(void *arg,int ncolumn,char**f_value,char**f_name);//检查vip
int callback4(void *arg,int ncolumn,char**f_value,char**f_name);//检查是否可以说话
void* createsqlite();//创建用户信息数据库
void* createsqlite_chatting_records();//创建用户聊天记录数据库
void serever_exit(int sig);//退出信号
void *thread_read(void *arg);//服务器接受客户端信息
#endif
serever.c文件
********************************************(保密)
客户端:
main.c
负责tcp网络的链接,并且创建线程函数实现聊天室功能
#include"client.h"
#include"Msg.h"
int loged=1;
FILE *fp;
int main(int argc, char const *argv[])
{
if(argc!=3)
{
printf("please input serever ip and port!\n");
}
int r_n;
long sockfd;
struct sockaddr_in s_addr;
pthread_t id;
char cmd[10]={0};
char cmd2[10]={0};
fp=fopen("./chat_msg.txt","r");
if(NULL == fp)
{
perror("open");
return -1;
}
pthread_mutex_init(&mutex,NULL);
Msg *pmsg=(Msg*)malloc(sizeof(Msg));
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
exit(1);
}
//printf("socket success!\n");
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(atoi(argv[2]));//指定服务器端口号
s_addr.sin_addr.s_addr=inet_addr(argv[1]);//指定服务器ip地址
if(connect(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr))!=0)
{
perror("connect");
exit(1);
}
//printf("connect success!\n");
//system("clear");
pthread_create(&id,NULL,thread_read,(void*)sockfd);//父线程
printf("欢迎来到菜鸡聊天室\n");
while(1)
{
//main_menu(cmd);
sleep(2);
system("clear");
printf("---------------------------------------------------------\n\n");
printf("\t 选项:(请输入文字前面的选项)\n\n");
printf("\t 1.注册(reg):\n\n");
printf("\t 2.登陆(log):\n\n");
printf("\t 3.退出(exit):\n\n");
printf("---------------------------------------------------------\n\n");
printf("请输入选项\n");
scanf("%s",cmd);
system("clear");
if(strcmp(cmd,"1")==0)//注册
{
client_reg(pmsg, sockfd);
}
if(strcmp(cmd,"2")==0)//登陆
{
client_log(pmsg,sockfd);
while(1)
{
if(loged == 2)//普通用户登陆之后的操作
{
//menu(cmd2);
sleep(2);
system("clear");
printf("******************************************\n");
printf("\t 选项:(请输入文字前面的选项)\n\n");
printf("\t 1.私聊(chat):\n\n");
printf("\t 2.群聊(allchat):\n\n");
printf("\t 3.申请成为会员(apply_vip):\n\n");
printf("\t 4.查看聊天记录(read):\n\n");
printf("\t 5.查看在线人数(look):\n\n");
printf("\t 6.返回注册界面(break):\n\n");
printf("******************************************\n\n");
scanf("%s",cmd2);
if(strcmp(cmd2,"1")==0)//私聊
{
client_chat(pmsg,sockfd);
}
if(strcmp(cmd2,"2")==0)//群聊
{
client_allchat(pmsg,sockfd);
}
if(strcmp(cmd2,"3")==0)//申请成为管理员
{
client_apply_vip(pmsg,sockfd);
}
if(strcmp(cmd2,"4")==0)//查看聊天记录
{
client_read(pmsg,sockfd);
}
if(strcmp(cmd2,"5")==0)//查看在线人数
{
client_look(pmsg,sockfd);
}
if(strcmp(cmd2,"6")==0)//退出
{
client_break(pmsg,sockfd);
break;
}
}
if(loged == 3)//vip用户登陆之后的操作
{
while(1)
{
//vipmenu(cmd2);
sleep(2);
system("clear");
printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
printf("\t $选项$:(请输入文字前面的选项)\n\n");
printf("\t $1.私聊$(chat):\n\n");
printf("\t $2.群聊$(allchat):\n\n");
printf("\t $3.禁言$(stop):\n\n");
printf("\t $4.解除禁言$(over_stop):\n\n");
printf("\t $5.踢人下线$(out):\n\n");
printf("\t $6.查看聊天记录(read):\n\n");
printf("\t $7.文件传输(io):\n\n");
printf("\t $8.查看在线人数(look):\n\n");
printf("\t $9.返回注册界面(break):\n\n");
printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
scanf("%s",cmd2);
if(strcmp(cmd2,"1")==0)//vip 功能私聊
{
client_vipchat(pmsg,sockfd);
}
if(strcmp(cmd2,"2")==0)//vip 功能群聊
{
client_vipallchat(pmsg,sockfd);
}
if(strcmp(cmd2,"3")==0)//vip 功能 禁言
{
client_stop(pmsg,sockfd);
}
if(strcmp(cmd2,"4")==0)//vip 功能 解除禁言
{
client_overstop(pmsg,sockfd);
}
if(strcmp(cmd2,"5")==0)//vip 功能 踢人下线
{
client_out(pmsg,sockfd);
}
if(strcmp(cmd2,"6")==0)//vip 功能 查看聊天记录
{
client_vipread(pmsg,sockfd);
}
if(strcmp(cmd2,"7")==0)//文件传输
{
printf("功能正在完善,请等待下次更新...");
//client_vip_io(pmsg,sockfd);
}
if(strcmp(cmd2,"8")==0)//查看在线人数
{
client_look(pmsg,sockfd);
}
if(strcmp(cmd2,"9")==0)//返回上一级界面
{
client_break(pmsg,sockfd);
break;
}
}
}
if(loged == 0)
{
loged = 1;
break;
}
}
}
if(strcmp(cmd,"3")==0)//退出系统
{
client_exit();
}
}
if(fclose(fp) == EOF)
{
printf("关闭失败\n");
return -1;
}
return 0;
}
client.h 文件
#ifndef _client_H_
#define _client_H_
#include "Msg.h"
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/un.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
pthread_mutex_t mutex ;
void *thread_read(void *);//子线程读
void client_reg(Msg *pmsg, long sockfd);//注册
void client_log(Msg*pmsg,long sockfd);//登陆
void client_chat(Msg*pmsg,long sockfd);//私聊
void client_allchat(Msg*pmsg,long sockfd);//群聊
void client_apply_vip(Msg*pmsg,long sockfd);//申请成为管理员
void client_read(Msg*pmsg,long sockfd);//查找聊天记录
void client_break(Msg*pmsg,long sockfd);//退出
void client_vipchat(Msg*pmsg,long sockfd);//vip 功能私聊
void client_vipallchat(Msg*pmsg,long sockfd);//vip 功能群聊
void client_stop(Msg*pmsg,long sockfd);//vip 功能 禁言
void client_overstop(Msg*pmsg,long sockfd);//vip 功能 解除禁言
void client_out(Msg*pmsg,long sockfd);//vip 功能 踢人下线
void client_vipread(Msg*pmsg,long sockfd);//vip 功能 查找聊天记录
//void client_vip_io(Msg*pmsg,long sockfd);//vip 功能 文件传输
void client_look(Msg*pmsg,long sockfd);//查看在线人数
void client_exit();//退出
// void main_menu(int cmd);//主菜单
// void menu(int cmd2);//普通用户菜单
// void vipmenu(int cmd2);//vip用户菜单
#endif
client.c文件
********************************************(保密)