1.3 UDP 协议下实现基于服务器的多个客户端聊天室
head.h
1 /*UDP 协议下实现基于服务器的多个客户端聊天室*/ 2 3 #ifndef __HEAD_H__ 4 #define __HEAD_H__ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <sys/socket.h> 12 #include <arpa/inet.h> 13 #include <netinet/in.h> 14 #include <netinet/ip.h> 15 #include <error.h> 16 #include <errno.h> 17 #include <unistd.h> 18 #include <signal.h> 19 20 #define error_exit(_errmsg_) error(EXIT_FAILURE, errno, _errmsg_) 21 22 #define SER_PORT 50000 23 #define SER_ADDR "192.168.1.104" 24 #define LOGIN_TYPE 100 25 #define CHAT_TYPE 200 26 27 typedef struct sockaddr_in DATATYPE;//定义IP地址类型 28 typedef struct msg//定义消息类型结构体变量 29 { 30 int type; 31 char name[64]; 32 char text[256]; 33 }MSG; 34 35 typedef struct node//定义客户端作为结构体数据节点 36 { 37 DATATYPE data; 38 struct node *next; 39 }LinkNode; 40 41 typedef struct list//定义表头 42 { 43 LinkNode *head; 44 int tlen; 45 int clen; 46 }LinkList; 47 48 LinkList *CreateLinkList(int tlen); 49 int InsertLinkList(LinkList *list, DATATYPE data); 50 int DeleteLinkList(LinkList *list, DATATYPE data); 51 int chat(int sockfd, LinkList *list, DATATYPE data, MSG message); 52 #endif
Makefile
1 ALL:server client 2 3 client:client.c 4 gcc $< -o $@ 5 server:server.c fun.c linklist.c 6 gcc $^ -o $@ -g 7 .PHONY: 8 clean: 9 rm server client
client.c
1 #include "head.h" 2 3 int main(void) 4 { 5 MSG message; 6 int sockfd = 0; 7 struct sockaddr_in seraddr; 8 pid_t pid,pid1; 9 10 seraddr.sin_family = AF_INET;//IPv4协议族 11 seraddr.sin_port = htons(SER_PORT);//本地到网络存储方式转化 12 seraddr.sin_addr.s_addr = inet_addr(SER_ADDR);//将字符串类型IP地址转化为二进制形式 13 14 if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) 15 error_exit("fail to socket"); 16 //创建一个用来通信的文件描述符 17 printf("请输入用户名:"); 18 fgets(message.name, 64, stdin); 19 message.type = LOGIN_TYPE; 20 //消息队列名字/类型 21 if (-1 == sendto(sockfd, &message, sizeof(MSG), 0, \ 22 (struct sockaddr *)&seraddr, sizeof(seraddr))) 23 error_exit("fail to sendto"); 24 //利用套接字文件描述符发送一条数据 25 if (-1 == (pid = fork())) 26 error_exit("fail to fork"); 27 28 //创建父子进程来实现数据信息互通 29 if (pid == 0)//子进程负责发送消息 30 { 31 while (1) 32 { 33 fgets(message.text, 256, stdin); 34 message.type = CHAT_TYPE; 35 if (-1 == sendto(sockfd, &message, sizeof(MSG), 0, 36 (struct sockaddr *)&seraddr, sizeof(seraddr))) 37 //文件描述符/要发送的信息首地址/发送数据的长度/发送属性默认为0 38 // /发送给目的地址端的IP地址/目的地址端IP地址长度 39 error_exit("fail to sendto"); 40 if (!strncmp(message.text, "quit", 4)) 41 break; 42 } 43 pid1 = getppid(); 44 } 45 else if (pid > 0)//父进程负责接受消息 46 { 47 MSG ret; 48 while (1) 49 { 50 if (-1 == recvfrom(sockfd, &ret, sizeof(MSG), 0, NULL, NULL)) 51 //文件描述符/读取到的信息存放空间的首地址/最大能够接受到的信息的长度/0 52 // /接受发送信息端的IP地址/保存要取发送端IP地址长度变量的地址 53 error_exit("fail to recvfrom"); 54 printf("%s#%s\n", ret.name, ret.text); 55 } 56 } 57 kill(pid1, SIGKILL);//杀死父子进程 58 close(sockfd);//关闭文件描述符 59 60 return 0; 61 } 62 63 /////////////////////////////////////////////////////// 64 /* 65 struct sockaddr_in 66 { 67 sa_family_t sin_family; //协议族 68 in_port_t sin_port; //端口号 69 struct in_addr sin_addr;//IP地址 70 }; 71 struct in_addr 72 { 73 uint 32_t s_addr; 74 }; 75 */
server.c
1 #include "head.h" 2 3 int main(void) 4 { 5 int sockfd = 0; 6 struct sockaddr_in seraddr;// 7 MSG ret; 8 DATATYPE data; 9 socklen_t addrlen = sizeof(struct sockaddr_in); 10 LinkList *linklist = NULL; 11 12 seraddr.sin_family = AF_INET; 13 seraddr.sin_port = htons(SER_PORT); 14 seraddr.sin_addr.s_addr = inet_addr(SER_ADDR); 15 16 linklist = CreateLinkList(100); 17 18 if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) 19 error_exit("fail to socket"); 20 //创建一个用来通信的文件描述符 21 if (-1 == bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr))) 22 error_exit("fail to bind"); 23 //将sockfd和IP地址绑定 24 while (1)//接受上线请求 25 { 26 if (-1 == (recvfrom(sockfd, &ret, sizeof(MSG), 0, 27 (struct sockaddr *)&data, &addrlen))) 28 //文件描述符/读取到的信息存放空间的首地址/最大能够接受到的信息的长度/0 29 // /接受发送信息端的IP地址/保存要取发送端IP地址长度变量的地址 30 error_exit("fail to recvfrom"); 31 32 33 switch(ret.type)//判断消息类型 34 { 35 case LOGIN_TYPE://登录类型 36 printf("%s:%d --> login\n", inet_ntoa(data.sin_addr), 37 ntohs(data.sin_port)); 38 //将二进制结构体形式的IP地址转化成字符串类型 39 //完成网络中数据大小端和在本地数据大小端存储方式和转化 40 InsertLinkList(linklist, data);break; 41 case CHAT_TYPE: //聊天类型 42 chat(sockfd, linklist, data, ret);break; 43 } 44 } 45 46 return 0; 47 }
linklist.c
1 #include "head.h" 2 3 LinkList *CreateLinkList(int tlen)//创建表头 4 { 5 LinkList *list = NULL;//表头地址 6 if (NULL == (list = malloc(sizeof(LinkList)))) 7 error_exit("fail to malloc"); 8 list->tlen = tlen;//初始化 9 list->clen = 0; 10 if (NULL == (list->head = malloc(sizeof(LinkNode))))//申请头节点(不存数据)地址空间 11 error_exit("fail to malloc"); 12 list->head->next = NULL; 13 14 return list;//返回表头 15 } 16 17 int InsertLinkList(LinkList *list, DATATYPE data)//插入链表节点 18 { 19 LinkNode *temp = NULL; 20 if (NULL == (temp = malloc(sizeof(LinkNode))))//申请节点(存数据)地址空间 21 error_exit("fail to malloc"); 22 memcpy(&temp->data, &data, sizeof(DATATYPE));//赋值数据 23 24 temp->next = list->head->next;//头插法 25 list->head->next = temp; 26 27 list->clen++;//计数 28 29 return 0; 30 } 31 32 int DeleteLinkList(LinkList *list, DATATYPE data)//删除链表节点 33 { 34 LinkNode *p = list->head->next; 35 LinkNode *q = list->head; 36 37 while (p != NULL) 38 { 39 if (!memcmp(&p->data, &data, sizeof(DATATYPE)))//找到目标节点数据 40 { 41 q->next = p->next; 42 free(p);//释放其地址空间 43 list->clen--; 44 p = q->next; 45 } 46 else 47 { 48 p = p->next;//没找到则继续遍历 49 q = q->next; 50 } 51 } 52 return 0; 53 }
fun.c
1 #include "head.h" 2 3 int chat(int sockfd, LinkList *list, DATATYPE data, MSG message) 4 { 5 LinkNode *p = list->head->next;//定义新的客户端结点地址p 6 7 if (!strncmp(message.text, "quit", 4))//有客户端发送quit则删除该链表结点 8 DeleteLinkList(list, data); 9 10 while (p != NULL) 11 { 12 if (memcmp(&p->data, &data, sizeof(DATATYPE))) 13 //内存字节比较/ 14 { 15 if (-1 == (sendto(sockfd, &message, sizeof(MSG), 16 0, (struct sockaddr *)&p->data, sizeof(DATATYPE)))) 17 error_exit("fail to sendto"); 18 } 19 p = p->next; 20 } 21 22 return 0; 23 }


浙公网安备 33010602011771号