服务器端
#include <myhd.h>
//传输结构体
struct cli
{
char type; // L/C/Q
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
if(argc!=3)
{
printf("请输入ip地址和端口号\n");
return -1;
}
//1、创建用于通信的套接字文件描述符
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
printf("socket success sfd=%d\n",sfd);
//设置端口号快速重用
int reuse=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("设置端口重用成功\n");
//2、绑定
//2.1、填充服务器信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(atoi(argv[2]));
sin.sin_addr.s_addr=inet_addr(argv[1]);
//2.2、绑定工作
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//填充客户端信息结构体
struct sockaddr_in cin[1024];
struct sockaddr_in cintemp;
cintemp.sin_family=AF_INET;
socklen_t socklen=sizeof(cintemp);
//定义用于检测文件描述符的集合
fd_set readfds,tempfds;
//清空容器中的内容
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合中
FD_SET(sfd,&readfds);
FD_SET(0,&readfds);
//用于存放客户昵称等信息
struct cli msg[1024];
struct cli temp;
//清空结构体数组
for(int i=0;i<1024;i++)
{
bzero(msg[i].name,sizeof(msg[0].name));
bzero(msg[i].text,sizeof(msg[0].text));
}
char buf[128]="";
int res=0; //接收select的返回值
int maxi=-1; //群聊成员个数
while(1)
{
//复制一份
tempfds=readfds;
//使用select阻塞等待集合中的文件描述符
res=select(sfd+1,&tempfds,NULL,NULL,NULL);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("time out\n");
return -1;
}
if(FD_ISSET(sfd,&tempfds))
{
//清空内容
temp.type=0;
bzero(temp.name,sizeof(temp.name));
bzero(temp.text,sizeof(temp.text));
recvfrom(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cintemp,&socklen);
if(temp.type=='l'||temp.type=='L') //登录
{
printf("%s[%s:%d]:登录成功\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
maxi++;
strcpy(msg[maxi].name,temp.name);
cin[maxi].sin_port=cintemp.sin_port;
cin[maxi].sin_addr.s_addr=cintemp.sin_addr.s_addr;
for(int j=0;j<=maxi;j++)
{
if(cintemp.sin_port==cin[j].sin_port)
{
continue;
}
sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[j],sizeof(cin[j]));
}
}
if(temp.type=='c'||temp.type=='C') //聊天
{
printf("%s发送了一条消息\n",temp.name);
for(int j=0;j<=maxi;j++)
{
if(cintemp.sin_port==cin[j].sin_port)
{
continue;
}
sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[j],sizeof(cin[j]));
}
}
if(temp.type=='q'||temp.type=='Q') //退出协议
{
int i=0;
if(strcmp(temp.text,"quit")==0)
{
strcpy(temp.name,msg[i].name);
}
printf("%s[%s:%d]:已离线\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
for(i=0;i<=maxi;i++)
{
if(cintemp.sin_port==cin[i].sin_port)
{
int t=i;
for(int j=maxi;j>=i;j--)
{
cin[t]=cin[t+1];
msg[t]=msg[t+1];
t++;
}
maxi--;
}
sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[i],sizeof(cin[i]));
}
}
}
if(FD_ISSET(0,&tempfds))
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strcmp(buf,"quit")==0)
{
close(sfd);
return 0;
}
temp.type='C';
strcpy(temp.text,buf);
strcpy(temp.name,"***system***");
for(int i=0;i<=maxi;i++)
{
sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[i],sizeof(cin[i]));
}
}
}
//5、关闭套接字文件描述符
close(sfd);
return 0;
}
客户端
#include <myhd.h>
//传输结构体
struct cli
{
char type; // L/C/Q协议
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
if(argc!=3)
{
printf("请输入服务器和端口号\n");
return -1;
}
//创建昵称
char n_name[20]="";
printf("请输入昵称:");
scanf("%s",n_name);
getchar();
//1、创建客户端套接字文件描述符
int cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0)
{
perror("socket error");
return -1;
}
printf("socket success cfd=%d\n",cfd);
//填充服务器端信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(atoi(argv[2]));
sin.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t socklen=sizeof(sin);
char buf[128]="";
char rbuf[128]="";
int res=0; //检测select
//定义用于检测文件描述符的集合
fd_set readfds,tempfds;
//清空容器中的内容
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合中
FD_SET(cfd,&readfds);
FD_SET(0,&readfds);
//创建客户端的同时,向服务器发送一个L协议的结构体
struct cli cmsg;
cmsg.type='L';
strcpy(cmsg.name,n_name);
sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));
//2、收发数据
while(1)
{
//复制一份
tempfds=readfds;
//使用select阻塞等待集合中的文件描述符
res=select(cfd+1,&tempfds,NULL,NULL,NULL);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("time out\n");
return -1;
}
if(FD_ISSET(0,&tempfds))
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strcmp(buf,"quit")==0)
{
cmsg.type='Q';
strcpy(cmsg.name,n_name);
sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));
close(cfd);
return 0;
}
else
{
strcpy(cmsg.name,n_name);
strcpy(cmsg.text,buf);
cmsg.type='C';
sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));
}
}
if(FD_ISSET(cfd,&tempfds))
{
bzero(buf,sizeof(buf));
bzero(rbuf,sizeof(rbuf));
recvfrom(cfd,&cmsg,sizeof(cmsg),0,NULL,NULL);
if(cmsg.type=='L')
{
printf("-----%s登陆成功-----\n",cmsg.name);
}
if(cmsg.type=='Q')
{
printf("-----%s已离线-----\n",cmsg.name);
}
if(cmsg.type=='C')
{
strcpy(buf,cmsg.text);
strcpy(rbuf,cmsg.name);
printf("%s:%s\n",rbuf,buf);
if(strcmp(cmsg.name,"***system***"))
{
strcpy(cmsg.name,n_name);
}
}
}
}
//4、关闭套接字文件描述符
close(cfd);
return 0;
}
结果演示