服务器端
#include <myhd.h>//传输结构体
struct cli
{char type; // L/C/Qchar 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;
}
结果演示