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 }

posted @ 2017-03-13 20:28  bkycrmn  阅读(245)  评论(0)    收藏  举报