服务器:
#include <myhead.h>
struct msg
{
char type;
char name[20];
char text[1024];
};
int main(int argc, const char *argv[])
{
if(argc!=3)
{
printf("input error\n");
printf("./a.out IP地址 端口号\n");
return -1;
}
//1、创建用于通信的套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//将端口号快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
//2、绑定IP地址和端口号
//2.1填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //地址族
short port=(short)atoi(argv[2]);
sin.sin_port = htons(port); //端口号
sin.sin_addr.s_addr = inet_addr(argv[1]); //IP地址
//2,2绑定
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//定义容器接收对端地址信息结构体
struct sockaddr_in cin;
socklen_t socklen = sizeof(cin);
//定义存储客户端地址信息结构体信息
struct sockaddr_in scin[1024];
for(int i=0;i<1024;i++)
{
scin[i].sin_family=AF_INET;
}
//1、创建文件描述符容器
fd_set readfds, tempfds;
//2、清空容器内容
FD_ZERO(&readfds);
//3、将sfd和0号文件描述符放入容器中
FD_SET(0, &readfds);
FD_SET(sfd, &readfds);
//定义结构体变量,用于接收和发送,和客户端进行通信
struct msg send;
struct msg in;
int res;
char buf[128]="";
char address[128]="";
int n=0;
while(1)
{
bzero(buf,sizeof(buf));
bzero(address,sizeof(address));
tempfds = readfds; //将要检测的容器复制保存一份
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))
{
res=recvfrom(sfd,&in,sizeof(in),0,(struct sockaddr*)&cin,&socklen);
if(res==-1)
{
perror("recvfrom error");
return -1;
}
if(in.type=='L')
{
scin[n]=cin;
n++;
// sprintf(buf,"---%s 已上线---",in.name);
sprintf(address,"---%s[%s:%d]登录成功---",in.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
printf("%s\n",address);
for(int i=0;i<n;i++)
{
if(scin[i].sin_port==cin.sin_port)
{
continue;
}
sendto(sfd,&in,sizeof(in),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
}
bzero(address,sizeof(address));
}
if(in.type=='C')
{
sprintf(address,"---%s[%s:%d]chat成功---",in.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
printf("%s\n",address);
//群聊
send.type=in.type;
strcpy(send.name,in.name);
strcpy(send.text,in.text);
for(int i=0;i<n;i++)
{
if(scin[i].sin_port==cin.sin_port)
{
continue;
}
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
}
}
if(in.type=='Q')
{
//下线
send.type=in.type;
strcpy(send.name,in.name);
strcpy(send.text,in.text);
for(int i=0;i<n;i++)
{
bzero(buf,sizeof(buf));
sprintf(buf,"---%s已下线---\n",send.name);
printf("%s\n",buf);
//删除用户
if(scin[i].sin_port==cin.sin_port)
{
int t=i;
for(int j=i;j<=n;j++)
{
scin[t]=scin[t+1];
t++;
}
}
n--;
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
}
}
}
if(FD_ISSET(0,&tempfds))
{
//用于接收系统的消息
bzero(&send,sizeof(send));
strcpy(send.name,"系统消息");
fgets(send.text,sizeof(send.text),stdin);
send.text[strlen(send.text)-1]=0;
send.type='C';
for(int i=0;i<=n;i++)
{
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
}
}
}
close(sfd);
return 0;
}
客户端:
#include <myhead.h>
struct msg
{
char type;
char name[20];
char text[1024];
};
int main(int argc, const char *argv[])
{
if(argc!=3)
{
printf("input error\n");
printf("usage:./a.out IP地址 端口号\n");
return -1;
}
//1、创建用于通信的套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、绑定IP地址和端口号
//2.1填充服务器端地址信息结构体
struct sockaddr_in sin;
short port=(short)atoi(argv[2]);
sin.sin_family = AF_INET; //地址族
sin.sin_port = htons(port); //端口号
sin.sin_addr.s_addr = inet_addr(argv[1]); //IP地址
//定义容器接收服务器端地址信息结构体
socklen_t socklen = sizeof(sin);
//定义结构体变量,用于发送和接收
struct msg send;
struct msg in;
printf("请输入用户名>>>");
fgets(send.name,sizeof(send.name),stdin);
send.name[strlen(send.name)-1]=0;
send.type='L';
//将登录的用户名发送给服务器
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
//1、创建文件描述符容器
fd_set readfds, tempfds;
//2、清空容器内容
FD_ZERO(&readfds);
//3、将sfd和0号文件描述符放入容器中
FD_SET(0, &readfds);
FD_SET(sfd, &readfds);
int res1;
char buf[128]="";
while(1)
{
tempfds = readfds; //将要检测的容器复制保存一份
int 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))
{
res1=recvfrom(sfd,&in,sizeof(in),0,(struct sockaddr*)&sin,&socklen);
if(res1==-1)
{
perror("recvfrom error");
return -1;
}
if(in.type=='L')
{
printf("---%s上线了---\n",in.name);
}
if(in.type=='C')
{
printf("%s: %s\n",in.name,in.text);
}
if(in.type=='Q')
{
printf("---%s已下线---\n",in.name);
}
}
//如果程序执行到此,说明检测的集合中有事件产生
if(FD_ISSET(0, &tempfds)) //表示sfd触发了事件
{
//判断是群聊还是退出
bzero(send.text,sizeof(send.text));
fgets(send.text,sizeof(send.text),stdin);
send.text[strlen(send.text)-1]=0;
if(strcmp(send.text,"quit")==0)
{
send.type='Q';
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
break;
}else
{
send.type='C';
sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
}
}
}
close(sfd);
return 0;
}
运行结果: