1.头文件
- /*===============================================
- * 文件名称:UDP.h
- * 创 建 者:crx
- * 创建日期:2023年09月3日
- * 描 述:
- ================================================*/
- #ifndef _UDP_H
- #define _UDP_H
-
- #include
- #include
/* See NOTES */ - #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- typedef struct node{ //链表节点保存用户地址结构体信息
- struct sockaddr_in caddr;
- struct node *next;
- }Node,*Pnode;
-
- typedef struct mesg{ //用户状态、姓名、消息
- char state;
- char name[20];
- char text[60];
- }Mesg;
-
- enum state{ //状态:登录、转发、下线
- Login,
- Relay,
- Quit,
- };
-
- //创建头节点
- Pnode create_node();
- //插入,登录
- int insert_node(Pnode p,struct sockaddr_in caddr,Mesg msg);
- //转发
- void relay(int sockfd,Pnode p,struct sockaddr_in caddr,Mesg msg);
- //删除,下线
- int delete_node(Pnode p,struct sockaddr_in caddr,Mesg msg);
-
- #endif
2.服务器
- /*===============================================
- * 文件名称:UDPs.c
- * 创 建 者:crx
- * 创建日期:2023年09月3日
- * 描 述:
- ================================================*/
- #include "UDP.h"
-
- int main(int argc, char *argv[])
- {
-
- //1.创建套接字
- int sockfd = socket(AF_INET,SOCK_DGRAM,0);
- if(-1 == sockfd)
- {
- perror("socket");
- return -1;
- }
-
- //2.绑定
- struct sockaddr_in saddr;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(8181);
- saddr.sin_addr.s_addr = INADDR_ANY;
- int bindfd = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
- if(-1 == bindfd)
- {
- perror("bind");
- return -1;
- }
-
- //3.准备保存客户端地址结构体,等待登录
- printf("等待登录....\n");
- struct sockaddr_in caddr;
- socklen_t addrlen = sizeof(caddr);
- Mesg msg;
-
- //4.创建头节点
- Pnode p = create_node();
- Pnode q = p;
- Pnode temp = p;
-
- while(1)
- {
-
- memset(&caddr,0,sizeof(caddr));
-
- //5.接收用户登录信息
- recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&caddr,&addrlen);
-
- //6.根据用户状态执行对应操作
-
- //msg.state = Login
- //用户登录,链表插入用户信息
- //判断链表是否为空,为空则插入用户
- //链表不为空,判断是否是链表中已有用户,不是则插入用户信息
- //转发登录消息给其他在线用户
-
- if(msg.state == Login)
- {
- if(NULL == q->next)
- {
- insert_node(p,caddr,msg);
- strcpy(msg.text,"已登录!");
- }
- else
- {
- temp = p->next;
- while(temp)
- {
- if(temp->caddr.sin_port == caddr.sin_port && temp->caddr.sin_addr.s_addr == caddr.sin_addr.s_addr)
- break;
- else
- temp = temp->next;
- }
- if(NULL == temp)
- {
- insert_node(p,caddr,msg);
- strcpy(msg.text,"已登录!");
- relay(sockfd,p,caddr,msg);
- }
- }
- }
-
- //msg.state = Relay
- //转发用户信息给其他在线用户
-
- if(msg.state == Relay)
- {
- relay(sockfd,p,caddr,msg);
- }
-
- //msg.state = Quit
- //用户下线
- //链表中删除用户信息
- //转发用户下线信息给其他用户
-
- if(msg.state == Quit)
- {
- delete_node(p,caddr,msg);
- strcpy(msg.text,"已下线");
- relay(sockfd,p,caddr,msg);
- }
- }
-
- return 0;
- }
-
- //创建头节点
- Pnode create_node()
- {
- Pnode p = (Pnode)malloc(sizeof(Node));
- if(NULL == p)
- {
- perror("malloc");
- return NULL;
- }
- p->next = NULL;
- return p;
- }
-
- //插入,登录
- int insert_node(Pnode p,struct sockaddr_in caddr,Mesg msg)
- {
- if(NULL == p)
- {
- return -1;
- }
- Pnode new = (Pnode)malloc(sizeof(Node));
- if(NULL == new)
- {
- perror("malloc");
- return -2;
- }
- new->next = p->next;
- p->next = new;
- new->caddr = caddr;
- printf("已登录!name:%s,ip:%s,port:%d\n",msg.name,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
-
- return 1;
- }
-
- //转发
- void relay(int sockfd,Pnode p,struct sockaddr_in caddr,Mesg msg) {
- while(p->next)
- {
- p = p->next;
- if(p->caddr.sin_port == caddr.sin_port && p->caddr.sin_addr.s_addr == caddr.sin_addr.s_addr)
- {
- continue;
- }
- else
- {
- sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->caddr),sizeof(p->caddr));
- }
- }
- }
-
- //删除,下线
- int delete_node(Pnode p,struct sockaddr_in caddr,Mesg msg)
- {
- if(NULL == p)
- {
- return -1;
- }
- while(p->next)
- {
- if(memcmp(&(p->next->caddr),&caddr,sizeof(caddr)) == 0)
- {
- Pnode q = p->next;
- p->next = q->next;
- free(q);
- break;
- }
- else
- {
- p = p->next;
- }
- }
- printf("已下线!name:%s,ip:%s,port:%d\n",msg.name,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
- }
3.客户端
- /*===============================================
- * 文件名称:UDPc.c
- * 创 建 者:crx
- * 创建日期:2023年09月3日
- * 描 述:
- ================================================*/
- #include "UDP.h"
-
- int main(int argc, char *argv[])
- {
- //1.创建套接字
- int sockfd = socket(AF_INET,SOCK_DGRAM,0);
- if(-1 == sockfd)
- {
- perror("socket");
- return -1;
- }
-
- //2.服务器地址
- struct sockaddr_in saddr;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(8181);
- saddr.sin_addr.s_addr = inet_addr("192.168.17.225");
-
- //3.创建用户
- Mesg msg;
-
- //4.运行后触发msg.state = Login
- //登录填写用户名
- //发送给服务器登录信息转发登录消息操作
- printf("登录\n");
- msg.state = Login;
- printf("请输入用户名:\n");
- fgets(msg.name,20,stdin);
- printf("******************************************\n");
- if(msg.name[strlen(msg.name)-1] == '\n')
- msg.name[strlen(msg.name) -1] = '\0';
-
- //发送用户登录消息
- if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr)))
- {
- perror("sendto");
- return -1;
- }
-
- //5.创建子进程
- pid_t pid = fork();
-
- //6.子进程循环接收其他用户消息并打印发送人及信息
- if(pid == 0)
- {
- while(1)
- {
- socklen_t addrlen = sizeof(saddr);
- recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,&addrlen);
- printf("~%s~ : %s\n",msg.name,msg.text);
- }
- }
- else
- {
- //7.父进程获取用户终端输入到用户消息中
- while(1)
- {
- fgets(msg.text,sizeof(msg.text),stdin);
- if(msg.text[strlen(msg.text)-1] == '\n')
- msg.text[strlen(msg.text) -1] = '\0';
-
- //8.处理终端输入
-
- //终端输入Quit则触发msg.state = Quit
- //发送给服务器下线信息执行对应操作
- //使用SIGKILL强制结束进程
-
- if(strcmp(msg.text,"Quit") == 0)
- {
- msg.state = Quit;
- if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr)))
- {
- perror("sendto");
- return -1;
- }
- kill(pid,SIGKILL);
- wait(NULL);
- exit(0);
- }
- //终端输入不是Quit则触发msg.state = Relay
- //发送给服务端执行转发操作
-
- else
- {
- msg.state = Relay;
- }
- if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr)))
- {
- perror("sendto");
- return -1;
- }
- }
- }
- close(sockfd);
-
- return 0;
- }
4.makefile
- all:UDPs UDPc
- UDPs:UDPs.c
- gcc UDPs.c -o UDPs
- UDPc:UDPc.c
- gcc UDPc.c -o UDPc
- clean:
- rm UDPs UDPc
5.结果
