基于UDP的网络聊天室
项目需求:
1.如果有用户登录,其他用户可以收到这个人的登录信息
2.如果有人发送信息,其他用户可以收到这个人的群聊信息
3.如果有人下线,其他用户可以收到这个人的下线信息
4.服务器可以发送系统信息
网络聊天室---------服务器
#include "headea.h"
#define SER_IP "192.168.126.27"
#define SER_PORT 8888
int main(int argc, const char *argv[])
{
//1.创建结构体
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1){
perror("socket");
return -1;
}
printf("socket success\n");
//2.设置端口号快速重启
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){
perror("setsockopt");
return -1;
}
printf("setsockopt success\n");
//3.绑定IP和端口号
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("bind");
return -1;
}
printf("bind success\n");
//4.数据交互
struct sockaddr_in cin; //客户端发送过来的地址
socklen_t cinlen=sizeof(cin);
char cbuf[255];
//创建用户地址结构体
int maxi=0;
struct sockaddr_in rin[100]; //创建用户地址结构体数组
//创建用户信息结构体
Msg_t* H=Head_creat(); //创建头结点
//IO多路复用
struct pollfd pfds[2];
pfds[0].fd=0;
pfds[0].events=POLLIN;
pfds[1].fd=sfd;
pfds[1].events=POLLIN;
char name[20];
char passwd[20];
while(1){
int res=poll(pfds,2,-1);
if(res==0){
printf("timr out\n");
return 0;
}
if(res==-1){
perror("poll");
return -1;
}
//执行到此处说明接发信息被触发
if(pfds[1].revents==POLLIN){
bzero(cbuf,sizeof(cbuf));
recvfrom(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,&cinlen);
char* p1=cbuf;
char cfs[211]="";
strcpy(cfs,p1+41);
// printf("cbuf:%s\ncfs:%s\n",p1+41,cfs);
if(cbuf[0]=='Z'){ //表示用户注册
bzero(name,sizeof(name));
bzero(passwd,sizeof(passwd));
char* p1=cbuf;
strncpy(name,p1+1,20);
strncpy(passwd,p1+21,20);
//
printf("注册用户信息%s||%s\n",name,passwd); //*********测试用
//
if(find_node(H,name,passwd)==0){ //如果用户列表不存在则允许创建
tail_inst(H,name,passwd); //用户信息存入链表
bzero(cbuf,sizeof(cbuf));
cbuf[0]='Y';
strcpy(p1+41,"注册成功,欢迎登录聊天系统"); //将成功信息返回客户端
// printf("rin[maxi].sin_port=%hd ,rin[maxi].sin_addr.s_addr=%s\n",ntohs(rin[maxi-1].sin_port),inet_ntoa(rin[maxi-1].sin_addr));
// printf("cin.sin_port=%hd ,cin.sin_addr.s_addr=%s\n",ntohs(cin.sin_port),inet_ntoa(cin.sin_addr));
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,sizeof(cin));
}else if(find_node(H,name,passwd)!=0){ //用户已存在并通知客户端
bzero(cbuf,sizeof(cbuf));
cbuf[0]='N';
strcpy(p1+41,"用户已存在,请重新输入"); //将失败信息返回客户端
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,sizeof(cin));
}
}
else if(cbuf[0]=='L'){ //表示用户登录
bzero(name,sizeof(name));
bzero(passwd,sizeof(passwd));
char* p1=cbuf;
strncpy(name,p1+1,20);
strncpy(passwd,p1+21,20);
int res=find_node(H,name,passwd);
if(res==1){
if(maxi>1024){ //判断登录用户是否过载
cbuf[0]='N';
strcpy(p1+41,"登录用户已满,请等待后重试\n");
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,sizeof(cin));
break;
}
rin[maxi]=cin; //*************************将登录信息存入注册数组中
maxi++;
/* int i=0;
while(1){
if(rin[i].sin_addr.s_addr=='\0'){
rin[i]=cin;
break;
}
i++; //最小未使用找出i
}
if(maxi<i){
maxi=i; //留下最大已使用的下标
printf("maxi=%d\n",maxi); ********测试用(暂时弃用)
}*/
printf("%s:已登录\n",name); //给服务端发送提示
cbuf[0]='Y';
strcpy(p1+41,"已登录"); //将上线信息发送给所有用户
for(int j=0;j<maxi;j++){
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&rin[j],sizeof(rin[j]));
}
}else if(res==2){ //密码错误,用户重新输入
cbuf[0]='N';
strcpy(p1+41,"密码错误,请重试\n");
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,sizeof(cin));
}else if(res==0){
cbuf[0]='N';
strcpy(p1+41,"暂无该用户,请注册\n");
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&cin,sizeof(cin));
}
}
else if(cbuf[0]=='C'){ //表示用户聊天
char* p1=cbuf;
bzero(name,sizeof(name));
cbuf[0]='Y';
strncpy(name,p1+1,20);
for(int j=0;j<maxi;j++){
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&rin[j],sizeof(rin[j]));
printf("rin[%d].sin_port=%d ,rin[maxi].sin_addr.s_addr=%s\n",j,ntohs(rin[j].sin_port),inet_ntoa(rin[j].sin_addr));
//printf("cin.sin_port=%d ,cin.sin_addr.s_addr=%s\n",ntohs(cin.sin_port),inet_ntoa(cin.sin_addr));
}
printf("用户%s信息发送成功\n",name);
}
else if(cbuf[0]=='Q'){ //表示用户退出
cbuf[0]='Y';
char* p1=cbuf;
strcpy(p1+41,"已下线");
//定义结构体rin[n],将下线信息发送给其他客户
for(int j=0;j<=maxi;j++){
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&rin[j],sizeof(rin[j]));
}
}
printf("ss:%s\n",cbuf);
}
if(pfds[0].revents==POLLIN){ //向所有客户端发送通知
bzero(cbuf,sizeof(cbuf));
char* p1=cbuf;
char buf[211]="";
cbuf[0]='Y';
strcpy(p1+1,"Server:");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
strcpy(p1+41,buf);
// show(H); //********测试用
for(int j=0;j<maxi;j++){
sendto(sfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&rin[j],sizeof(rin[j]));
// printf("rin[%d].sin_port=%d ,rin[maxi].sin_addr.s_addr=%s\n",j,ntohs(rin[maxi-1].sin_port),inet_ntoa(rin[maxi-1].sin_addr));
// printf("客户端说了\n");
}
}
}
//5.关闭文件
close(sfd);
return 0;
}
网络聊天室------结构体部分
#include "header.h"
//创建头结点
Msg_t* Head_creat(){
Msg_t* H=(Msg_t*)malloc(sizeof(Msg_t));
if(H==NULL){
perror("malloc");
return NULL;
}
H->next=NULL;
return H;
}
//写入子节点
Msg_t* node_creat(const char* name,const char* passwd){
Msg_t* new=(Msg_t*)malloc(sizeof(Msg_t));
if(new==NULL){
printf("子节点创建有问题\n");
return NULL;
}
new->next=NULL;
strcpy(new->name,name);
strcpy(new->passwd,passwd);
return new;
}
//尾插创建子节点
void tail_inst(Msg_t* H,const char* name,const char* passwd){
if(H==NULL){
printf("入参错误,请检查\n");
return;
}
Msg_t* P=H;
while(P->next!=NULL){
P=P->next;
}
Msg_t* new=node_creat(name,passwd);
P->next=new;
printf("用户%s数据存储成功\n",name);
}
//遍历删除子节点
void del_pos_node(Msg_t* H,const char* name){
if(H==NULL){
printf("入参错误,请检查\n");
return;
}
Msg_t* P=H;
while(P->next!=NULL){
if(strcmp(P->next->name,name)==0){
Msg_t* q=P->next;
P->next=q->next;
free(q);
break;
}
P=P->next;
}
printf("用户%s数据删除\n",name);
}
//遍历查询相同子节点
int find_node(Msg_t* H,const char* name,const char* passwd){
if(H==NULL){
printf("入参错误,请检查\n");
return -1;
}
int num=0;
Msg_t* P=H->next;
while(P!=NULL){
if(strcmp(P->name,name)==0){
if(strcmp(passwd,P->passwd)==0){
num=1;
}else{
num=2;
}
}
P=P->next;
}
// printf("num=%d\n",num); //*********测试用
return num;
}
void show(Msg_t* H){
Msg_t* P=H->next;
while(P!=NULL){
printf("%s->",P->name);
P=P->next;
}
printf("结束show\n");
putchar(10);
}
网路聊天室------客户端
#include <header.h>
int main(int argc, const char *argv[])
{
//1.输入要连接的端口号
char ip[30]="";
short pt=0;
printf("请输入IP地址: ");
scanf("%s",ip);
while(getchar()!=10);
printf("请输入端口号: ");
scanf("%hd",&pt);
while(getchar()!=10);
//2.创建套接字
int cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd==-1){
perror("socket");
return -1;
}
printf("socket success\n");
//3.设置端口号快速重启
int reuse=1;
if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){
perror("socketopt");
return -1;
}
printf("set setsockopt success\n");
printf("登陆成功,请稍后\n");
sleep(1);
//循环执行功能界面
//准备变量、结构体
struct sockaddr_in cin;
socklen_t cinlen=sizeof(cinlen);
char cbuf[255];
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(pt);
sin.sin_addr.s_addr=inet_addr(ip);
//使用poll完成IO多路复用
//
//数据交互
struct pollfd pfd[2];
pfd[0].fd=0;
pfd[1].fd=cfd;
pfd[0].events=POLLIN;
pfd[1].events=POLLIN;
char name[20];
char name1[20];
char passwd[20];
char buf[211];
char cfs[211];
char* p1=cbuf;
printf("欢迎加入网络聊天室\n");
while(1){
printf("=======1.登录========\n");
printf("=======2.注册========\n");
printf("=======0.退出========\n");
int num;
scanf("%d",&num);
while(getchar()!=10);
switch(num){
case 1: //登录、进入聊天界面
{
bzero(name,sizeof(name));
bzero(passwd,sizeof(passwd));
bzero(cbuf,sizeof(cbuf));
bzero(buf,sizeof(buf));
printf("请输入用户名:");
scanf("%s",name);
strcpy(name1,name);
while(getchar()!=10);
printf("请输入密码:");
scanf("%s",passwd);
while(getchar()!=10);
cbuf[0]='L';
strncpy(p1+1,name,20);
strncpy(p1+21,passwd,20); //将账户密码输入并等待服务端会话
sendto(cfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&sin,sizeof(sin));
bzero(cbuf,sizeof(cbuf));
recvfrom(cfd,cbuf,sizeof(cbuf),0,NULL,NULL);
if(cbuf[0]=='N'){
printf("错误:%s\n",p1+41);
break;
}
printf("登陆成功,欢迎\n");
while(1){
int res=poll(pfd,2,-1);
if(res==0){
printf("time out\n");
return -1;
}else if(res==-1){
perror("poll");
return -1;
} //poll设立完成
char cfs[211]="";
if(pfd[0].revents==POLLIN){ //将消息发送给服务器
//*****
bzero(cbuf,sizeof(cbuf));
bzero(cfs,sizeof(cfs));
cbuf[0]='C';
strncpy(p1+1,name1,20);
//*****
fgets(cfs,sizeof(cfs),stdin);
// printf("cfs:%s\n",cfs);
cfs[strlen(cfs)-1]='\0';
strcpy(p1+41,cfs);
// printf("cbuf:%s\n",p1+41);
if(strcmp(cfs,"quit")==0){
bzero(cbuf,sizeof(cbuf));
cbuf[0]='Q';
strcpy(p1+1,name1);
sendto(cfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&sin,sizeof(sin));
close(cfd);
goto END;
}
sendto(cfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&sin,sizeof(sin));
// printf("发送成功\n");
}
if(pfd[1].revents==POLLIN){ //将从服务器接收到的信息发送给终端
bzero(cbuf,sizeof(cbuf));
bzero(name,sizeof(name));
bzero(buf,sizeof(buf));
recvfrom(cfd,cbuf,sizeof(cbuf),0,NULL,NULL);
//char* p1=cbuf;
//char name[20]="";
strncpy(name,p1+1,20);
strncpy(buf,p1+41,211);
printf("%s:%s\n",name,buf);
// printf("接收成功\n"); //***********测试用
}
}
}
END: break;
case 2: //注册
{
bzero(name,sizeof(name));
bzero(passwd,sizeof(passwd));
bzero(cbuf,sizeof(cbuf));
printf("请输入注册名:");
scanf("%20s",name);
while(getchar()!=10);
printf("请输入密码:");
scanf("%20s",passwd);
while(getchar()!=10);
cbuf[0]='Z';
strncpy(p1+1,name,20);
strncpy(p1+21,passwd,20);
sendto(cfd,cbuf,sizeof(cbuf),0,(struct sockaddr*)&sin,sizeof(sin));
bzero(cbuf,sizeof(cbuf));
recvfrom(cfd,cbuf,sizeof(cbuf),0,NULL,NULL);
if(cbuf[0]=='N'){
printf("错误:%s\n",p1+41);
break;
}else if(cbuf[0]='Y'){
printf("%s\n",p1+41);
}
}
break;
case 0: //退出客户端
{
printf("已退出聊天系统\n");
return 0;
}
break;
}
}
}