服务器
#include
//定义存放客户端信息的结构体
typedef struct msg
{
char type; //L登录,Q退出,C聊天
char name[20];
char text[128];
}msg_t;
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
printf("socket success\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;
struct sockaddr_in save[64];
cin.sin_family=AF_INET;
socklen_t socklen=sizeof(cin);
for(int i=0;i<64;i++)
{
save[i].sin_family=AF_INET;
}
msg_t msg[128];
msg_t cli;
//定义一个用于检测文件描述符的集合
fd_set readfds,tempfds;
//清空容器中的内容
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合中
FD_SET(sfd,&readfds); //将sfd文件描述符放入
FD_SET(0,&readfds); //将0号文件描述符放入
//定义一个容器
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;
}
//判断sfd是否还在集合中
if(FD_ISSET(sfd,&tempfds))
{
recvfrom(sfd,&cli,sizeof(cli),0,(struct sockaddr*)&cin,&socklen);
switch (cli.type)
{
case 'L':
{
//登录
printf("%s[%s:%d]:登陆成功\n",cli.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
maxi++;
strcpy(msg[maxi].name,cli.name);
save[maxi].sin_port=cin.sin_port;
save[maxi].sin_addr.s_addr=cin.sin_addr.s_addr;
for(int i=0;i<=maxi;i++)
{
if(cin.sin_port==save[i].sin_port)
{
continue;
}
sendto(sfd,&cli,sizeof(cli),0,(struct sockaddr *)&save[i],sizeof(save[i])); //将登陆的消息发送给除自己外的其他用户
}
}
break;
case 'Q':
{
//登录
int j=0;
//退出
while(1)
{
int t=j;
while(1)
{
if(cin.sin_port==save[j].sin_port)
{
strcpy(msg[t].name,msg[t+1].name);
save[t].sin_port=cin.sin_port;
save[t].sin_addr.s_addr=cin.sin_addr.s_addr;
t++;
if(t==maxi-1)
{
break;
}
}
}
break;
j++; //找到退出的用户的下标
}
for(int i=0;i<=maxi;i++)
{
if(cin.sin_port==save[i].sin_port)
{
continue;
}
maxi--;
sendto(sfd,&cli,sizeof(cli),0,(struct sockaddr *)&save[i],sizeof(save[i])); //将信息发送给除退出用户外的其他用户
}
}
break;
case 'C':
{
if(strcmp(cli.text,"quit")==0)
{
printf("%s[%s:%d]:退出群聊\n",cli.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
}
if(strcmp(cli.text,"quit")!=0)
{
//登录
printf("%s发送了一条消息\n",cli.name);
}
//聊天
for(int i=0;i<=maxi;i++)
{
if(cin.sin_port==save[i].sin_port)
{
continue;
}
if(strcmp(cli.text,"quit")==0)
{
cli.type='Q';
}
sendto(sfd,&cli,sizeof(cli),0,(struct sockaddr *)&save[i],sizeof(save[i]));
}
}
break;
default:printf("您的协议输入有误,请重新输入!!!\n");break;
}
}
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;
}
cli.type='C';
strcpy(cli.text,buf);
strcpy(cli.name,"***system***");
for(int i=0;i<=maxi;i++)
{
sendto(sfd,&cli,sizeof(cli),0,(struct sockaddr *)&save[i],sizeof(save[i]));
}
}
}
//4、关闭文件描述符
close(sfd);
return 0;
}
客户端
#include
//定义用户信息的结构体
typedef struct cin
{
char type;
char name[20];
char text[128];
}cin_t;
int main(int argc, const char *argv[])
{
if(argc!=3)
{
printf("输入错误,请输入IP地址和端口号\n");
}
//1、创建用于通信的套接字
int cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd==-1)
{
perror("socket error");
return -1;
}
printf("socket success\n");
//2、绑定(可选)
//填充服务器地址信息结构体
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]="";
char name[20]="";
//定义客户端地址信息结构体
cin_t cin;
cin_t save;
//清空内容
bzero(cin.name,sizeof(cin.name));
bzero(cin.text,sizeof(cin.text));
bzero(buf,sizeof(buf));
bzero(rbuf,sizeof(rbuf));
bzero(name,sizeof(name));
//登录
printf("请输入姓名>>>");
scanf("%s",name);
getchar();
//比较协议
strcpy(cin.name,name);
cin.type='L';
sendto(cfd,&cin,sizeof(cin),0,(struct sockaddr*)&sin,sizeof(sin));
//定义一个用于检测文件描述符的集合
fd_set readfds,tempfds;
//清空容器中的内容
FD_ZERO(&tempfds);
FD_ZERO(&readfds);
//将要检测的文件描述符放到集合中
FD_SET(cfd,&readfds); //将cfd文件描述符放入
FD_SET(0,&readfds); //将0号文件描述符放入
int res=0; //用来接收select 的返回值
while(1)
{
tempfds=readfds;
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;
}
//判断当前i是否在集合中,如果不在,则直接判断下一个
if(FD_ISSET(cfd,&tempfds))
{
bzero(buf,sizeof(buf));
bzero(rbuf,sizeof(rbuf));
//接收从服务器发来的消息
recvfrom(cfd,&save,sizeof(save),0,NULL,NULL);
if(save.type=='L')
{
printf("------您的好友%s登陆成功------\n",save.name);
}
if(save.type=='Q')
{
printf("------您的好友%s已经下线------\n",save.name);
}
if(save.type=='C')
{
strcpy(buf,save.text);
strcpy(rbuf,save.name);
printf("%s:%s\n",rbuf,buf);
if(strcmp(save.name,"***system***"))
{
strcpy(save.name,name);
}
}
}
if(FD_ISSET(0,&tempfds))
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strcmp(buf,"quit")==0)
{
save.type='C';
strcpy(save.name,name);
strcpy(save.text,"quit");
sendto(cfd,&save,sizeof(save),0,(struct sockaddr*)&sin,sizeof(sin));
close(cfd);
return 0;
}
else
{
strcpy(save.text,buf);
strcpy(save.name,name);
save.type='C';
sendto(cfd,&save,sizeof(save),0,(struct sockaddr*)&sin,sizeof(sin));
}
}
}
//关闭文件描述符
close(cfd);
return 0;
}