一.所需工具
1.vmwarestation虚拟机软件
2.ubuntu系统(64位)
3.vscode编译器(方便模块化编程)
4. mysql8.0,以及相关指令
1)安装数据库服务器:sudo apt-get install mysql-server
2)安装数据库客户端:sudo apt-get install mysql-client
3)安装数据库开发包:sudo apt-get install libmysqlclient-dev
5.TCP协议,以及其相关函数
ps :shell终端里ifconfig可以查看ip。
6.线程及其相关函数
7.多路IO复用及其相关函数(这里用的select)
8.相关功能
1)登录系统(注册)(登录)(注销) >>已完成
2)群聊 >>已完成
3)私聊(单向) >>已完成
4)显示昵称,时间 >>已完成
5)查看聊天内容(数据库) >>已完成
6)管理员系统(禁言,解禁,踢出群聊) >>已完成
7)文件上传 >>未完成(本人能力所限,正在研究)
本人刚学Linux系统编程,才疏学浅,次帖子做学习记录所用,代码结构也比较混乱,其中不乏有许多小bug,也望大家批评指教,
所用的ip是宏定义,只需在宏定义时把ip改成自己的即可,ip查看方法时在shell终端输入
ifconfig。
主函main.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <denglu.h>
#include "server.h"
int main()
{
server();//开启服务器
sleep(3);
return (0);
}
服务器端server.c
#include "server.h"
#define IP "192.168.134.10"
#define PORT 6667
#define PEOPLE 10//连接客户端的数量
extern struct infor
{ char id[20];//承接账号
char pw[20];//承接密码
char name[20];//承接昵称
int fd; //承接通信套节字
int index; //承接轮训下标,即count的值
int jinzhi;//承接禁止标识位
int onlin;
}zhanghao; //账号系统的结构体,存放id,pw,name,fd等信息
struct infor zhanghao_shuzu[PEOPLE];//制作结构体数组,用来存储登陆的在线人员信息
pthread_t pth_id;//线程的进程号
int ser_ac_fd[PEOPLE];//通信描述符
int count;//统计在线人数,count表示最后加入的人在数组的下标
struct timeval shijian;//轮询用的结构体,里面是时间
int maxfd;
fd_set backup;
struct sockaddr_in clien;//此结构体存放连接的客户端信息
void server(void)
{
//mysql数据库
MYSQL * my = mysql_init(NULL);//初始化MYSQL结构体
// MYSQL_RES * res;//存放执行结果函数的返回值
// MYSQL_ROW row;//返回结果集的下一行的返回值
int chat_ret;//mysql_real_query返回值
char chat_buf[100];//通信用的字符串
my = mysql_real_connect(my,"localhost","root",
"1","wechat",0,NULL,0);//连接MYSQL服务器,在终端执行可执行程序时需要加sudo
if(my==NULL)
printf("连接数据库失败!\n");
//TCP通信
int i,j,ret;//maxfd:最大描述符。i,j:循环。ret:承接返回值
int ser_fd,flag=1;//套节字相关变量
fd_set set;//多路io复用相关变量
char buf[100];//通信用的字符串
shijian.tv_usec=2;//轮询时间定为2秒;usec2毫秒
socklen_t len;
//TCP客户端
ser_fd = socket(AF_INET,SOCK_STREAM,0);//创建套节字
setsockopt(ser_fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));//重用本地地址和端口
struct sockaddr_in ser_addr;//此结构体存放服务器信息
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);
ser_addr.sin_addr.s_addr=inet_addr(IP);
bind(ser_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));//绑定服务器地址
listen(ser_fd,PEOPLE);//监听,设置服务器在同一时刻允许多少客户端连接
len=sizeof(clien);
FD_ZERO(&backup);//清空集合
FD_SET(ser_fd,&backup);//将连接套节字放到集合中
maxfd = ser_fd;//将连接套节字设置为初始最大描述符
while(1)
{
// printf("进入while循环\n");
set = backup;
ret = select(maxfd+1,&set,NULL,NULL,&shijian);//创建select项目/
// perror("select");
//printf("\n创建select项目\n");
//连接套节字变化
if(FD_ISSET(ser_fd,&set))//连接套节字发生变化,说明有客户端要连接,上accpet
{
ser_ac_fd [count]= accept(ser_fd,(struct sockaddr *)&clien,&len);//等待客户端连接,成功返回通信套节子,失败返回-1
//这个地方加线程,即在accept之后recv之前做线程,利用通信套节字来然让客户端进行账号系统的使用
pthread_create(&pth_id,NULL,dengluxiancheng,NULL);
// perror("pthread_create");
}
//通信套节字变化
for(i=0;i<count;i++)
{
//printf("进入通信套接字循环\n");
if(FD_ISSET(ser_ac_fd[i],&set))//判断通信套节字是否变化
{
printf("11111进入通信套接字循环11111\n");
memset(buf,0,sizeof(buf));
ret=recv(ser_ac_fd[i],buf,sizeof(buf),0);//recv返回值是接收到的字节,当客户端下线,recv不再阻塞,且返回0
if(ret==0)//客户端下线消息转发
sprintf(buf,"客户端:%s:%d,name=%s,id=%s下线",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port),
zhanghao_shuzu[i].name,zhanghao_shuzu[i].id);
if(zhanghao_shuzu[i].jinzhi==0)//等于0说明没有被禁言,把他的发的消息存入数据库
{
memset(chat_buf,0,sizeof(chat_buf));
sprintf(chat_buf,"insert into chat_date values('%s')",buf);//将mysql数据库指令写入
chat_ret= mysql_real_query(my,chat_buf,strlen(chat_buf));//指令执行
if(chat_ret!=0)
printf("查询发生错误!chat_ret==%d/n",chat_ret);//mysql不能使用perror
}
//转发客户端消息
for(j=0;j<count;j++)
{
//禁言
if(zhanghao_shuzu[i].jinzhi==1)//禁言标识符为1,说明要禁言
break;
///群聊
printf("接收到:buf=%s\n",buf);
if(j!=i)
send(ser_ac_fd[j],buf,strlen(buf),0);//给除了发消息的客户端外所有客户端发送消息,即群聊功
}
//客户端下线处理
if((ret==0))//清理下线的客户端
{
printf("开始清除1\n");
//1.从轮询集合中删除该通信符
FD_CLR(ser_ac_fd[i],&backup);
//2.将下线的客户端从数组里删除(覆盖)
for(j=i;j<count;j++)
ser_ac_fd[j]=ser_ac_fd[j+1];
//3.统计客户端-1,
count--;
//4.更新文件最大描述符
maxfd=ser_fd;
for (j=0;j<count;j++)
{
if(ser_ac_fd[j]>maxfd)
maxfd=ser_ac_fd[j];
}
//5.将对应的结构体数组删除(覆盖)
for(j=i;j<count;j++)
zhanghao_shuzu[j]=zhanghao_shuzu[j+1];
}
}
if(zhanghao_shuzu[i].onlin==0)//被管理员踢出去的成员信息的清理
{
printf("开始清除2\n");
//1.从轮询集合中删除该通信符
FD_CLR(ser_ac_fd[i],&backup);
//2.将下线的客户端从数组里删除(覆盖)
for(j=i;j<count;j++)
ser_ac_fd[j]=ser_ac_fd[j+1];
//3.统计客户端-1,
count--;
//4.更新文件最大描述符
maxfd=ser_fd;
for (j=0;j<count;j++)
{
if(ser_ac_fd[j]>maxfd)
maxfd=ser_ac_fd[j];
}
//5.将对应的结构体数组删除(覆盖)
for(j=i;j<count;j++)
zhanghao_shuzu[j]=zhanghao_shuzu[j+1];
}
}
}
return;
}
void * dengluxiancheng(void *arg)
{
char sel[2]={0};
char xuanze_buf[30];//进行群聊或私聊的选择,,传输名字
char private_chat[100],private_keep[80];//承接私聊的内容,keep用来续接
int k;
int fd = ser_ac_fd [count];//fd局部变量,承接通信套节字
pthread_t id = pthread_self();//获取当前线程号,结束时要结束该线程
int ret = denglu(fd);//跳转进登陆系统,把要进行账号操作的客户端的套节字传过去
perror("denglu");
//1代表登陆成功,2代表注册成功,3,注销成功,4,管理员登陆,5,退出登陆,0代表失败
//1.登陆成功
if(ret==1)
{
printf("*********************\n");
printf("* 登陆成功! *\n");
printf("*1.加入群聊 *\n");
printf("*2.进行私聊 *\n");
printf("*********************\n");
zhanghao_shuzu[count]=zhanghao;//将连接的客户端的id,pw,name给到对应的结构体里,
zhanghao_shuzu[count].fd=fd; //将连接的客户端的通信套节字描述符给到对应的结构体里,
zhanghao_shuzu[count].index=count;//将连接的客户端的通信套节字描是第几个给到对应的结构体里,
zhanghao_shuzu[count].onlin=1; //将在线标志符online赋值为1,表示在线;
printf("id=%s,pw=%s,name=%s,fd=%d,index=%d\n",zhanghao_shuzu[count].id,zhanghao_shuzu[count].pw,
zhanghao_shuzu[count].name,zhanghao_shuzu[count].fd,zhanghao_shuzu[count].index); //检查denglu是否能把数据传过来
memset(xuanze_buf,0,sizeof(xuanze_buf));
recv(fd,xuanze_buf,sizeof(xuanze_buf),0);//群聊选择1,私聊选择2; 🍤第一次接收消息
//1.加入群聊
if(strcmp(xuanze_buf,"1")==0)//直接结束线程,进入群聊
{
//将连接的新的通信套节字加入轮询
FD_SET(ser_ac_fd[count],&backup);//向集合中添加通信套节字
if(ser_ac_fd[count]>maxfd)//让maxfd一直都是最大描述符
maxfd = ser_ac_fd[count];
count++;
printf("count=%d\n",count);
printf("客户端上线!\n连接的客户端信息:%s:%d\n",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port));//显示客户端的ip和端口
pthread_cancel(id);//结束进入群聊的线程
}
//2.进行私聊,发送人数名单给客户端(由于本人技术问题无法建立双向私聊,这只是单向私聊消息)
else if(strcmp(xuanze_buf,"2")==0)
{
printf("id=%s,pw=%s,name=%s,fd=%d,index=%d\n",zhanghao_shuzu[count].id,zhanghao_shuzu[count].pw,
zhanghao_shuzu[count].name,zhanghao_shuzu[count].fd,zhanghao_shuzu[count].index); //检查denglu是否能把数据传过来
for(k=0;k<count;k++)
{
send(fd,zhanghao_shuzu[k].name,strlen(zhanghao_shuzu[k].name),0);//发送在线的客户端的通信套节字的描述符
printf("🍤zhanghao[%d].name=%s\n",k,zhanghao_shuzu[k].name);
usleep(10);
}
send(fd,"0",1,0);//发送0,以便让客户端知道发送结束 🍤第一次发送消息
printf("text2\n");
star:
memset(xuanze_buf,0,sizeof(xuanze_buf));
recv(fd,xuanze_buf,sizeof(xuanze_buf),0);// 🍤第二次接收消息(姓名)
printf("接收到的姓名是:%s...\n",xuanze_buf);
for(k=0;k<count;k++)//对比name
{
printf("遍历姓名:%s...\n",zhanghao_shuzu[k].name);
if(strcmp(xuanze_buf,zhanghao_shuzu[k].name)==0)//对比传输的名字,一样的话进入if语句
{
usleep(50);
send(fd,"私聊建立成功!",23,0);//发送在线的客户端的通信套节字的描述符 🍤第二次发送消息;
printf("第二次发送的信息是:私聊建立成功!");
//第k个结构体中的name是客户端想要私聊的用户
//将连接的新的通信套节字加入轮询
printf("(私聊!\n连接的客户端信息:%s:%d\n",inet_ntoa(clien.sin_addr),ntohs(clien.sin_port));//显示客户端的ip和端口
// k;接收端
//count;发送端
while(1)
{
memset(private_chat,0,sizeof(private_chat));
memset(private_keep,0,sizeof(private_keep));
int ret = recv(fd,private_keep,sizeof(private_keep),0); // 🍤第三次接收
sprintf(private_chat,"(私聊)");//在chat前面加上提示(私聊)
strcat(private_chat,private_keep);//把内容续接上
printf("接收到的消息是%s\n",private_keep);//接收的消息储存到keep里
send(zhanghao_shuzu[k].fd,private_chat,strlen(private_chat),0); //发送私聊信息给私聊对象
printf("发送的私聊信息是:%s\n",private_chat);
if(ret==0)
break;
}
printf("私聊结束1\n");
pthread_cancel(id);
printf("私聊结束2\n");
}
}
send(fd,"私聊建立失败!",sizeof(xuanze_buf),0);//发送在线的客户端的通信套节字的描述符
memset(xuanze_buf,0,sizeof(xuanze_buf));
recv(fd,xuanze_buf,sizeof(xuanze_buf),0); //🍤第三次接收
if(strcmp(xuanze_buf,"y")==0)
goto star;//重新输入
else
return 0;//退出登陆
}
}
//2.注册成功
else if(ret==2)
{
printf("注册成功!\n");
pthread_cancel(id);
}
//3.注销成功
else if (ret == 3)
{
printf("注销成功!\n");
pthread_cancel(id);
}
//4.管理员登陆
else if (ret==4)
{
printf("+---------------------------------------+\n");
printf("| 登陆系统 |\n");
printf("|1.禁言 |\n");//已完成
printf("|2.解禁 |\n");//已完成
printf("|3.提出群聊 |\n");//已完成
printf("+---------------------------------------+\n");//已完成
mingdan:
printf("**********count=%d\n\n",count);
for(k=0;k<count;k++)//发送在线名单
{
usleep(10);
send(fd,zhanghao_shuzu[k].name,strlen(zhanghao_shuzu[k].name),0);//发送在线的客户端的通信套节字的描述符
printf("🍤zhanghao[%d].name=%s\n",k,zhanghao_shuzu[k].name);
usleep(10);
}
send(fd,"0",1,0);//发送0,以便让客户端知道发送结束 🍌第一次发送消息
printf("text2\n");
memset(xuanze_buf,0,sizeof(xuanze_buf));
recv(fd,xuanze_buf,sizeof(xuanze_buf),0); // 🍌第二次接收消息(姓名)
printf("接收到的姓名是:%s...\n",xuanze_buf); //😡
printf("请选择您要对其进行的操作\n");
memset(sel,0,sizeof(sel));
recv(fd,sel,sizeof(sel),0); //🍌第三次接收
for(k=0;k<count;k++)//对比name
if(strcmp(xuanze_buf,zhanghao_shuzu[k].name)==0)//对比传输的名字,一样的话进入if语句,此时,k下标的结构体是要禁言的用户
break;
printf("要禁言的用户的fd是%d,id是:%s,name是%s\n",zhanghao_shuzu[k].fd,zhanghao_shuzu[k].id,zhanghao_shuzu[k].name);
if (strcmp(sel,"1")==0)//禁言
{ printf("禁言。。。。\n");
zhanghao_shuzu[k].jinzhi=1;
send(fd,"禁言成功!",20,0); //下面3个if是 🍌第二次发送消息
usleep(10);
goto mingdan;
}
else if (strcmp(sel,"2")==0)//解禁
{
zhanghao_shuzu[k].jinzhi=0;
send(fd,"解禁成功!",20,0);
usleep(10);
goto mingdan;
}
else if (strcmp(sel,"3")==0)//提出群聊
{
close(zhanghao_shuzu[k].fd);//关闭对应name的通信套节字
zhanghao_shuzu[k].onlin=0;//将online标识符设置为0,表示不再线
send(fd,"踢出群聊成功!",20,0);
usleep(10);
goto mingdan;
}
else if (strcmp(sel,"4")==0)//退出管理系统
{
return 0;
}
}
//5.查看聊天记录成功
else if (ret==4)
{
printf("查看聊天记录成功\n");
}
//6.退出登陆
else if (ret==6)
{
printf("退出登陆!\n");
pthread_cancel(id);
}
//0.失败
else if (ret == 0)
{
printf("操作失败!\n");
pthread_cancel(id);
}
return NULL;
}
服务器端包含的头文件server.h
#ifndef SERVER_H
#define SERVER_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <fcntl.h>
#include <pthread.h>
#include "denglu.h"
void * dengluxiancheng(void *);
void server(void);
#endif
账号系统denglu.c(在server.c里线程中被引用)
#include "denglu.h"
struct infor
{
char id[20];//承接账号
char pw[20];//承接密码
char name[20];//承接昵称
int fd; //承接通信套节字
int index; //承接轮训下标,即count的值
int jinzhi;//承接禁止标识位
}zhanghao;
char denglu_buf[100];
char sel[2];
char client_ret[2];
int denglu(int denglu_fd)//传参是通信套节字描述符,后期用来私聊
{
//账号系统
char command[100]={0};//承接mysql的命令
MYSQL * my = mysql_init(NULL);//初始化MYSQL结构体
MYSQL_RES * res;//存放执行结果函数的返回值
MYSQL_ROW row;//返回结果集的下一行的返回值
int chat_num;//承接结果值的行数
int ret;//ret是query返回值,choose是goto要用;
int num;
my = mysql_real_connect(my,"localhost","root",
"1","wechat",0,NULL,0);//连接MYSQL服务器,在终端执行可执行程序时需要加sudo
printf("my = %p\n",my);
star:
recv(denglu_fd,sel,sizeof(sel),0);//接收客户端发送的选择
// printf("账号4!\n");
//1.登陆
if(strcmp(sel,"1")==0)
{
denglu:
//接收新连接客户端的消息
num = recv(denglu_fd,&zhanghao,sizeof(zhanghao),0);//接收新加入的客户端输入的结构体信息
if(num!=0)
send(denglu_fd,"服务器接收成功!",sizeof(denglu_buf),0);
else //发送第一条消息给客户端确认收到结构体 //🎏第一次发送
send(denglu_fd,"服务器接收失败!",sizeof(denglu_buf),0);
printf("id=%s\n",zhanghao.id);
//判读id所对应的密码是否正确
memset(command,0,sizeof(command));
sprintf(command,"select pw from idandpw where id = '%s'",zhanghao.id);//从idandpw表中找出密码,条件是账号=输入的id,里面不需加
printf("command = %s\n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
res = mysql_store_result(my);//存放执行结果,
row = mysql_fetch_row(res);//返回结果集的下一行,
if(row==0)//没有一个符合的
{
printf("登陆7!\n");
send(denglu_fd,"您输入的账号不存在,按y重新输入,按任意按键退出登陆。",sizeof(denglu_buf),0);
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0); //🎏第一次接收
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 0;//退出登陆
}
else if(strcmp(row[0],zhanghao.pw)==0) //输入的密码符合id
{
// printf("登陆8!\n");
send(denglu_fd,"登陆成功!",sizeof(denglu_buf),0);//发送消息,返回
printf("登陆成功\n");
memset(command,0,sizeof(command));
sprintf(command,"select name from idandpw where id = '%s'",zhanghao.id);//从idandpw表中找出姓名,条件是对应姓名
printf("command = %s\n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
res = mysql_store_result(my);//存放执行结果,
row = mysql_fetch_row(res);//返回结果集的下一行,
printf("name= %s\n",*row);
strcpy(zhanghao.name,*row);//把名字传给结构体,以便于服务器的结构体数组使用
send(denglu_fd,*row,strlen(*row),0);//发送姓名,因为row是char*类型,所以用strlen统计一下name的字节大小
if (strcmp(sel,"3")==0)
goto zhuxiaozhanghao; //注销账户用的,因为注销账户需要跑一遍登陆
return 1;
}
else
{
// printf("登陆9!\n");
send(denglu_fd,"密码错误!",sizeof(denglu_buf),0);//发送消息
return 0;
}
}
//2.注册账号
else if (strcmp(sel,"2")==0)
{
memset(zhanghao.id,0,sizeof(zhanghao.id));
memset(zhanghao.pw,0,sizeof(zhanghao.pw));
memset(zhanghao.name,0,sizeof(zhanghao.name));
num = recv(denglu_fd,&zhanghao,sizeof(zhanghao),0);//接收新加入的客户端输入的结构体信息
printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
if(num!=0)
send(denglu_fd,"服务器接收成功!",sizeof(denglu_buf),0);
else //发送第一条消息给客户端确认收到结构体 第一次send
send(denglu_fd,"服务器接收失败!",sizeof(denglu_buf),0);
printf("id=%s\n",zhanghao.id);
memset(command,0,sizeof(command));
//id//
//printf("请输入您要注册的账号id:%s",zhanghao.id);
//scanf("%s",zhanghao.id);
sprintf(command,"select id from idandpw");//表中id信息
printf("command=%s\n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
perror("mysql_real_query");
res=mysql_store_result(my);//存放执行结果,
perror("mysql_store_result");
int row_num = mysql_num_rows(res);
perror("mysql_num_rows");
printf("num===%d\n",row_num);
for(int i=0;i<row_num;i++)//遍历数据库表中所有id
{
printf("测试\n");
row=mysql_fetch_row(res);//返回结果集的下一行
printf("row=%s\n",row[0]); //段错误
if(strcmp(row[0],zhanghao.id)==0)
{
send(denglu_fd,"此id已经注册!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0);//发送选择y 第二次send
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0);
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 0;//退出登陆
}
}
send(denglu_fd,"id注册成功!",sizeof(denglu_buf),0);// 注册id成功 第二次send
//密码(密码无要求)//
//printf("请输入您要注册账号的密码%s:",zhanghao.pw);
//scanf("%s",zhanghao.pw);
//昵称//
//printf("请输入您要注册账号的昵称%s:",zhanghao.name);
//scanf("%s",zhanghao.name);
memset(command,0,sizeof(command));
mysql_free_result(res);//释放
sprintf(command,"select name from idandpw");//表中name信息
printf("command=%s/n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
res=mysql_store_result(my);//存放执行结果,
row_num=mysql_num_rows(res);
printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
for(int i=0;i<row_num;i++)//遍历数据库中所有name
{
printf("注册1\n");
//printf("row_num=%d",row_num);
row=mysql_fetch_row(res);//返回结果集的下一行
if(strcmp(row[0],zhanghao.name)==0)
{
printf("row[%d]=%s,row_num=%d",i,row[i],row_num);
printf("注册2\n");
send(denglu_fd,"此name已经注册!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0); //发送选择y 第三次send
memset(client_ret,0,sizeof(client_ret));
printf("注册2.1\n");
recv(denglu_fd,client_ret,sizeof(client_ret),0);
printf("注册2.2\n");
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 0;//退出登陆
}
}
send(denglu_fd,"昵称注册成功!",sizeof(denglu_buf),0);// 注册昵称成功 第三次send
//id,密码,name全部无重复,则将账号信息加入数据库中
printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
printf("注册3\n");
memset(command,0,sizeof(command));
printf("id=%s,pw=%s,name=%s\n",zhanghao.id,zhanghao.pw,zhanghao.name);
sprintf(command,"insert into idandpw values(%s,%s,%s)",zhanghao.id,zhanghao.pw,zhanghao.name);//添加账号信息到数据库
ret = mysql_real_query(my,command,strlen(command));//指令执行
printf("注册4\n");
send(denglu_fd,"注册成功!按y回到登陆界面,按任意按键退出登陆。",sizeof(denglu_buf),0); //发送选择y //第四次send
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0);
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 2;//退出登陆
}
//3.注销账号信息(id,密码,昵称)
else if (strcmp(sel,"3")==0)
{
goto denglu;
zhuxiaozhanghao: //相当于跑一边登陆
printf("请问您真的要注销吗?,输入y确认,输入其他字符退出\n");
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0); //🍎第一次接收哦
if(strcmp(client_ret,"y")==0)
{
memset(command,0,sizeof(command));
sprintf(command,"delete from idandpw where id='%s'",zhanghao.id);//从idandpw表中找出姓名,条件是对应姓名
printf("command = %s\n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
if(ret==0)
{ memset(denglu_buf,0,sizeof(denglu_buf));
sprintf(denglu_buf,"删除id为%s的账户!",zhanghao.id);
printf("发送%s\n",denglu_buf);
send(denglu_fd,denglu_buf,sizeof(denglu_buf),0); //🍎第一次发送
//接收到y返回登陆界面,其余退出程序
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0); //🍎第二次接收
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 3;//退出登陆
}
}
else
return 0;
}
//4.4.管理员登陆(禁言,解禁,提出群聊)
else if (strcmp(sel,"4")==0)
{
memset(denglu_buf,0,sizeof(denglu_buf));
recv(denglu_fd,denglu_buf,sizeof(denglu_buf),0); //🍌第一次接收
if(strcmp(denglu_buf,"管理员登陆成功!")==0)
return 4;//回到server操作
}
//5.查看聊天记录
else if (strcmp(sel,"5")==0)
{
memset(command,0,sizeof(command));
sprintf(command,"select * from chat_date");//从数据库选择聊天数据
printf("command = %s\n",command);
ret = mysql_real_query(my,command,strlen(command));//指令执行
res = mysql_store_result(my);//存放执行结果,
chat_num=mysql_num_rows(res);//返回结果集的行数
for(int k=0;k<chat_num;k++)
{
// printf("test1\n");
row = mysql_fetch_row(res);//返回结果集的下一行,
memset(command,0,sizeof(command));
sprintf(command,"|第%d记录|_%s",k,row[0]); //把聊天记录接到字符传串里
//printf("%s\n",command);
send(denglu_fd,command,sizeof(denglu_buf),0);// 🕒第一次发送聊天记录
if(chat_num>10000); //如果消息超过10000行,重新清空表格chat_date,即设置历史聊天记录的上限
}
send(denglu_fd,"0",sizeof(denglu_buf),0); //给客户端发送0来确认发送结束
memset(client_ret,0,sizeof(client_ret));
recv(denglu_fd,client_ret,sizeof(client_ret),0);
if(strcmp(client_ret,"y")==0)
goto star;//重新输入
else
return 5;//退出
return 5;
}
//6.退出登陆
else if (strcmp(sel,"6")==0)
return 6;//退出
//1代表登陆成功,2代表注册成功,3,注销成功,4,管理员登陆,5,查看聊天记录成功,6,退出登陆,0代表失败
return 0;
}
账号系统denglu.h
#ifndef DENGLU_H
#define DENGLU_H
#include <mysql/mysql.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>
int denglu(int denglu_fd);
#endif
客户端client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#define IP "192.168.134.10"
#define PORT 6667
void *ptrhead_read(void *arg);
int client_fd;
struct infor
{ char id[20];//承接账号
char pw[20];//承接密码
char name[20];//承接昵称
int fd; //承接通信套节字
int index; //承接轮训下标,即count的值
int jinzhi;//承接禁止标识位
}zhanghao;
char buf[100];//承接服务器发送的语句
char sel[2];//选择
int main()
{
char guanli_id[20]={0},guanli_pw[20]={0};
int ret,choose,k;
client_fd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in ser_addr;//此结构体存放服务器信息
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);//服务端口
ser_addr.sin_addr.s_addr=inet_addr(IP);//服务器IP
ret = connect(client_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));//要连接的服务器地址
if(ret == -1)
{
perror("connect");
return -1;
}
//连接成功,进入登陆系统
star:
printf("+---------------------------------------+\n");
printf("| 登陆系统 |\n");
printf("|1.登陆账号 |\n");//已完成
printf("|2.注册账号 |\n");//已完成
printf("|3.注销账号信息(id,密码,昵称) |\n");//已完成
printf("|4.管理员登陆(禁言,解禁,提出群聊) |\n");//已完成
printf("|5.查看聊天记录(100条) |\n");//已完成
printf("|6.退出登陆 |\n");//已完成
printf("+---------------------------------------+\n");//已完成
scanf("%s",sel);
send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的登陆系统
//1.登陆账号
if(strcmp(sel,"1")==0)
{
denglu:
printf("请输入账号id:");//发送账号信息,对账号信息作初步的填写传输
scanf("%s",zhanghao.id);
printf("请输入账号密码:");
scanf("%s",zhanghao.pw);
send(client_fd,&zhanghao,sizeof(zhanghao),0);//把结构体发送给服务器端的账号系统
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 🎏第一次接收
if(strcmp(buf,"服务器接收失败!")==0)//退出
{
printf("出错!服务器接收失败!buf=%s\n",buf);//goto一下
return 0;
}
else if (strcmp(buf,"服务器接收成功!")==0)
printf("服务器接收成功!buf=(%s)\n\n\n",buf);
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//接收登陆命令 🎏第二次接收
if(strcmp(buf,"您输入的账号不存在,按y重新输入,按任意按键退出登陆。")==0)
{
printf("您输入的账号不存在,按y重新输入,按任意按键退出。\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
else if(strcmp(buf,"登陆成功!")==0)//如果登陆成功,跳出循环,进入收消息的循环
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//接收name
strcpy(zhanghao.name,buf);//将name写入结构体中
strcat(zhanghao.name,":");//在name后面写上':'
printf("name=%s\n",buf);
if (strcmp(sel,"3")==0)
goto zhuxiaozhanghao; //注销账户用的,因为注销账户需要跑一遍登陆
printf("*********************\n");
printf("* 登陆成功! *\n");
printf("*1.加入群聊 *\n");
printf("*2.进行私聊 *\n");
printf("*********************\n");
scanf("%s",sel);
send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的服务系统子线程 🍤第一次发送,选择群聊或者私聊
if(strcmp(sel,"1")==0)//直接goto到收发消息
goto chat;
else if(strcmp(sel,"2")==0)//选择私聊
{
printf("在线名单:\n");
for(k=0;;k++)//接收名单
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 🍤第一次接收
if(strcmp(buf,"0")==0)
{
printf("接收名单结束,共%d个在线人员\n",k);
private_chat:
printf("请输入您想要私聊的人的name:\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);//发送想要私聊的对象 🍤第二次发送
printf("您输入的name是:%s\n",buf);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 🍤第二次接收
printf("第二次接收的信息是:%s\n",buf);
if(strcmp(buf,"私聊建立成功!")==0)
{
printf("**私聊建立成功!**\n");
goto chat;
}
else if(strcmp(buf,"私聊建立失败!")==0)
{
printf("**私聊建立失败!**输入y重新私聊,输入其他按键退出\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0); //🍤第三次发送 ,或者chat
if(strcmp(buf,"y")==0)
goto private_chat;
else
return 0;
}
}
printf("第%d个的昵称是:%s\n",k+1,buf);
}
}
else if(strcmp(buf,"密码错误!")==0)
{
printf("密码错误!退出");
return 0;
}
}
}
//2.注册账号
else if(strcmp(sel,"2")==0)
{
printf("请输入您要注册的的账号id:");
scanf("%s",zhanghao.id);
printf("请输入您要注册的的账号密码:");
scanf("%s",zhanghao.pw);
printf("请输入您要注册的的账号name:");
scanf("%s",zhanghao.name);
send(client_fd,&zhanghao,sizeof(zhanghao),0);//把结构体发送给服务器端的账号系统
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 😀第一次接收
if(strcmp(buf,"服务器接收失败!")==0)//退出
{
printf("出错!服务器接收失败!buf=%s\n",buf);
return 0;
}
else if (strcmp(buf,"服务器接收成功!")==0)
printf("服务器接收成功!buf=(%s)\n\n\n",buf);
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 😀第二次接收
if(strcmp(buf,"此id已经注册!按y回到登陆界面,按任意按键退出登陆。")==0)
{
printf("此id已经注册!按y回到登陆界面,按任意按键退出登陆。\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
else if (strcmp(buf,"id注册成功!")==0)
printf("id注册成功!buf=(%s)\n\n",buf);
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 😀第三次接收
if(strcmp(buf,"此name已经注册!按y回到登陆界面,按任意按键退出登陆。")==0)
{
printf("此name已经注册!按y回到登陆界面,按任意按键退出登陆。");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
else if (strcmp(buf,"昵称注册成功!")==0)
printf("昵称注册成功!buf=(%s)\n\n\n",buf);
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 😀第四次接收
if(strcmp(buf,"注册成功!按y回到登陆界面,按任意按键退出登陆。")==0)
{
printf("注册成功!按y回到登陆界面,按任意按键退出登陆。\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
}
//3.注销账号信息(id,密码,昵称)
else if (strcmp(sel,"3")==0)
{
goto denglu;
zhuxiaozhanghao: //相当于跑一边登陆
printf("请问您真的要注销吗?,输入y确认,输入其他字符退出\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0); //🍎第一次发送
if(strcmp(buf,"y")==0)
printf("正在注销id为%s的账号!\n",zhanghao.id);//客户端开始删除操作
else
return 0;
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0); //🍎第一次接收
printf("%s\n",buf);
printf("注册成功!按y回到登陆界面,按任意按键退出登陆。\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0); //🍎第二次发送
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
//4.管理员登陆
else if (strcmp(sel,"4")==0)
{
memset(guanli_id,0,sizeof(guanli_id));
printf("请输入管理员id:");
scanf("%s",guanli_id);
if(strcmp(guanli_id,"yao")==0)
printf("管理员id正确\n");
else
{
printf("管理员id错误,即将退出\n");
sleep(1);
return 0;
}
memset(guanli_pw,0,sizeof(guanli_pw));
printf("请输入管理员pw:");
scanf("%s",guanli_pw);
if(strcmp(guanli_pw,"yao")==0)
printf("管理员pw正确\n");
else
{
printf("管理员id错误,即将退出\n");
sleep(1);
return 0;
}
send(client_fd,"管理员登陆成功!",sizeof(buf),0); //🍌第一次发送
mingdan:
printf("在线名单:\n");
for(k=0;;k++)//接收名单
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);//根据返回内容判断是否接收成功,阻塞 🍌第一次接收(名单)
if(strcmp(buf,"0")==0)
{
printf("接收名单结束,共%d个在线人员\n",k);
break;
}
printf("第%d个在线用户:%s\n",k+1,buf);
}
printf("请输入您想要进行从操作的用户的name\n"); /😡
memset(buf,0,100);
scanf("%s",buf);
printf("您输入的名字是:%s\n",buf);
send(client_fd,buf,strlen(buf),0);//发送想要私聊的对象 🍌第二次发送(姓名)
printf("+---------------------------------------+\n");
printf("| 管理系统 |\n");
printf("|1.禁言 |\n");
printf("|2.解禁 |\n");
printf("|3.提出群聊 |\n");
printf("+---------------------------------------+\n");
printf("请选择您要对其进行的操作\n");
memset(sel,0,sizeof(sel));
scanf("%s",sel);
send(client_fd,&sel,strlen(sel),0);//发送一个选择给服务器的登陆系统 //🍌第三次发送
if (strcmp(sel,"1")==0) //3个选择🍌第四次接收
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);
printf("禁言=%s\n",buf);
if(strcmp(buf,"禁言成功!")==0)
{
printf("禁言成功!即将返回管理系统\n");
goto mingdan;
}
}
else if (strcmp(sel,"2")==0)
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);
if(strcmp(buf,"解禁成功!"))
{
printf("解禁成功!即将返回管理系统\n");
goto mingdan;
}
}
else if (strcmp(sel,"3")==0)
{
memset(buf,0,100);
recv(client_fd,buf,sizeof(buf),0);
if(strcmp(buf,"踢出群聊成功!"))
{
printf("踢出群聊成功!即将返回管理系统\n");
goto mingdan;
}
}
else if (strcmp(sel,"4")==0)//退出管理系统
{
return 0;
}
return 0;
}
//5.查看聊天记录
else if (strcmp(sel,"5")==0)
{
printf("********查询历史记录开始********\n");
while(1)
{ memset(buf,0,100);
ret=recv(client_fd,buf,sizeof(buf),0); //🕒第一次接收数据
printf("%s\n",buf);
if(strcmp(buf,"0")==0)
{
printf("********查询历史激励完成********\n");
printf("**按y回到菜单,按任意按键退出。***\n");
memset(buf,0,100);
scanf("%s",buf);
send(client_fd,buf,strlen(buf),0);
if(strcmp(buf,"y")==0)
goto star;
else
return 0;
}
}
}
//6.退出登陆
else if (strcmp(sel,"6")==0)
{
return 0;
}
//else.输入其他提示错误,退出
else
{
printf("输入错误,即将退出!\n");
return 0;
}
//从服务器端收信息
chat:
{
printf("进入聊天\n");
//避免并法,创建线程,做到收发“同时进行”
pthread_t id;
pthread_create(&id,NULL,ptrhead_read,NULL);
while(1)//此循环是收信息,,//后期区分群聊和私聊
{
memset(buf,0,100);
ret=recv(client_fd,buf,sizeof(buf),0);
if(ret==0)
{
printf("服务器已断开\n");
return 0;
}
printf("%s\n",buf);
}
}
return 0;
}
//发信息到服务器端
void *ptrhead_read(void *arg)
{
time_t t;
char time_date[50];//时间信息
char date[80];
while(1)
{
memset(buf,0,sizeof(buf));
strcpy(buf,zhanghao.name);//将name:复制到strcpy中
scanf("%s",date);//承接输入的消息
strcat(buf,date); //将信息接到name之后
getchar();
t = time(NULL); //获取秒数值
struct tm * now = localtime(&t);//转换为本地时间
strftime(time_date,sizeof(time_date),"\t%Y-%m-%d %H:%M:%S",now);//设置格式化字符串
strcat(buf,time_date); //将时间接到信息之后
send(client_fd,buf,strlen(buf),0);
perror("send");
}
}