一、项目介绍
该项目实现简单的私聊,群聊,聊天记录查看,文件传输的功能,该项目主要涉及到tcp/ip,多路IO复用,多线程,数据库操作等知识。
客户端代码
1.代码解析
这段代码是一个基于TCP的聊天室客户端程序,实现了用户注册、登录、注销、私聊、群聊、文件传输等功能。以下是对代码的解析:
1. 引入头文件:包含了所需的标准库头文件,如`<stdio.h>`、`<sys/types.h>`、`<sys/socket.h>`、`<string.h>`、`<unistd.h>`、`<netinet/in.h>`、`<arpa/inet.h>`、`<pthread.h>`和`<stdlib.h>`。
2. 定义全局变量:包括一个字符数组`line[1024]`用于存储接收到的信息,一个字符指针`M_name`作为全局变量指定自己的用户名,以及一些结构体变量和线程标识符。
3. 定义结构体:包括`struct User_name`用于判断登录信息,`struct User`用于保存用户的信息,`struct Infor`用于接收和发送信息的结构体。
4. 定义函数:包括`receive_inf_1()`和`receive_inf_2()`用于私聊和群聊信息的接收,`pri_chat()`和`gro_chat()`用于私聊和群聊功能的选择,`Pri_record()`和`Group_record()`用于私聊和群聊记录的查看,`outfile()`和`infile()`用于文件传输的功能选择,`send_file()`用于文件传输的操作,`function()`用于功能选择视图,`view()`用于登录功能选择视图,`con_ser()`用于连接服务器,`regist()`用于注册,`login()`用于登录,`logou()`用于注销'。
5. 在`main()`函数中,首先调用`con_ser()`函数连接服务器,然后根据用户的选择调用相应的功能函数。
2.代码实现
这段代码是一个基于TCP协议的聊天室客户端程序。它实现了用户注册、登录、注销、私聊、群聊、发送文件等功能。以下是各个功能的具体实现:
1. 注册功能:用户输入用户名、账号和密码,将信息发送给服务器进行注册。如果注册成功,服务器会返回"1",否则返回其他值。
2. 登录功能:用户输入用户名和密码,将信息发送给服务器进行登录。如果登录成功,服务器会返回"1",否则返回其他值。
3. 注销功能:用户选择注销,客户端向服务器发送注销请求。
4. 私聊功能:用户选择私聊,输入接收方用户名,输入要发送的消息,将消息发送给服务器。服务器收到消息后,将消息保存到对应用户的私聊记录中。
5. 群聊功能:用户选择群聊,输入要发送的消息,将消息发送给服务器。服务器收到消息后,将消息保存到对应用户的群聊记录中。
6. 发送文件功能:用户选择发送文件,可以选择发送文件或接收文件。发送文件时,用户选择发送文件,输入接收方用户名,选择要发送的文件,将文件发送给服务器。接收文件时,用户选择接收文件,服务器将文件发送给客户端,客户端将文件保存到本地。
7. 查看记录功能:用户选择查看记录,可以选择查看私聊记录或群聊记录。查看记录时,客户端从服务器获取记录,并显示在屏幕上。
8. 退出功能:用户选择退出,客户端向服务器发送退出请求,然后关闭与服务器的连接。
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
char line[1024]={0};
char *M_name=NULL; //定义全局变量指针指定自己的用户名
pthread_t pd1,pd2; //创建线程读信息的id
struct User_name{ //用于判断登录信息
char name[50];
int flag;
};
struct User{ //用于保存用户的信息
char name[50];
char account[24];
char password[10];
int flag; //用于服务器端进行功能判断 1、注册 ,2、登录,3、注销
};
int cd; //客户端套接字
//struct sockaddr_in cliaddr;
struct sockaddr_in seraddr;
//收发信息的结构体
struct Infor{
int flag; //用于功能选择 1、私聊 ,2、群聊
char mname[50]; //发送方用户名
char yname[50]; //接收方用户名
char buff[1024];//发送的信息
};
void save_chat(FILE *fp,struct Infor in)
{
char lin[1096]={0};
memset(lin,0,sizeof(lin));
sprintf(lin,"%s:%s\n",in.yname,in.buff);
fputs(lin,fp);
fclose(fp);
}
void pri(FILE *fp)
{
memset(line,0,sizeof(line));
while(fgets(line,1024,fp))
{
printf("%s",line);
memset(line,0,sizeof(line));
}
fclose(fp);
}
//私聊收取信息
void *receive_inf_1()
{
int num;
struct Infor in;
while(1)
{
memset(&in,0,sizeof(in));
num=read(cd,&in,sizeof(in));
if(num==0)
{
printf("服务器关闭\n");
return NULL;
}
if(in.flag==1)
{
FILE *fp=fopen("./Pri_Chat.txt","a+");
save_chat(fp,in);
printf("%s:%s\n",in.yname,in.buff);
continue;
}else if(in.flag==2)
{
FILE *fp=fopen("./Group_Chat.txt","a+");
FILE *gfp=fopen("./Gro_tem.txt","a+");
save_chat(gfp,in);
save_chat(fp,in);
}
}
}
//群聊收取信息
void *receive_inf_2()
{
int num;
struct Infor in;
while(1)
{
memset(&in,0,sizeof(in));
num=read(cd,&in,sizeof(in));
if(num==0)
{
printf("服务器关闭\n");
return NULL;
}
if(in.flag==2)
{
FILE *fp=fopen("./Group_Chat.txt","a+");
save_chat(fp,in);
printf("%s:%s\n",in.yname,in.buff);
continue;
}else if(in.flag==1)
{
FILE *fp=fopen("./Pri_Chat.txt","a+");
FILE *pfp=fopen("./Pri_tem.txt","a+");
save_chat(pfp,in);
save_chat(fp,in);
}
}
}
//私聊
void pri_chat()
{
FILE *pfp=fopen("./Pri_tem.txt","r");
if(pfp!=NULL)
{
pri(pfp);
remove("./Pri_tem.txt");
}
char na_me[50]={0};
struct Infor out;
pthread_create(&pd1,NULL,receive_inf_1,NULL);
printf("请输入私聊的用户名\n");
scanf("%s",na_me);
getchar();
printf("输入quit退出\n");
while(1)
{
memset(&out,0,sizeof(out));
sprintf(out.yname,"%s",na_me);
scanf("%s",out.buff);
getchar();
sprintf(out.mname,"%s",M_name);
out.flag=1;
if(strcmp(out.buff,"quit")==0)
{
pthread_cancel(pd1);
return;
}
FILE *fp=fopen("./Pri_Chat.txt","a+");
save_chat(fp,out);
write(cd,&out,sizeof(out));
}
}
//群聊
void gro_chat()
{
FILE *gfp=fopen("./Gro_tem.txt","r");
if(gfp!=NULL)
{
pri(gfp);
remove("./Gro_tem.txt");
}
struct Infor out;
pthread_create(&pd2,NULL,receive_inf_2,NULL);
printf("输入quit退出\n");
while(1)
{
memset(&out,0,sizeof(out));
sprintf(out.mname,"%s",M_name);
scanf("%s",out.buff);
getchar();
out.flag=2;
if(strcmp(out.buff,"quit")==0)
{
pthread_cancel(pd2);
return;
}
FILE *fp=fopen("./Group_Chat.txt","a+");
save_chat(fp,out);
write(cd,&out,sizeof(out));
}
}
//私聊记录
void Pri_record()
{
FILE *fp=fopen("./Pri_Chat.txt","a+");
pri(fp);
}
//群聊记录
void Group_record()
{
FILE *fp=fopen("./Group_Chat.txt","a+");
pri(fp);
}
void outfile()
{
struct Infor outfile;
struct Infor in;
char filename[20]={0};
char name[50]={0};
printf("请输入你要发送的文件路径\n");
scanf("%s",filename);
getchar();
FILE * fp=NULL;
fp=fopen(filename,"r");
if(fp==NULL)
{
printf("该文件不存在\n");
return;
}
printf("请输入要接收文件的用户名\n");
scanf("%s",name);
getchar();
memset(line,0,sizeof(line));
memset(&in,0,sizeof(in));
while(fgets(line,1024,fp))
{
sprintf(outfile.buff,"%s",line);
outfile.flag=5;
sprintf(outfile.mname,"%s",filename);
sprintf(outfile.yname,"%s",name);
write(cd,&outfile,sizeof(outfile));
read(cd,&in,sizeof(in));
if(in.flag==0)
{
printf("%s\n",in.buff);
fclose(fp);
return;
}
memset(&outfile,0,sizeof(outfile));
memset(line,0,sizeof(line));
}
fclose(fp);
sprintf(outfile.yname,"%s",name);
outfile.flag=10;
write(cd,&outfile,sizeof(outfile));
printf("发送完成\n");
}
void infile()
{
struct Infor in;
FILE *fp=NULL;
char filename[50]={0};
while(1)
{
read(cd,&in,sizeof(in));
if(in.flag==10)
{
printf("接收成功\n");
return;
}
sprintf(filename,"%s",in.mname);
fp=fopen(filename,"a+");
// fp=fopen("w.txt","a+");
memset(line,0,sizeof(line));
sprintf(line,"%s",in.buff);
fputs(line,fp);
fclose(fp);
}
}
void send_file()
{
char flag;
printf("选择功能\n");
printf("1、发文件\n");
printf("2、收文件\n");
scanf("%c",&flag);
getchar();
switch(flag)
{
case '1':
outfile();
return;
case '2':
infile();
return;
}
}
//功能选择
void function()
{
char flag;
while(1)
{
printf("\n********************\n");
printf("* 1、私聊 *\n");
printf("* 2、群聊 *\n");
printf("* 3、私聊记录 *\n");
printf("* 4、群聊记录 *\n");
printf("* 5、文件传输 *\n");
printf("* 0、退出 *\n");
printf("********************\n\n");
printf("请选择功能\n");
scanf("%c",&flag);
getchar();
switch(flag)
{
case '1':
pri_chat();
pthread_join(pd1,NULL);
break;
case '2':
gro_chat();
pthread_join(pd2,NULL);
break;
case '3':
Pri_record();
break;
case '4':
Group_record();
break;
case '5':
send_file();
break;
case '0':
return;
}
}
}
//登录功能选择视图
void view()
{
printf("********************\n");
printf("*欢迎来到网络聊天室*\n");
printf("********************\n");
printf("* 1、注册 *\n");
printf("* 2、登录 *\n");
printf("* 3、注销 *\n");
printf("* 4、退出 *\n");
printf("********************\n");
printf("* 请输入选项: *\n");
}
//连接服务器
int con_ser()
{
int flag;
cd=socket(AF_INET,SOCK_STREAM,0);
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(55555);
seraddr.sin_addr.s_addr = inet_addr("172.20.10.3");
flag=connect(cd, (struct sockaddr*)&seraddr, sizeof(seraddr));
if(flag==0)
{
return 1;
}
printf("连接服务器失败!\n");
return 0;
}
//注册
void regist()
{
char flag[2];
struct User user;
printf("请输入用户名:");
scanf("%s",user.name);
getchar();
printf("请输入账号:");
scanf("%s",user.account);
getchar();
printf("请输入密码:");
scanf("%s",user.password);
getchar();
user.flag=1;
write(cd,&user,sizeof(struct User));
read(cd,flag,2);
printf("\n********************\n");
if(strcmp("1",flag)==0)
{
printf("* 注册成功 *\n");
}else{
printf("用户名已存在\n");
}
printf("********************\n\n");
}
//登录
void login()
{
struct User_name u_name;
struct User user;
printf("请输入账号:");
scanf("%s",user.account);
getchar();
printf("请输入密码:");
scanf("%s",user.password);
getchar();
user.flag=2;
write(cd,&user,sizeof(struct User));
read(cd,&u_name,sizeof(u_name));
printf("\n********************\n");
if(u_name.flag==1)
{
printf("* 登录成功 *\n");
printf(" 欢迎登录:%s \n",u_name.name);
M_name=u_name.name;
printf("********************\n\n");
function();
}else{
printf("* 登录失败 *\n");
printf("********************\n\n");
}
}
//注销
void logout()
{
char flag[2];
struct User user;
printf("请输入要注销的账号:");
scanf("%s",user.account);
getchar();
printf("请输入密码:");
scanf("%s",user.password);
getchar();
user.flag=3;
write(cd,&user,sizeof(user));
read(cd,flag,2);
printf("\n********************\n");
if(strcmp("1",flag)==0)
{
printf("* 注销成功 *\n");
}else{
printf("* 账号不存在 *\n");
}
printf("********************\n\n");
}
int main()
{
if(con_ser()==0)
{
return -1;
}
while(1)
{
char flag;
view();
scanf("%c",&flag);
getchar();
switch(flag)
{
case '1':
regist();
break;
case '2':
login();
break;
case '3':
logout();
break;
case '4':
return 0;
default:
break;
}
}
return 0;
}
服务端代码
1.代码解析
这段代码是一个基于TCP的聊天服务器程序,主要功能包括用户注册、登录、注销、私聊、群聊和文件发送。以下是对代码的逐行解释:
1. 定义了一些结构体,如User_name(用于判断登录信息)、User(保存用户的信息)、Infor(收发信息的结构体)、cd_name(储存客户端的信息)和c_n(储存多个客户端的信息)。
2. 定义了一些全局变量,如query(用于储存sql语句)、sd、cd、read_num、seraddr等。
3. 定义了一个函数con_data(),用于连接数据库。
4. 定义了一个函数regist(),用于注册新用户。
5. 定义了一个函数login(),用于用户登录。
6. 定义了一个函数logout(),用于用户注销。
7. 定义了一个函数pri_chat(),用于私聊功能。
8. 定义了一个函数gro_chat(),用于群聊功能。
9. 定义了一个函数send_file(),用于发送文件功能。
10. 定义了一个函数send_ok(),用于接收成功消息。
11. 定义了一个函数login_ver(),用于处理客户端的请求。
12. 在main()函数中,首先调用con_data()函数连接数据库,然后调用login_ver()函数启动服务器。
整体来说,这段代码实现了一个简单的聊天服务器,可以处理用户的注册、登录、注销、私聊、群聊和文件发送等功能。
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<mysql/mysql.h>
#include <sys/epoll.h>
struct User_name{ //用于判断登录信息
char name[50];
int flag;
};
//用于保存用户的信息
struct User{
char name[50];
char account[24];
char password[10];
int flag;
};
//收发信息的结构体
struct Infor{
int flag;
char mname[50];
char yname[50];
char buff[1024];
};
char query[255]={0};//用于储存sql语句
int sd,cd,read_num;
struct sockaddr_in seraddr;
int c_num=0;
MYSQL ret;
MYSQL_RES * res=NULL;
MYSQL_ROW myrow;
struct cd_name{ //储存客户端的信息
int cid;
char name[50];
};
struct cd_name c_n[20];
//连接数据库
MYSQL *con_data()
{
MYSQL *data=NULL;
data=mysql_init(&ret);
data=mysql_real_connect(data,"localhost","root","root","fwq",0,0,0);
return data;
}
//注册
void regist(MYSQL *data,struct User user,int m_cd,struct sockaddr_in cli_addr)
{
memset(query,0,sizeof(query));
sprintf(query,"select * from users where name='%s' or account='%s';",user.name,user.account);
mysql_query(data,query);
res=mysql_store_result(data);
if(mysql_num_rows(res)==0)
{
mysql_free_result(res);
memset(query,0,sizeof(query));
sprintf(query,"insert into users(name,account,password,ip) values('%s','%s','%s','%s');",user.name,\
user.account,user.password,inet_ntoa(cli_addr.sin_addr));
mysql_query(data,query);
res=mysql_store_result(data);
mysql_free_result(res);
write(m_cd,"1",2);
}else{
write(m_cd,"0",2);
}
}
//登录
void login(MYSQL *data,struct User user,int m_cd,struct sockaddr_in cli_addr)
{
struct User_name u_name;
memset(query,0,sizeof(query));
sprintf(query,"select name from users where account='%s' and password='%s';",user.account,user.password);
mysql_query(data,query);
res=mysql_store_result(data);
if(mysql_num_rows(res)==0)
{
u_name.flag=0;
mysql_free_result(res);
write(m_cd,&u_name,sizeof(u_name));
}else{
myrow=mysql_fetch_row(res);
sprintf(u_name.name,"%s",myrow[0]);
for(int m=0;m<c_num;m++)
{
if(c_n[m].cid==m_cd)
{
sprintf(c_n[m].name,"%s",u_name.name);
}
}
u_name.flag=1;
mysql_free_result(res);
write(m_cd,&u_name,sizeof(u_name));
memset(query,0,sizeof(query));
sprintf(query,"update users set ip='%s' where account='%s' and password='%s';",\
inet_ntoa(cli_addr.sin_addr),user.account,user.password);
mysql_query(data,query);
}
}
//注销
void logout(MYSQL *data,struct User user,int m_cd)
{
memset(query,0,sizeof(query));
sprintf(query,"select name from users where account='%s' and password='%s';",user.account,user.password);
mysql_query(data,query);
res=mysql_store_result(data);
if(mysql_num_rows(res)==0)
{
mysql_free_result(res);
write(m_cd,"0",2);
}
else
{
mysql_free_result(res);
memset(query,0,sizeof(query));
sprintf(query,"delete from users where account='%s' and password='%s';",user.account,user.password);
mysql_query(data,query);
write(m_cd,"1",2);
}
}
//私聊
void pri_chat(struct Infor outfor,int m_cd)
{
int flag=0;
int n=0;
for(n;n<c_num;n++)
{
if(strcmp(outfor.yname,c_n[n].name)==0)
{
if(c_n[n].cid!=0)
{
flag=c_n[n].cid;
sprintf(outfor.yname,"%s",outfor.mname);
write(c_n[n].cid,&outfor,sizeof(struct Infor));
return;
}
}
}
if(flag==0)
{
memset(&outfor,0,sizeof(outfor));
outfor.flag=1;
sprintf(outfor.buff,"对方不在线,请手动退出");
write(m_cd,&outfor,sizeof(struct Infor));
}
}
//群聊
void gro_chat(struct Infor outfor,int m_cd)
{
int n=0;
for(n;n<c_num;n++)
{
if(c_n[n].cid!=0&&c_n[n].cid!=m_cd)
{
sprintf(outfor.yname,"%s",outfor.mname);
write(c_n[n].cid,&outfor,sizeof(struct Infor));
}
}
}
//发送文件
void send_file(struct Infor outfor,int m_cd)
{
int flag=0;
int n=0;
for(n;n<c_num;n++)
{
if(strcmp(outfor.yname,c_n[n].name)==0)
{
if(c_n[n].cid!=0)
{
flag=c_n[n].cid;
write(c_n[n].cid,&outfor,sizeof(struct Infor));
memset(&outfor,0,sizeof(outfor));
outfor.flag=1;
write(m_cd,&outfor,sizeof(struct Infor));
return;
}
}
}
if(flag==0)
{
memset(&outfor,0,sizeof(outfor));
outfor.flag=0;
sprintf(outfor.buff,"对方不在线");
write(m_cd,&outfor,sizeof(struct Infor));
}
}
//接收成功
void send_ok(struct Infor outfor,int m_cd)
{
int n=0;
for(n;n<c_num;n++)
{
if(strcmp(outfor.yname,c_n[n].name)==0)
{
if(c_n[n].cid!=0)
{
write(c_n[n].cid,&outfor,sizeof(struct Infor));
memset(&outfor,0,sizeof(outfor));
outfor.flag=10;
return;
}
}
}
}
void login_ver(MYSQL *data)
{
//创建TCP网络通信
sd=socket(AF_INET,SOCK_STREAM,0);
int count,flag=1;
//开启多路IO
setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(55555);
seraddr.sin_addr.s_addr=inet_addr("172.20.10.3");
bind(sd,(struct sockaddr *)&seraddr,sizeof(seraddr));
listen(sd,10);
int pol,i;
struct epoll_event ev;
pol=epoll_create(1);
ev.events=EPOLLIN;
ev.data.fd=sd;
epoll_ctl(pol,EPOLL_CTL_ADD,sd,&ev);
struct epoll_event myepoll[10];
struct User user;
struct Infor infor;
socklen_t len;
struct sockaddr_in cli_addr[10];
while(1)
{
count=epoll_wait(pol,myepoll,10,-1);
for(i=0;i<count;i++)
{
if(myepoll[i].events==EPOLLIN)
{
struct sockaddr_in cliaddr;
len=sizeof(cliaddr);
MYSQL_RES * res=NULL;
MYSQL_ROW myrow;
if(myepoll[i].data.fd==sd)
{
cd=accept(sd,(struct sockaddr*)&cliaddr,&len);
c_n[c_num].cid=cd;
c_num++;
ev.events=EPOLLIN;
ev.data.fd=cd;
cli_addr[cd]=cliaddr;
epoll_ctl(pol,EPOLL_CTL_ADD,cd,&ev);
}
else
{
char buffer[1280] = {0};
if((read_num=read(myepoll[i].data.fd,buffer,sizeof(buffer)))==0)
{
for(int p=0;p<c_num;p++)
{
if(myepoll[i].data.fd==c_n[p].cid)
{
c_n[p].cid=0;
}
}
printf("已下线\n");//用户已下线
close(myepoll[i].data.fd);
epoll_ctl(pol,EPOLL_CTL_DEL,myepoll[i].data.fd,NULL);
}
else
{
if(read_num==sizeof(struct User))
{
memcpy(&user, buffer, sizeof(struct User));
switch(user.flag)
{
case 1:
regist(data,user,myepoll[i].data.fd,cli_addr[myepoll[i].data.fd]);
break;
case 2:
login(data,user,myepoll[i].data.fd,cli_addr[myepoll[i].data.fd]);
break;
case 3:
logout(data,user,myepoll[i].data.fd);
break;
}
}
else if(read_num==sizeof(struct Infor))
{
memcpy(&infor, buffer, sizeof(struct Infor));
switch(infor.flag)
{
case 1:
pri_chat(infor,myepoll[i].data.fd);
break;
case 2:
gro_chat(infor,myepoll[i].data.fd);
break;
case 5:
send_file(infor,myepoll[i].data.fd);
break;
case 10:
send_ok(infor,myepoll[i].data.fd);
break;
}
}
}
}
}
}
}
}
int main()
{
MYSQL *data=NULL;
data=con_data();
login_ver(data);
close(sd);
return 0;
}