/********************************************
*先连接mysql数据库
* TCP通信服务器端流程
* 1,创建套接字
* 2,绑定
* 3,监听
* 4,开启IO多路复用 select 检测 等待连接
* *************************************************/
服务器端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <mysql/mysql.h>
/*
1:服务器先连接数据库 数据库在子函数中运用
2:创建套节字TCP协议
3:运用多路io复用进行监测客户端运行与信息接受情况与数据库结合完成功能实现
*/
#define PORT 5418
#define IP "127.0.0.1"
MYSQL* con;
int fd;
struct user{
int type;//选项类型
int lei; //数据库登陆的标志位
char name[25];
char passwd[20];
char jilu[200];
char opposite[25];//要与之私聊的账户
};
struct user use[25];
fd_set oldSet,newSet;//oldSet 存放监测的描述符,用于备份 newSet用于select函数
int clifd[200];
int count=0,maxfd;
void LINK (int fd);
void FUN (int cliFd,int i);
void REGISTER(int ke,int i);
void LOGIN(int deng,int i);
void Meun(void);
void Group_chat(int i,int readRet);
void Private_chat(int i,int readRet);
int main()
{
con = mysql_init(NULL); //初始化数据库空间
if(mysql_real_connect(con,"localhost","root","1","chatgpt",0,NULL,0)==NULL)//连接数据库
{
printf("连接数据库失败\n");
return -1;
}
printf("数据库已连接\n");
//创建套节字
fd = socket(AF_INET,SOCK_STREAM,0);
//绑定
int flag = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
struct sockaddr_in ser;
ser.sin_addr.s_addr = inet_addr(IP);
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
int ret = bind(fd,(struct sockaddr*)&ser,sizeof(ser));
if(ret==-1)
{
perror("bind\n");
return -1;
}
//监听
ret = listen(fd,25);
if(ret == -1)
{
perror("listen");
return -1;
}
printf("服务器基本属性已设置好\n");
//getchar();
//等待连接 多路io复用
FD_ZERO(&oldSet);
FD_SET(fd,&oldSet);//将连接套节字添加到集中
maxfd = fd; //处理连接请求--》fd发生可读变化//接收数据--》maxfd发生可读变化
int i,cliFd=0; //readRet 表示服务器读取到客户端法发的消息
while(1)
{
newSet = oldSet;
//开selertIO多路复用 使客户端连接和接受客户端消息互不影响
ret = select(maxfd+1,&newSet,NULL,NULL,NULL);// select监测maxfd通信和连接文件标识符
if(ret == -1)
{
return -1;
}
if(FD_ISSET(fd,&newSet)) //监测连接套接字在newSet集有没有变化
{
LINK(fd); //实现客户端连接
}
for(i=0;i<count;i++)
{ //在已加入的通信文件符的客户端进行
cliFd=clifd[count];
FUN (cliFd,i);
}
}
return 0;
}
void LINK (int fd)
{
struct sockaddr_in cli;
int len = sizeof(cli);
clifd[count] = accept(fd,(struct sockaddr*)&cli,&len);
printf("%s_%d:客户端上线\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
FD_SET(clifd[count],&oldSet);//将新加入的通信套节字给oldSet备份
maxfd = (maxfd<clifd[count])?clifd[count]:maxfd; // maxfd小于后者就执行?后第一个否则第二个
count++;
}
void FUN (int cliFd,int i)
{
char buf[100];
int readRet,j,str,ke,deng;
if(FD_ISSET(clifd[i],&newSet))
{
// printf("%s\n",use[i].name);
// memset(&use[i],0,sizeof(use[i]));
readRet = recv(clifd[i],&use[i],sizeof(use[i]),0); //接收客户端发的请求
printf("服务器接收到客户端的指令\n");
if(readRet<0)
{
perror("recv\n");
return;
}
else if(readRet==0)
{
//readRet = recv(clifd[i],&use[i],sizeof(use[i]),0);
printf("tonxintaojiezi :%d,%s",clifd[i],use[i].name);
printf("服务器接收到客户端的退出指令\n");
if(readRet<0)
{
perror("recv\n");
return;
}
sprintf(buf,"%d客户端已下线\n",clifd[i]);
printf("%s",buf);
FD_CLR(clifd[i],&oldSet);
memset(buf,0,sizeof(buf));
sprintf(buf,"update chat set lei=0 where name='%s'",use[i].name); //先!将!退出过!的标志为由1改为0
int ret = mysql_real_query(con,buf,strlen(buf)); //执行
for(j=i;j<count;j++)
{
clifd[j]=clifd[j+1]; //再关闭的客户端删除
use[j] = use[j+1]; //将每个客户端的用户信息删除
}
count--;
}
else
{
str = use[i].type;//接收客户端法的类型请求
printf("%d\n",use[i].type);
switch (str)
{
case 1:
deng = clifd[i];
LOGIN(deng,i);
break;
case 2:
ke = clifd[i];
REGISTER(ke,i);
break;
case 3:
Group_chat(i,readRet);
break;
case 4:
Private_chat(i,readRet);
break;
default:
printf("输入有误 请重新输入\n");
break;
}
}
}
}
void LOGIN(int deng,int i) //登陆
{
char buf[100];
char rbuf[100];
printf("接收客户端法来的请求\n");
sprintf(buf,"select * from chat where name = '%s'",use[i].name);
int ret = mysql_real_query(con,buf,strlen(buf));//执行sql指令
if(ret != 0){
printf("语句执行失败\n");
printf("query:%s",mysql_error(con)); //mysql 出错打印
}
else{
printf("语句执行成功\n");
//指令执行过之后检索到的必须调用mysql_store_result函数存储结果
MYSQL_RES *res = mysql_store_result(con);
MYSQL_ROW row ;
while(1){
row = mysql_fetch_row(res);//持续检索下一行的结果
if(row ==NULL)
{
//printf("查无此人\n");
break;
}
else{
if(strcmp(use[i].passwd,row[1])==0)
{
printf("密码正确\n");
memset(rbuf,0,strlen(rbuf));
sprintf(rbuf,"密码正确!");
memset(buf,0,sizeof(buf));
sprintf(buf,"select lei from chat where name='%s'",use[i].name);
ret = mysql_real_query(con,buf,strlen(buf)); //执行
MYSQL_RES *re = mysql_store_result(con);
MYSQL_ROW result = mysql_fetch_row(re);
if(atoi(result[0])==1)
{
memset(rbuf,0,strlen(rbuf));
sprintf(rbuf,"该用户已登陆!");
ret = send(clifd[i],rbuf,sizeof(rbuf),0);
break;
}
memset(buf,0,sizeof(buf));
sprintf(buf,"update chat set lei=1 where name='%s'",use[i].name); //将登陆过的标志为由0改为1
ret = mysql_real_query(con,buf,strlen(buf)); //执行
ret = send(clifd[i],rbuf,sizeof(rbuf),0);
if(ret == -1)
{
perror("send\n");
return ;
}
printf("login:%d",clifd[i]);
printf("密码正确已发送\n");
}
else{
printf("密码错误\n");
memset(rbuf,0,strlen(rbuf));
sprintf(rbuf,"密码错误,请重新输入!");
ret = send(clifd[i],rbuf,sizeof(rbuf),0);
if(ret == -1)
{
perror("send\n");
return ;
}
printf("密码出错已发送\n");
break;
}
}
}
}
}
void Meun(void) //次菜单
{
printf("|\t------------------------\t|\n");
printf("|\t-------\t3:群聊\t--------\t|\n");
printf("|\t-------\t4:私聊\t--------\t|\n");
printf("|\t-------\t3.查看聊天记录\t--\t|\n");
printf("|\t------------------------\t|\n");
}
void REGISTER(int ke,int i) //注册
{
char buf[100];
printf("接收客户端法来的请求\n");
sprintf(buf,"insert into chat values('%s','%s',%d)",use[i].name,use[i].passwd,use[i].lei);
int ret = mysql_real_query(con,buf,strlen(buf));//执行sql指令
if(ret != 0){
printf("语句执行失败\n");
printf("query:%s",mysql_error(con)); //mysql 出错打印
}
else{
printf("语句执行成功\n");
}
memset(&use[i],0,sizeof(use[i]));
use[i].type = 2;
ret = send(clifd[i],&use[i],sizeof(use[i]),0);
if(ret == -1)
{
perror("send\n");
return ;
}
printf("发送成功\n");
}
void Group_chat(int i,int readRet) //群聊 clifd【i】表示那个客户端发了信息
{
char buf[50];
int j;
printf("服务器接收到客户端的信息\n");
if(readRet<0)
{
perror("recv\n");
return;
}
else if(readRet==0)
{
sprintf(buf,"%d客户端已退出群聊\n",clifd[i]);
}
else
{
printf("%d客户端消息:%s\n",clifd[i],use[i].jilu);
for(j=0;j<count;j++)
{
if(j!=i){
send(clifd[j],&use[i],sizeof(use[i]),0);
}
}
}
}
void Private_chat(int i,int readRet) //私聊
{
char buf[50];
int j;
printf("服务器接收到客户端的信息\n");
if(readRet<0)
{
perror("recv\n");
return;
}
else if(readRet==0)
{
sprintf(buf,"%d客户端已退出私聊\n",clifd[i]);
}
else
{
printf("私聊信息发送\n");
printf("count%d\n",count);
for(j=0;j<count;j++){
printf("%s%s\n",use[j].name,use[i].opposite);
if(strcmp(use[j].name,use[i].opposite)==0){
printf("%d客户端:%s:%s\n",clifd[i],use[i].name,use[i].jilu);
send(clifd[j],&use[i],sizeof(use[i]),0);
}
}
}
}
客户端:
/********************************************
*先连接mysql数据库
* TCP通信服务器端流程
* 1,创建套接字
* 2,连接服务器
* 3,给服务器数据类型实现 登录注册群聊私聊
* *************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
int fd;
void meun(void);
void login(int fd);
void REGISTER(int fd);
void MeUn(void);
void *recv_pthread(void *arg);
void sigFun(int n);
void *Private_chat(void *arg);
#define PORT 5418
#define IP "127.0.0.1"
struct user
{
int type; // 选项类型
int lei; // 数据库登陆的标志位
char name[25];//账号
char passwd[20];//密码
char jilu[200];//聊天记录
char opposite[25];//要与之私聊的账户
} use,rec;
/*1:使用TCP协议创建套接字*/
int main()
{
// 创建套节字
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
perror("socket\n");
return -1;
}
// 连接
printf("client sockfd = %d\n", fd);
/*2.连接服务器
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
第二个参数:填写的是服务器的相关信息
*/
struct sockaddr_in cli;
cli.sin_family = AF_INET;
cli.sin_port = htons(PORT);
cli.sin_addr.s_addr = inet_addr(IP);
int ret = connect(fd, (struct sockaddr *)&cli, sizeof(struct sockaddr));
if (ret == -1)
{
perror("connect");
return -1;
}
printf("连接服务器成功\n");
int q = 0;
while (1)
{
meun();
scanf("%d", &q);
switch (q)
{
case 1:
login(fd);
break;
case 2:
REGISTER(fd);
break;
case 0:
break;
default:
printf("输入有误请重新输入\n");
break;
}
}
return 0;
}
void meun(void)
{
printf("|\t------------------------\t|\n");
printf("|\t-------\t1:登陆\t--------\t|\n");
printf("|\t-------\t2:注册\t--------\t|\n");
printf("|\t-------\t3.退出\t--------\t|\n");
printf("|\t------------------------\t|\n");
}
void MeUn(void)
{
printf("|\t------------------------\t|\n");
printf("|\t-------\t3:群聊\t--------\t|\n");
printf("|\t-------\t4:私聊\t--------\t|\n");
printf("|\t------------------------\t|\n");
}
void login(int fd)
{
int ret = 0, num;
char buf[100];
memset(&use, 0, sizeof(use));
use.type = 1; // 数据类型为时是登陆
printf("请输入要登陆的账号姓名!\n");
q:
scanf("%s", use.name);
printf("请输入密码!\n");
p:
scanf("%s", use.passwd);
printf("正在向服务器发送请求\n");
ret = send(fd, &use, sizeof(use), 0);
if (ret == -1)
{
perror("send\n");
return;
}
printf("发送成功\n");
recv(fd, buf, sizeof(buf), 0);
printf("%s\n", buf);
if (strcmp(buf, "密码正确!") == 0) // 群聊私聊嵌套在登陆之中
{
signal(2, sigFun); // 捕获异常结束 将账户信息发送到服务器
printf("\n");
int see;
pthread_t chatid;
char buff[25];
while (1)
{
MeUn();
printf("请输如功能:\n");
scanf("%d", &see);
switch (see)
{
case 3:
printf("已加入群聊\n");
pthread_create(&chatid, NULL, recv_pthread, NULL);
while (1)
{
scanf("%s", use.jilu);
use.type = 3; // 群聊
printf("%d",use.type);
ret = send(fd, &use, sizeof(use), 0);
if (ret == -1)
{
perror("send\n");
return;
}
memset(&use,0,sizeof(use));
printf("发送成功\n");
}
break;
case 4:
printf("请输入您要私聊的账号名\n");
pthread_create(&chatid, NULL, Private_chat, NULL);
scanf("%s",use.opposite);
use.type = 4; // 给服务器私聊指令
printf("\n");
while(1)
{
scanf("%s",use.jilu);
ret = send(fd, &use, sizeof(use), 0);
if (ret == -1)
{
perror("send\n");
return;
}
printf("发送成功\n");
}
break;
}
}
}
if (strcmp(buf, "密码错误,请重新输入!") == 0)
{
goto p;
}
if (strcmp(buf, "该用户已登陆!") == 0)
{
printf("请输入重新登陆账号\n");
goto q;
}
}
void sigFun(int n) // 退出登陆
{
/* int ret;
send(fd,&use,sizeof(use),0);
if (ret == -1)
{
perror("send\n");
return;
}
printf("发送成功\n");*/
exit(0);
}
void REGISTER(int fd) // 注册
{
int ret = 0;
memset(&use, 0, sizeof(use));
use.type = 2; // 数据类型为2时是注册
printf("请输入要注册的账号姓名!\n");
scanf("%s", use.name);
printf("请输入密码!\n");
scanf("%s", use.passwd);
use.lei = 0;
printf("正在向服务器发送请求\n");
ret = send(fd, &use, sizeof(use), 0);
if (ret == -1)
{
perror("send\n");
return;
}
printf("发送成功\n");
recv(fd, &use, sizeof(use), 0);
printf("注册成功!\n");
}
void *recv_pthread(void *arg) // 群聊子线程接受信息
{
int READ_recv;
while (1)
{
READ_recv = recv(fd, &use, sizeof(use), 0);
if (READ_recv != 0)
{
printf("[群聊消息]:%s\n", use.jilu);
}
// memset(&use, 0, sizeof(use));
}
}
void *Private_chat(void *arg) //私聊接收信息
{
int READ_recv;
while (1)
{
READ_recv = recv(fd, &rec, sizeof(rec), 0);
if (READ_recv != 0)
{
printf("[私聊消息]%s:%s\n",rec.name,rec.jilu);
}
//memset(&use, 0, sizeof(use));
}
}