1、 基于UDP的网络聊天室
项目需求:
- 如果有用户登录,其他用户可以收到这个人的登录信息
- 如果有人发送信息,其他用户可以收到这个人的群聊信息
- 如果有人下线,其他用户可以收到这个人的下线信息
- 服务器可以发送系统信息
服务器
#include <myhead.h>//链表结构体
typedef struct Node
{char name[20];struct sockaddr_in cin;struct Node *next; //指针域
}node,*node_p;//数据结构体
struct msgTyp
{char type;char usrName[20];char msgText[1024];
};//创建链表头结点
node_p create_head()
{node_p L = (node_p)malloc(sizeof(node));if(L == NULL){printf("空间申请失败\n");return NULL;}L->next = NULL;return L;
}//创建结点
node_p create_node(char *name,struct sockaddr_in cin)
{node_p new = (node_p)malloc(sizeof(node));if(new == NULL){printf("空间申请失败\n");return NULL;}strcpy(new->name,name);new->cin = cin;return new;
}//头插
void insert(node_p L,char *name,struct sockaddr_in cin)
{if(L == NULL){printf("入参为空\n");return;}node_p new = create_node(name,cin);new->next = L->next;L->next = new;
}//按姓名删除
void delete(node_p L,char *name)
{if(L == NULL){printf("入参为空\n");return;}node_p p = L;while(p != NULL){if(strcmp(p->next->name,name) == 0){node_p del = p->next;free(del);p->next = p->next->next;break;}p = p->next;}
}int main(int argc, const char *argv[])
{//定义结构体变量node link;struct msgTyp msg;//创建链表node_p L = create_head();//创建套接字int sfd = socket(AF_INET,SOCK_DGRAM,0);if(sfd == -1){perror("socket error");return -1;}//判断是否有外部传参if(argc < 3){printf("请输入IP和端口号\n");return -1;}//端口号快速重用int reuse = 1;if(setsockopt(sfd,SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -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]);//绑定IP和端口号if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1){perror("bind error");return -1;}struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);//定义一个文件描述符集合fd_set readfds,tempfds;//将集合清空FD_ZERO(&readfds);//将检测的文件描述符放入集合FD_SET(0,&readfds);FD_SET(sfd,&readfds);int maxfd = sfd;while(1){//将readfds备份tempfds = readfds;int res = select(maxfd+1,&tempfds,NULL,NULL,NULL);if(res == -1){perror("select error");return -1;}else if(res == 0){printf("time out\n");return -1;}link.cin = cin;//数据的互通if(FD_ISSET(sfd,&tempfds)){recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&link.cin,&addrlen);//登录if(msg.type == 'L'){strcpy(link.name,msg.usrName);insert(L,link.name,link.cin);char arr[128] = "";sprintf(arr,"***%s登录成功***",msg.usrName);node_p p1 = L->next;while(p1 != NULL){sendto(sfd,arr,sizeof(arr),0,(struct sockaddr*)&(p1->cin),sizeof(p1->cin));p1 = p1->next;}printf("%s [%s %d]:登录成功\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));}//聊天else if(msg.type == 'C'){strcpy(link.name,msg.usrName);char brr[2048] = "";sprintf(brr,"%s:%s",msg.usrName,msg.msgText);node_p p2 = L->next;//循环遍历while(p2 != NULL){if(strcmp(p2->name,msg.usrName) != 0){sendto(sfd,brr,sizeof(brr),0,(struct sockaddr*)&(p2->cin),sizeof(p2->cin));}p2 = p2->next;}printf("%s [%s %d]:chat成功\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));}//退出else if(msg.type == 'Q'){strcpy(link.name,msg.usrName);delete(L,link.name);char crr[128] = "";sprintf(crr,"***%s退出聊天***",msg.usrName);node_p p3 = L->next;while(p3 != NULL){sendto(sfd,crr,sizeof(crr),0,(struct sockaddr*)&(p3->cin),sizeof(p3->cin));p3 = p3->next;}printf("%s [%s %d]:退出聊天\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));}}//系统发送信息if(FD_ISSET(0,&tempfds)){char sbuf[1024] = "";char buf[512] = "";fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';sprintf(sbuf,"**system**:%s",buf);node_p p = L->next;while(p != NULL){if(sendto(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)) == -1){perror("sendto error");return -1;}p = p->next;}printf("**system**[%s %d]:chat成功\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));}}return 0;
}
客户端
#include <myhead.h>struct msgTyp
{char type; //L C Qchar usrName[20];char msgText[1024];
};int main(int argc, const char *argv[])
{struct msgTyp usr;int cfd = socket(AF_INET,SOCK_DGRAM,0);if(cfd == -1){perror("socket error");return -1;}//判断是否写外部参数if(argc < 3){printf("请输入IP和端口号\n");return -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]);socklen_t addrlen = sizeof(sin);//输入姓名char name[20] = "";printf("请输入姓名>>>>");fgets(name,sizeof(name),stdin);name[strlen(name)-1] = '\0';usr.type = 'L';strcpy(usr.usrName,name);if(sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,sizeof(sin)) == -1){perror("sendto error");return -1;}//定义一个文件描述符,将检测的文件描述符放入集合fd_set readfds,tempfds;FD_ZERO(&readfds);FD_SET(0,&readfds);FD_SET(cfd,&readfds);int maxfd = cfd; //记录最大的文件描述符while(1){tempfds = readfds;int res = select(maxfd+1,&tempfds,NULL,NULL,NULL);if(res == -1){perror("select error");return -1;}//收到客户端信息if(FD_ISSET(cfd,&tempfds)){bzero(usr.msgText,sizeof(usr.msgText));if(recvfrom(cfd,usr.msgText,sizeof(usr.msgText)-1,0,(struct sockaddr*)&sin,&addrlen) == -1){perror("recvfrom error");return -1;}printf("%s\n",usr.msgText);}//发送数据if(FD_ISSET(0,&tempfds)){bzero(usr.msgText,sizeof(usr.msgText));fgets(usr.msgText,sizeof(usr.msgText),stdin);usr.msgText[strlen(usr.msgText)-1] = '\0';//退出操作if(strcmp(usr.msgText,"quit") == 0){usr.type = 'Q';sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,addrlen);close(cfd);return 0;}//聊天操作else {usr.type = 'C';if(sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,addrlen) == -1){perror("sendto error");return -1;}}}}return 0;
}
结果