服务端使用框架:非阻塞轮询;
客户端使用框架:多线程;
使用数据库:mysql;
实现主要功能:
1.注册;
2.登录;
3.群聊;
4.私聊;
5.查看聊天记录;
6.查看在线人数;
7.修改个性签名;
8.修改密码;
9.发送文件;
10.退出登录;
11.注销账号;
进入时界面:左边服务器,右边客户端;
登录成功时界面:
客户端:
client.h
#ifndef _select_h_
#define _select_h_
#include <stdio.h>
#include <time.h>
#define SIZE 1024
#define NOW_MAX 20
typedef struct msg //表示聊天时的信息
{
char msg[SIZE]; //消息内容
char localname[20]; //消息目的名称
char account[20]; //账户
char fromname[20]; //消息来源名称
char password[20]; //用户密码
char signname[40]; //个性签名
int cmd; //工作模式
int num; //用于保存文件复制的字节数
}Msg;
//最大允许并行客户端数量
void regisc(int client_socket);
void main_menu(void);
int Ask_server(int client_socket);
int entry(int client_socket);
void user_menu(void);
void User_used(int client_socket);
void * readMsg (void *v);
void entry_out(int client_socket);
void chat_all(int client_socket);
void chat_one(int client_socket);
void client_logout(int client_socket);
void save(Msg*msg);
void look_chat();
void see_now_time(int client_socket);
void revise_sign(int client_socket);
void revise_password(int client_socket);
void client_guanli(int client_socket);
void transfer_file(int client_socket);//传输文件
void transfer_file_y(int client_socket);//同意接受文件
void transfer_file_n(int client_socket);//拒绝接受文件
void start_transfer_file(int client_socket);//开始传输文件
void save_transfer_file(Msg * buf);//接受文件
#endif
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include "client.h"
#define SER_IP "127.0.0.1"
#define SER_PORT 6789
#define LISTEN_MAX 100
char myname[20]={0};
char signname[100]={0};
char account_c[100]={0};
char mylocalname[20]; //用于保存传输文件来源的名字
char fsignname[40]; //用于保存文件传输名字
int root=0;
int flag = 1;
void *recvdata_do_handler(void *a);
/********************************************
* TCP通信客户端流程
* 1,创建套接字
* 2,请求连接
* 3,收发数据
* 4,关闭套接字
* *************************************************/
int main(int argc, char *argv[])
{
//1.创建套接字 IPV4 流式套接字 TCP协议
int clifd = socket(AF_INET,SOCK_STREAM,0);
if(clifd == -1)
{
perror("socket");
return -1;
}
printf("clifd = %d\n",clifd);
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(struct sockaddr_in));
seraddr.sin_family = AF_INET;//ipv4协议类型
seraddr.sin_addr.s_addr = inet_addr(SER_IP);
seraddr.sin_port = htons(SER_PORT);//端口 网络字节序
//2.请求连接
if(connect(clifd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr_in)) == -1)
{
perror("connect");
return -1;
}
printf("连接成功\n");
Ask_server(clifd);
close(clifd);
return 0;
}
void main_menu(void)
{
system("clear");
printf("\n\n\n\n\n\n\n\n");
printf("+++++++++++++++++++++++++++++++++++++++++\n");
printf("+++\t 欢迎使用101聊天室 +++\n");
printf("+++\t\t1,注册 +++\n");
printf("+++\t\t2,登录 +++\n");
printf("+++\t\t3,退出 +++\n");
printf("+++++++++++++++++++++++++++++++++++++++++\n");
}
int Ask_server(int client_socket)
{
char ch;
int ret;
while(1)
{
main_menu();//引用菜单栏
printf("请输入您要做操作\n");
scanf("%c",&ch);
while(getchar() != '\n');
switch(ch)
{
case '1': //注册
regisc(client_socket);
sleep(1);
break;
case '2': //登录
ret = entry(client_socket);
if (ret == 1)
{
User_used(client_socket); //调用函数表示用户界面
}
sleep(1);
break;
case '3': //退出
exit(0);
break;
}
}
}
//注册函数
void regisc(int client_socket)
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd=1;
printf("请输入用户名\n");
scanf("%s",msg.fromname);
while(getchar() != '\n');
printf("请输入账号\n");
scanf("%s",msg.account);
while(getchar() != '\n');
printf("请输入密码\n");
scanf("%s",msg.password);
while(getchar() != '\n');
printf("请输入个性签名\n");
scanf("%s",msg.signname);
while(getchar() != '\n');
send(client_socket,&msg,sizeof(msg),0);
recv(client_socket,&msg,sizeof(msg),0);
printf("%d\n",msg.cmd);
if(msg.cmd == 1001)
{
printf("注册成功\n");
}
else if (msg.cmd == -1)
{
printf("用户名以存在,注册失败\n");
}
else
{
printf("系统繁忙,注册失败\n");
}
sleep(2);
}
//登录账号
int entry(int client_socket)
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd = 2;
printf("登录,请输入账号名: ");
scanf("%s",msg.account);
while(getchar() != '\n');
printf("登录,请输入密码: ");
scanf("%s",msg.password);
while(getchar() != '\n');
printf("\n");
write(client_socket, &msg, sizeof(msg));
memset(&msg,0,sizeof(msg));
read(client_socket, &msg, sizeof(msg));
printf("%d\n",msg.cmd);
if(msg.cmd == -1) //表示用户不存在
{
printf("登录失败,系统繁忙.\n");
sleep(2);
return -1;
}
if(msg.cmd == -2) //表示用户不存在
{
printf("登录失败,用户不存在.\n");
sleep(2);
return -2;
}
if(msg.cmd == -3)
{
printf("登录失败,用户已登录.\n");
sleep(2);
return -3;
}
if(msg.cmd == -4)
{
printf("登录失败,密码错误.\n");
sleep(2);
return -4;
}
if(msg.cmd == 1002)
{
printf("登录成功,登录中...\n");
printf("%s %s \n",msg.fromname,msg.signname);
strcpy(myname,msg.fromname); //保存在线名字
strcpy(signname, msg.signname); //保存个性签名
strcpy(account_c,msg.account); //保存账号
root=msg.num; //获取权限
sleep(2);
return 1;
}
}
void user_menu(void)
{
system("clear");
printf("%s: %s\n",myname,signname);
//printf("\n\n\n\n\n\n\n");
printf("++++++++++++++++++++++++++++++++++++++++\n");
printf("+++ +++\n");
printf("+++\t 欢迎使用101聊天室 +++\n");
printf("+++ +++\n");
printf("++++++++++++++++++++++++++++++++++++++++\n");
printf("+++\t\t1,群聊 +++\n");
printf("+++\t\t2,私聊 +++\n");
printf("+++\t\t3,退出登录 +++\n");
printf("+++\t\t4,查看聊天记录 +++\n");
printf("+++\t\t5,显示当前在线人员 +++\n");
printf("+++\t\t6,修改个性签名 +++\n");
printf("+++\t\t7,修改密码 +++\n");
printf("+++\t\t8,传输文件 +++\n");
printf("+++\t\t9,注销账号 +++\n");
printf("+++\t\tA,管理 +++\n");
printf("++++++++++++++++++++++++++++++++++++++++\n");
}
//用户界面
void User_used(int client_socket)
{
//要进行读写分离
pthread_t read_id;
pthread_create(&read_id, NULL, readMsg, (void *)&client_socket);
pthread_detach(read_id); //等待线程分离
char ch;
flag=1;
while(flag)
{
user_menu();//调用第二个用户界面
printf("请输入您要做操作\n");
scanf("%c",&ch);
while(getchar() != '\n');
switch(ch)
{
case '1': //群聊
chat_all(client_socket);
break;
case '2': //私聊
chat_one(client_socket);
break;
case '3': //退出登录
entry_out(client_socket);
flag = 0;
break;
case '4': //查看聊天记录
look_chat();
break;
case '5': //显示当前在线人数
see_now_time(client_socket);
break;
case '6': //修改个性签名
revise_sign(client_socket);
break;
case '7': //修改密码
revise_password(client_socket);
sleep(1);
break;
case '8': //传输文件
transfer_file(client_socket);
break;
case 'y': //表示愿意接受文件
transfer_file_y(client_socket);
printf("接收中\n");
getchar();
break;
case 'n': //表示不愿意接受文件
transfer_file_n(client_socket);sleep(1);
break;
case '9':client_logout(client_socket);//表示注销账号
flag=0;break;
case 'a':if(root==1)//判断是不是管理员
client_guanli(client_socket);
else
printf("管理失败没有权限\n");
sleep(1);
break;
}
}
//pthread_cancel(read_id);
}
//读写分离专门收消息
void * readMsg (void *v)
{
int client_socket = *((int*)v);
Msg buf;
time_t t;
struct tm *pt;
int i = 0;
while(1)
{
memset(&buf,0,sizeof(Msg));
int ret = read(client_socket, &buf, sizeof(Msg));
if(ret == -1)
{
perror("read");
break;
}
switch(buf.cmd)
{
case 3:
printf(" %s\n",buf.msg);
save(&buf); //保存聊天记录
break;
case 4: //私聊
printf(" %s\n",buf.msg);
save(&buf); //保存聊天记录
break;
case -3: //私聊失败
printf("私聊失败,用户不存在或下线\n");
break;
case 5 : //退出登录
printf("%s 退出登录\n",buf.fromname);
sleep(1);
pthread_exit(NULL); //线程退出
break;
case 6 : //显示当前在线人数
printf("当前在线人员:\n");
printf("%s\n",buf.msg);
break;
case 7 : //修改个性签名成功
strcpy(signname,buf.signname);
printf("修改个性签名成功\n");
break;
case -7 : //修改个性签名失败
printf("修改个性签名失败\n");
break;
case 8 : //修改密码成功
printf("修改密码成功\n");
entry_out(client_socket);
flag=0;
break;
case -8 : //修改密码失败
printf("修改密码失败\n");
break;
case 9 : //调用函数确认是否接受文件
system("clear");
printf("请问你是否接受来自 %s 的文件 %s(y/n)\n",buf.fromname,buf.signname);
strcpy(mylocalname,buf.fromname); //保存传输文件来源名字
sleep(1);
break;
case -9 : //表示传输文件失败,没有找到该人
printf("发送文件失败,好友不在线或不存在\n");sleep(1);
break;
case 10 : //表示愿意接受文件,开始传输
start_transfer_file(client_socket);
sleep(1);
break;
case -10 : //表示不愿意接受文件
printf("发送文件失败,好友拒绝接受文件\n");sleep(1);
break;
case 11 : //接受文件
save_transfer_file(&buf);
break;
//printf("%d\n",i);
case 22:printf("%s\n",buf.msg);
break;
case 87:printf("禁言成功\n");
break;
case 88:printf("踢人成功\n");
break;
case 99:printf("注销成功\n");
sleep(1);
pthread_exit(NULL); //线程退出
break;
default:break;
}
}
}
void entry_out(int client_socket)//退出登录
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd=5;
strcpy(msg.account,account_c);
strcpy(msg.fromname,myname);
send(client_socket,&msg,sizeof(msg),0);
sleep(1);
}
void chat_all(int client_socket)//群聊
{
system("clear");
Msg msg;
time_t t;
char senr[150];
memset(&msg,0,sizeof(msg));
msg.cmd = 3;
strcpy(msg.fromname,myname);
strcpy(msg.localname,"All");
printf("输入quit退出\n");
printf("请输入你要群发送的信息\n");
while(1)
{
memset(msg.msg,0,sizeof(msg.msg));
memset(senr,0,sizeof(senr));
// getchar();
time(&t);
struct tm *pt = localtime(&t);
getchar();
printf("%d年%d月%d日 %d:%d:%d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
pt->tm_hour,pt->tm_min,pt->tm_sec);
sprintf(msg.msg,"\t\t\t%d年%d月%d日 %d:%d:%d\n\t\t\t",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
pt->tm_hour,pt->tm_min,pt->tm_sec);
printf("%s:",msg.fromname);
scanf("%s",senr);
strcat(msg.msg,msg.fromname);
strcat(msg.msg,":");
strcat(msg.msg,senr);
while(getchar() != '\n');
if(strcmp("quit",senr)==0)
break;
write(client_socket, &msg, sizeof(Msg));
}
save(&msg);
sleep (2);
}
void chat_one(int client_socket)//私聊
{
system("clear");
Msg msg;
char senr[150];
time_t t;
memset(&msg,0,sizeof(msg));
msg.cmd = 4;
printf("请输入你要聊天的对象:\n");
scanf ("%s",msg.localname);
while(getchar() != '\n');
strcpy (msg.fromname,myname);
while(1)
{
getchar();
time(&t);
struct tm *pt = localtime(&t);
printf("%d年%d月%d日 %d:%d:%d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
pt->tm_hour,pt->tm_min,pt->tm_sec);
sprintf(msg.msg,"\t\t\t%d年%d月%d日 %d:%d:%d\n\t\t\t",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
pt->tm_hour,pt->tm_min,pt->tm_sec);
printf("%s:",msg.fromname);
scanf("%s",senr);
while(getchar() != '\n');
if(strcmp("quit",senr)==0)
{
break;
}
strcat(msg.msg,msg.fromname);
strcat(msg.msg,":");
strcat(msg.msg,senr);
write(client_socket, &msg, sizeof(Msg));
save(&msg); //保存聊天记录
}
sleep(2);
}
void client_logout(int client_socket)//注销
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd=99;
strcpy (msg.account,account_c);
send(client_socket,&msg,sizeof(msg),0);
}
void save(Msg*msg)//保存
{
char buf2[120];
char ai[1050];
memset(buf2,0,sizeof(buf2));
memset(ai,0,sizeof(ai));
if(strcmp(myname,msg->fromname)==0)
{
sprintf(buf2,"../liaotianjilu/%s & %s.txt",msg->fromname,msg->localname);
}
else
{
sprintf(buf2,"../liaotianjilu/%s & %s.txt",msg->localname,msg->fromname);
}
int ps=open(buf2,O_WRONLY|O_CREAT|O_APPEND,0644);
sprintf(ai,"%s\n",msg->msg);
write(ps,ai,strlen(ai));
close (ps);
}
void look_chat()//查看聊天记录
{
char buf2[120];
char ai[10500];
memset(ai,0,sizeof(ai));
memset(buf2,0,sizeof(buf2));
printf("查看与谁的聊天记录:\n");
scanf("%s",buf2);
while(getchar()!='\n');
sprintf(ai,"../liaotianjilu/%s & %s.txt",myname,buf2);
int ps=open(ai,O_RDONLY);
if(ps==-1)
{
printf("查看失败\n");
sleep(1);
return;
}
memset(ai,0,sizeof(ai));
read(ps,ai,sizeof(ai));
printf("%s\n",ai);
getchar();
close(ps);
}
void see_now_time(int client_socket)//查看在线人数
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd = 6;
strcpy (msg.fromname,myname);
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
void client_guanli(int client_socket)//管理
{
Msg msg;
memset(&msg,0,sizeof(msg));
printf("请选择:1禁言,2踢人\n");
int i;
scanf("%d",&i);
while(getchar()!='\n');
if(i==1)
{
msg.cmd=87;
printf("请选择要禁言的人:\n");
see_now_time(client_socket);
scanf("%s",msg.localname);
}
else if(i==2)
{
msg.cmd=88;
printf("请选择要踢的人:\n");
see_now_time(client_socket);
scanf("%s",msg.localname);
}
else
{
printf("管理失败\n");
return;
}
strcpy (msg.fromname,myname);
write(client_socket, &msg, sizeof(Msg));
sleep(1);
}
//修改个性签名
void revise_sign(int client_socket)
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd = 7;
printf("请输入新的个性签名:");
scanf("%s",msg.signname);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
strcpy(msg.account,account_c);
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
//修改密码
void revise_password(int client_socket)
{
Msg msg;
memset(&msg,0,sizeof(msg));
msg.cmd = 8;
printf("请输入新的密码: ");
scanf("%s",msg.password);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
strcpy(msg.account,account_c);
write(client_socket, &msg, sizeof(Msg));
sleep(2);
}
void transfer_file(int client_socket)//传输文件
{
Msg msg;
msg.cmd = 9;
printf("请输入你要传输文件的对象:");
scanf ("%s",msg.localname);
while(getchar() != '\n');
printf("请输入你要传输的本地文件名:");
scanf ("%s",msg.signname);
while(getchar() != '\n');
strcpy(msg.fromname,myname); //需要保存名字
write(client_socket, &msg, sizeof(Msg));
printf("等待验收中...\n");
strcpy(fsignname,msg.signname);
strcpy(mylocalname,msg.localname);
getchar();
}
//表示愿意接受文件
void transfer_file_y(int client_socket)
{
Msg msg;
msg.cmd = 10;
strcpy(msg.fromname,myname);
strcpy(msg.localname,mylocalname);
strcpy(mylocalname,"\0"); //用完后置零
write(client_socket, &msg, sizeof(Msg));
}
//表示不愿意接受文件
void transfer_file_n(int client_socket)
{
Msg msg;
msg.cmd = -10;
strcpy(msg.fromname,myname);
strcpy(msg.localname,mylocalname);
strcpy(mylocalname,"\0"); //用完后置零
write(client_socket, &msg, sizeof(Msg));
}
//传出文件来源开始传输文件
void start_transfer_file(int client_socket)
{
Msg msg;
char ai[120];
msg.cmd = 11;
strcpy(msg.fromname,myname);
strcpy(msg.signname,fsignname);
strcpy(msg.localname,mylocalname);
sprintf(ai,"../send/%s",msg.signname);
int fd = open(ai,O_RDONLY);
if(fd == -1)
{
perror("open");
printf("传输失败\n");
return ;
}
int ret = 0;
int i = 0;
while(ret = read(fd,msg.msg,SIZE))
{
if(ret == -1)
{
perror("read");
break;
}
if (ret == 0)
{
break;
}
msg.num = ret;
write(client_socket, &msg, sizeof(Msg));
usleep(10000); //这个睡眠时间减缓传输速度,降低不可预祝错误出现
}
printf("文件复制完成传输\n");
strcpy(fsignname,"\0");
strcpy(mylocalname,"\0");
close(fd);
}
//接受文件
void save_transfer_file(Msg * buf)
{
char ai[120];
sprintf(ai,"../recv/%s",buf->signname);
int fd = open(ai,O_WRONLY|O_CREAT|O_APPEND,0777);
write(fd,buf->msg,buf->num);
if(buf->num != SIZE)
{
printf ("文件接受完成\n");
}
close (fd);
}
服务端:
select.h
#ifndef _select_h_
#define _select_h_
#include <stdio.h>
#define SIZE 1024
#define NOW_MAX 20
typedef struct msg //表示聊天时的信息
{
char msg[SIZE]; //消息内容
char localname[20]; //消息目的名称
char account[20]; //账户
char fromname[20]; //消息来源名称
char password[20]; //用户密码
char signname[40]; //个性签名
int cmd; //工作模式
int num; //用于保存文件复制的字节数
}Msg;
//最大允许并行客户端数量
typedef struct now_client //在线人数
{
char name[20];
int socket;
int jin;
int ti;
}NowClient;
int regiss(int client_socket, Msg *msg);
int entry(int client_socket, Msg *msg);
void server_entryout(int client_socket,Msg *msg);
void server_chatall(int client_socket, Msg * msg);
void server_chatone(int client_socket, Msg * msg);
void server_logout(int client_socket,Msg *msg);
void server_save(Msg*msg);
void see_nowuser(int client_socket, Msg * msg);
void sremove(int client_socket, Msg * msg);
void shutup(int client_socket, Msg * msg);
void server_transfer_file(int client_socket, Msg * msg);//收到开始传输文件命令
void server_transfer_file_y(Msg * msg);//接受文件
void server_transfer_file_n(Msg * msg);//拒绝文件
void server_start_transfer_file(Msg * msg);//开始文件传输
void server_revise_sign(int client_socket, Msg * msg);
void server_revise_password(int client_socket, Msg * msg);
#endif
server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include "select.h"
#include "1mysql.h"
#define SER_IP "127.0.0.1"
#define SER_PORT 6789
#define LISTEN_MAX 100
NowClient user[NOW_MAX]; //表示当前在线人的资料
/********************************************
* TCP通信服务器端流程
* 1,创建套接字
* 2,绑定
* 3,监听
* 4,等待连接
* 5,收发数据
* 6,关闭套接字
* *************************************************/
int all_clifd[256] = {0};//存放所有的客户端描述符
int count = 0;//计数
MYSQL *my_sql;//数据库描述符
void accept_do_handler(int sockfd);
void recv_do_handler(int sockfd);
void send_to_all_cli(char *buf);
int sockfd;
void guan()//关闭函数
{
close(sockfd);
mysql_close(my_sql);
exit(-1);
}
int main(int argc, char *argv[])
{
pid_t pid= fork();
if (pid==0){
//1.创建套接字 IPV4 流式套接字 TCP协议
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("socket");
return -1;
}
printf("sockfd = %d\n",sockfd);
signal(10,guan);
//2.绑定
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(struct sockaddr_in));
seraddr.sin_family = AF_INET;//ipv4协议类型
// seraddr.sin_addr;//ip地址 网络字节序
// inet_aton(SER_IP,&seraddr.sin_addr);
seraddr.sin_addr.s_addr = inet_addr(SER_IP);
seraddr.sin_port = htons(SER_PORT);//端口 网络字节序
int opt=1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(sockfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr_in)) == -1)
{
perror("bind");
return -1;
}
printf("绑定成功\n");
//3.监听
if(listen(sockfd,LISTEN_MAX) == -1)
{
perror("listen");
return -1;
}
printf("监听成功\n");
//非阻塞轮询
int maxfd = sockfd;
//处理连接请求--》sockfd发生可读变化
//接收数据--》clifd发生可读变化
fd_set readfds;//检测可读变化
my_sql=Sconnect();//连接数据库
int ret = 0;
// struct timeval select_time;
int i = 0;
while(1)
{
//
//先去清空集合
FD_ZERO(&readfds);
//再去添加
FD_SET(sockfd,&readfds);
for(i = 0;i<count;i++)
{
//添加描sockfd述符
FD_SET(all_clifd[i],&readfds);
//求出来最大的
if(all_clifd[i]>maxfd)
{
maxfd = all_clifd[i];
}
}
printf("\n");
//永久阻塞
ret = select(maxfd+1,&readfds,NULL,NULL,NULL);
if(ret == -1)
{
perror("select");
return -1;
}
else if(ret == 0)
{
printf("超时\n");
continue;
}
//有变化,会打断阻塞,将集合里边没有变化的删除掉
else
{
// printf("有文件描述符发生变化\n");
//判断是谁发生的变化,根据变化去作处理
//轮询从0到maxfd所有的文件描述符
for(i = 0;i<=maxfd;i++)
{
//检查发生变化的是否是readfds集合里边的
if(FD_ISSET(i,&readfds)==1)
{
//服务器套接字描述符发生变化
//有客户端请求连接
if(i == sockfd)
{
accept_do_handler(sockfd);
}
//有客户端发消息
else
{
recv_do_handler(i);
}
}
}
}
}
}
else
{
char a;
while(1)
{
printf("y/n\n");
a=getchar();
if(a=='y')
{
printf("1");
kill(pid,10);
exit(0);
}
}
}
}
void accept_do_handler(int sockfd)
{
//4.等待连接
//存放连接成功之后,客户端的IP地址和端口
struct sockaddr_in cliaddr;
memset(&cliaddr,0,sizeof(struct sockaddr_in));
socklen_t len = sizeof(struct sockaddr_in);
int clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
if(clifd == -1)
{
perror("accept");
return;
}
//存储所有的客户端描述符
all_clifd[count] = clifd;
count++;
}
void recv_do_handler(int client_socket)
{
// 5.数据收发
int i = 0;
int ret = 0;
int pos = 0;//记录下表
Msg msg;
memset(&msg,0,sizeof(msg));
//阻塞
ret = recv(client_socket,&msg,sizeof(msg),0);
char shi[1024];
sprintf(shi,"%s %s %s %s",msg.fromname,msg.account,msg.signname,msg.password);
printf("%s\n",shi);
if(ret==-1)
{
perror("recv");
return ;
}
else if(ret == 0)
{
for(i = 0;i<count;i++)
{
if(all_clifd[i] == client_socket)
{
all_clifd[i] = 0;
pos = i;
break;
}
}
for(i = pos;i<count-1;i++)
{
all_clifd[i] = all_clifd[i+1];
}
all_clifd[count-1]=0;
count--;
}
else
{
switch (msg.cmd)
{
case 1 : // 注册
regiss(client_socket, &msg);
break;
case 2 : //登录
ret = entry(client_socket, &msg);
send(client_socket , &msg ,sizeof(Msg),0);
if (ret == 1)
{
//在线人数加1
int i;
for (i=0; i<NOW_MAX; i++)
{
if(user[i].socket == 0)
{
strcpy(user[i].name, msg.fromname);
user[i].socket = client_socket;
user[i].jin=0;
user[i].ti=0;
printf("客户端在线人数加一\n");
break;
}
}
char guanbuf[1000];
sprintf(msg.msg,"%s已登录",msg.fromname);
msg.cmd=22;
//用户界面
for(i=0;i<NOW_MAX;i++)
{
if(user[i].socket == 0)
continue;
if(user[i].socket==client_socket)
continue;
send(user[i].socket,&msg,sizeof(Msg),0);
}
}
break;
case 3://群聊
server_chatall(client_socket, &msg);
server_save(&msg);
break;
case 4 : //私聊
server_chatone(client_socket, &msg);
server_save(&msg);
break;
case 5://退出登录
server_entryout(client_socket,&msg);
sprintf(msg.msg,"%s已下线",msg.fromname);
msg.cmd=22;
for(i=0;i<NOW_MAX;i++)
{
if(user[i].socket == 0)
continue;
if(user[i].socket==client_socket)
continue;
send(user[i].socket,&msg,sizeof(Msg),0);
} break;
case 6://在线人数
see_nowuser(client_socket,&msg);break;
case 7://改签名
server_revise_sign(client_socket, &msg);
break;
case 8://改密码
server_revise_password(client_socket, &msg);
break;
case 9://传输文件
server_transfer_file(client_socket, &msg);
break;
case 10: //接受传输文件
server_transfer_file_y(&msg);
break;
case -10 : //拒绝传输文件
server_transfer_file_n(&msg);
break;
case 11: //一切条件都已成立,直接开始传输
server_start_transfer_file(&msg);break;
case 87://禁言
shutup(client_socket,&msg);break;
case 88://移除
sremove(client_socket,&msg);break;
case 99://注销
server_logout(client_socket,&msg);break;
}
}
}
int regiss(int client_socket, Msg *msg)//注册
{
//printf("nihaos%d\n",__LINE__);
int flag;
int i=-1,j=-1,a=0;
i=search(my_sql,msg);
j=search1(my_sql);
if(j==0)
{
a=1;
}
printf("%d\n",i);
if(i==0)
{ //printf("nihaos%d\n",__LINE__);
flag= data_insert(my_sql,msg,client_socket,a);
msg->cmd+=1000;
}
else if(i==1)
msg->cmd=-1;
printf("%d\n",msg->cmd);
// printf("nihaos%d\n",__LINE__);
send(client_socket,msg,sizeof(Msg),0);
}
int entry(int client_socket, Msg *msg)
{
printf(" %s进行登录\n",msg->fromname);
//用户登录
int flag = Entry_User(my_sql,msg,client_socket);
if (flag == -1)
{
printf("登录失败,系统繁忙\n");
msg->cmd = -1;
return -1;
}
if (flag == -2)
{
printf("登录失败,用户名不存在\n");
msg->cmd = -2;
return -2;
}
if (flag == -3)
{
printf("登录失败,用户已登录\n");
msg->cmd = -3;
return -3;
}
if (flag == -4)
{
printf("登录失败,密码错误\n");
msg->cmd = -4;
return -4;
}
else
{
printf("登录成功\n");
msg->cmd += 1000;
return 1;
}
}
void server_entryout(int client_socket,Msg *msg)//退出登录
{
int ret= change(my_sql,"type","0",msg->account);
//printf("%d\n",client_socket);
if(ret==0)
{
int i ;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket == client_socket)
{
user[i].socket = 0;
printf("客户端在线人数减一\n");
break;
}
}
msg->cmd=5;
sprintf(msg->msg,"退出登录成功");
}
else
{
sprintf(msg->msg,"退出登录失败");
msg->cmd=-5;
}
//printf("%d\n",client_socket);
char shi [1024];
sprintf(shi,"%s %s %s %s %d",msg->fromname,msg->account,msg->signname,msg->password,msg->cmd);
printf("%s\n",shi);
send(client_socket,msg,sizeof(Msg),0);
//printf("fasongcg\n");
}
void server_chatall(int client_socket, Msg * msg)//群聊
{
printf (" %s 进行群发.\n",msg->fromname);
int j;
for (j=0; j<NOW_MAX; j++)
{
if(user[j].socket==client_socket)
{
if(user[j].ti==1)
{
memset(msg->msg,0,1000);
sprintf(msg->msg,"发送失败已被移除群聊");
write(client_socket, msg , sizeof(Msg));
return ;
}
if(user[j].jin==1)
{
memset(msg->msg,0,1000);
sprintf(msg->msg,"发送失败已被禁言");
write(client_socket, msg , sizeof(Msg));
return ;
}
break;
}
}
int i;
for(i=0; i<NOW_MAX; i++)
{
if (user[i].socket != 0&&user[i].socket!=client_socket&&user[i].ti!=1)
{
write(user[i].socket, msg , sizeof(Msg));
}
}
}
void server_chatone(int client_socket, Msg * msg)//私聊
{
printf ("私聊 %s发送信息给%s\n",msg->fromname,msg->localname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
printf("私聊成功\n");
break;
}
}
if (i == NOW_MAX)
{
msg->cmd = -3; //表示私聊失败
write(client_socket, msg , sizeof(Msg));
printf("私聊失败\n");
}
}
void server_logout(int client_socket,Msg *msg)//注销
{
int ret=mysql_delete(my_sql,msg->account);
if(ret==0)
{
printf("注销成功\n");
int i ;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket == client_socket)
{
user[i].socket = 0;
printf("客户端在线人数减一\n");
break;
}
}
msg->cmd=99;
}
else
msg->cmd=-99;
send(client_socket,msg,sizeof(Msg),0);
}
void server_save(Msg*msg)//保存聊天记录
{
char buf2[120];
char ai[1250];
memset(buf2,0,sizeof(buf2));
memset(ai,0,sizeof(ai));
sprintf(buf2,"../serverliaotianjilu/all.txt");
int ps=open(buf2,O_WRONLY|O_CREAT|O_APPEND,0644);
sprintf(ai,"%s->%s:%s\n",msg->fromname,msg->localname,msg->msg);
write(ps,ai,strlen(ai));
close (ps);
}
void see_nowuser(int client_socket, Msg * msg)//在线人员
{
printf("%s 查看当前在线人员\n",msg->fromname);
int i;
int len;
char buf[1024] = {0};
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0)
{
strcat(buf,user[i].name);
len = strlen(buf);
buf[len] = ' ';
}
}
strcpy(msg->msg,buf);
write(client_socket, msg, sizeof(Msg));
printf("查看成功\n");
}
void server_revise_sign(int client_socket, Msg * msg)//修改个性签名
{
printf("%s 修改个性签名\n",msg->fromname);
int ret = change(my_sql,"signname",msg->signname,msg->account); //修改数据库
if (ret == -1)
{
msg->cmd = -7;
printf("%s 修改个性签名失败\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
printf("%s 修改个性签名成功\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
//修改密码
void server_revise_password(int client_socket, Msg * msg)
{
printf("%s 修改密码\n",msg->fromname);
int ret = change(my_sql,"passwd",msg->password,msg->account);
if (ret == -1)
{
msg->cmd = -8;
printf("%s 修改密码失败\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
printf("%s 修改密码成功\n",msg->fromname);
write(client_socket, msg, sizeof(Msg));
}
void shutup(int client_socket, Msg * msg)//禁言
{
int i;
for (i=0; i<NOW_MAX; i++)
{
if(strcmp(user[i].name ,msg->localname)==0)
{
user[i].jin=1;
break;
}
}
write(client_socket, msg, sizeof(Msg));
}
void sremove(int client_socket, Msg * msg)//移除
{
int i;
for (i=0; i<NOW_MAX; i++)
{
if(strcmp(user[i].name ,msg->localname)==0)
{
user[i].ti=1;
break;
}
}
write(client_socket, msg, sizeof(Msg));
}
//传输文件
void server_transfer_file(int client_socket, Msg * msg)
{
printf ("%s 请求发送%s文件给 %s\n",msg->fromname,msg->signname,msg->localname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
printf("发送给%s信息进行判断是否接受\n",msg->localname);
break;
}
}
if (i == NOW_MAX)
{
msg->cmd == -9; //表示传输文件失败
write(client_socket, msg , sizeof(Msg));
printf("发送文件失败,好友不在线或不存在\n");
}
}
void server_transfer_file_y(Msg * msg)//接收文件
{
printf("%s 接受文件传输.\n",msg->fromname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
break;
}
}
}
//拒绝文件
void server_transfer_file_n(Msg * msg)
{
printf("%s 不愿意接受文件传输.\n",msg->fromname);
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg));
break;
}
}
}
void server_start_transfer_file(Msg * msg)
{
//printf("文件传输中\n");
int i;
for(i=0; i<NOW_MAX; i++)
{
if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
{
write(user[i].socket, msg , sizeof(Msg)); //写文件数据
break;
}
}
if(msg->num != 1024)
{
printf("文件传输完成\n");
}
}
数据库端:
1mysql.h
#ifndef _mysql_h_
#define _mysql_h_
#include <stdio.h>
#include <mysql/mysql.h>
#include <string.h>
#include <stdlib.h>
#include "select.h"
#define HOSTNAME "localhost"
#define USERNAME "root"
#define PASSWD "1"
#define DATABASENAME "chatroom"
#define TABLENAME "infor"
void print_all_data(MYSQL *mysql,Msg *msg);
int search(MYSQL *mysql,Msg *msg);
MYSQL * Sconnect();
int data_insert(MYSQL *mysql,Msg *msg,int sockfd,int root);
int Entry_User(MYSQL *con,Msg *msg,int my_sockfd);
int change(MYSQL *my_sql,char *field,char * changenaem,char *naem);
int mysql_delete(MYSQL *my_sql,char *deletename);
int search1(MYSQL *mysql);
#endif
mysql.c
#include "1mysql.h"
#include "select.h"
MYSQL * Sconnect()//连接数据库
{
static MYSQL my_sql;
//0.初始化核心结构体
mysql_init(&my_sql);
//1.连接数据库
//指定使用"hello"数据库
MYSQL*p = mysql_real_connect(&my_sql,HOSTNAME,USERNAME,PASSWD,DATABASENAME,0,NULL,0);
if(p == NULL)
{
// perror("mysql_real_connect");
printf("mysql_error:%s\n",mysql_error(&my_sql));
return NULL;
}
printf("数据库连接成功\n");
return &my_sql;
}
void print_all_data(MYSQL *mysql,Msg *msg)
{
char buf[256];
int ret = 0;
memset(buf,0,sizeof(buf));
sprintf(buf,"SELECT * FROM %s",TABLENAME);
// printf("buf:%s\n",buf);
ret = mysql_real_query(mysql,buf,strlen(buf));
if(ret != 0)
{
printf("mysql_error:%s\n",mysql_error(mysql));
return;
}
// printf("查询数据成功\n");
//获取结果集
MYSQL_RES *pres = mysql_store_result(mysql);
if(pres == NULL)
{
printf("mysql_error:%s\n",mysql_error(mysql));
return;
}
// printf("获取结果集成功\n");
MYSQL_ROW row;
int i,j;
for(i = 0;i<mysql_num_rows(pres);i++)
{
//读取结果集里边的一行数据
row = mysql_fetch_row(pres);
if(row == NULL)
{
printf("mysql_error:%s\n",mysql_error(mysql));
return;
}
for(j=0;j<mysql_num_fields(pres);j++)
{
printf("%s\t",row[j]?row[j]:NULL);
}
printf("\n");
}
//释放结果集
mysql_free_result(pres);
}
int search(MYSQL *mysql,Msg *msg)//查询数据表某一项
{
int ret;
char buf[256];
printf("nihaos\n");
sprintf(buf,"SELECT * FROM %s WHERE account='%s'",TABLENAME,msg->account);
ret = mysql_real_query(mysql,buf,strlen(buf));
MYSQL_RES *pres = mysql_store_result(mysql);
uint64_t rownum = mysql_num_rows(pres);
return rownum;
}
int search1(MYSQL *mysql)//查询数据表
{
int ret;
char buf[256];
printf("nihaos\n");
sprintf(buf,"SELECT * FROM %s ",TABLENAME);
ret = mysql_real_query(mysql,buf,strlen(buf));
MYSQL_RES *pres = mysql_store_result(mysql);
uint64_t rownum = mysql_num_rows(pres);
return rownum;
}
int data_insert(MYSQL *mysql,Msg *msg,int sockfd,int root)//插入数据表
{
char query[1024];
sprintf(query,"insert into %s values('%s','%s','%s','%d','%d','%d','%s')",
TABLENAME ,msg->fromname,msg->account,msg->password,0,sockfd,root,msg->signname);
printf("%s\n",query);
if((mysql_real_query(mysql,query,(unsigned int)strlen(query))) != 0){
printf("insert failed.\n");
return 0;
}
return 1;
}
int Entry_User(MYSQL *con,Msg *msg,int my_socket)//登录
{
char query[1024],shi[1024];
sprintf(query,"SELECT * FROM %s WHERE account = '%s'",TABLENAME,msg->account);
if((mysql_real_query(con,query,(unsigned int)strlen(query))) != 0)
{
printf("insert failed.\n");
return -1;
}
MYSQL_RES *pres = mysql_store_result(con);
uint64_t rownum = mysql_num_rows(pres);
if(rownum==0)
{
return -2;
}
MYSQL_ROW row;
int num_fields=mysql_num_fields(pres);
row=mysql_fetch_row(pres);
if(strcmp(row[3],"1")==0)
{
return -3;
}
if(strcmp(msg->password,row[2])!=0)
{
return -4;
}
char di[10];
sprintf(di,"%d",my_socket);
change(con,"socket",di,msg->account);
change(con,"type","1",msg->account);
strcat(msg->signname,row[6]);
strcat(msg->fromname,row[0]);
if(strcmp(row[5],"1")==0)
msg->num=1;
else
msg->num=0;
return 0;
}
int change(MYSQL *my_sql,char *field,char * changenaem,char *naem)//修改数据表
{
char query[1024];
sprintf(query,"update %s set %s = '%s' where account='%s'",TABLENAME,field,changenaem,naem);
if((mysql_real_query(my_sql,query,(unsigned int) strlen(query)))!=0)
{
printf("insert failed.\n");
return -1;
}
return 0;
}
int mysql_delete(MYSQL *my_sql,char *deletename)//删除数据表
{
char query[1024];
sprintf(query,"delete from %s where account='%s'",TABLENAME,deletename);
if((mysql_real_query(my_sql,query,(unsigned int) strlen(query)))!=0)
{
printf("insert failed.\n");
return -1;
}
return 0;
}
编译客户端:gcc client.c -lmysqlclient -lpthread -o c
编译服务端:gcc server.c mysql.c -lmysqlclient -lpthread -o s
注意:使用mysql要安装相应的库;
安装服务器端
sudo apt-get install mysql-server
安装客户端
sudo apt-get install mysql-client
安装C语言API库
sudo apt-get install libmysqlclient-dev
如果想直接运行代码的话还要在mysql里面添加chatroom数据库,在chatroom库里面添加infor数据表;
步骤1:创建chatroom数据库,代码:create database chatroom;
步骤2:进入chatroom数据库,代码:use chatroom;
步骤3:创建infor数据表,代码:CREATE TABLE infor(name varchar(20),account varchar(20),passwd varchar(20),type int,socket int,root int,signname varchar(100));