关于UDP的普通C/S模型、广播C/S模型以及组播C/S模型

简述:
UDP与TCP一个主要的区别就是客户端与服务器之间不需要建立连接,即不经历三次握手过程。只需要主动发送数据的一方确定接收数据的一方的地址信息(IP地址+端口号),可以这么理解,数据的接收方要先确定自己用于接收数据的本地地址信息,即要显示绑定。作为接收数据的一方可以在接收数据的过程中获知对方的地址信息,然后通过获得的地址信息,向对方发送数据。

一、普通C/S模型

1、说明

在普通的C/S模型中,客户端一般作为发起数据传输的主动方,即客户端会先发送数据,故客户端在发送数据前要先确定服务器的IP地址(这个IP地址可以是本地回环地址:127.0.0.1)和接收数据的端口号。客户端不需要显式绑定套接字描述符和本地地址信息,由系统隐式分配绑定。

服务器作为数据数据传输的被动方,即服务器会先接收数据,服务器在接收数据前要先绑定(bind)套接字描述符和本地IP地址+所确定的接收数据的端口号。在接收到数据后,服务器便可确定客户端的IP地址和端口号(通过在接收数据函数如 recvfrom()中传递参数,通过相应参数保存客户端的地址信息),然后通过所获取的地址信息给对应客户端发送数据。

2、服务器代码

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <unistd.h>
  5 #include <cctype>
  6 #include <arpa/inet.h>
  7 
  8 #define SERV_PORT 8000
  9 
 10 int main(void)
 11 {
 12     struct sockaddr_in serv_addr, clie_addr;
 13     socklen_t clie_addr_len;
 14     int sockfd;
 15     char buf[BUFSIZ];
 16     char str[INET_ADDRSTRLEN];
 17     int i, n;
 18 
 19     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 20 
 21     memset(&serv_addr, 0, sizeof(serv_addr));
 22     serv_addr.sin_family = AF_INET;
 23     serv_addr.sin_port = htons(SERV_PORT);
 24     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 25 
 26     bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
 27 
 28     printf("Accepting connection...\n");
 29     while(1) {
 30         clie_addr_len = sizeof(clie_addr);
 31         n = recvfrom(sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&clie_addr, &clie_addr_len);
 32         if(n == -1)
 33             perror("recvfrom error\n");
 34 
 35         printf("received from %s at port %d\n",
 36                 inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
 37                 ntohs(clie_addr.sin_port));
 38 
 39         for(i = 0; i < n; i++)
 40             buf[i] = toupper(buf[i]);
 41 
 42         n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
 43         if(n == -1)
 44             perror("sento error");
 45     }
 46     close(sockfd);
 47 
 48     return 0;
 49 }

3、客户端代码

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <cctype>
  7 
  8 #define SERV_PORT 8000
  9 
 10 int main(int argc, char *argv[])
 11 {
 12     struct sockaddr_in serv_addr;
 13     int sockfd, n;
 14     char buf[BUFSIZ];
 15 
 16     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 17 
 18     memset(&serv_addr, 0, sizeof(serv_addr));
 19     serv_addr.sin_family = AF_INET;
 20     inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
 21     serv_addr.sin_port = htons(SERV_PORT);
 22 
 23     while(fgets(buf, BUFSIZ, stdin) != NULL) {
 24         n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
 25         if(n == -1)
 26             perror("sendto error");
 27 
 28         n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);
 29         if(n == -1)
 30             perror("recvfrom error");
 31 
 32         write(STDOUT_FILENO, buf, n);
 33     }
 34     close(sockfd);
 35 
 36     return 0;
 37 }

二、广播C/S模型

1、说明

(在测试广播时,要关闭防火墙)
在广播C/S模型中,服务器server是数据发送方,客户端是数据接收方,故服务器在发送数据前要先确定数据接收方(客户端)的IP地址(即广播的IP地址)和端口号,并且要设置开放广播权限。

而对于客户端,其接收数据的端口号要和服务器中确定的端口号统一,故不可由系统自动隐式分配绑定,客户端中要显示绑定(bind)套接字描述符和本地IP地址+所确定的接收数据的端口号,这个本地IP地址可以用INADDR_ANY指定,即表示本机的所有可用IP地址,也就是说本地IP地址可以泛指,但是端口号必须要使用和服务器端共同约定的那个端口号。由于此时客户端只是接收数据而不发送数据,故客户端在接收数据时也不用了解所接收到的数据来自哪个IP地址的哪个端口号。

2、服务器代码

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <sys/socket.h>
  7 
  8 #define SERV_PORT 8000
  9 #define CLIE_PORT 9000
 10 #define MAXLINE 1500
 11 
 12 //#define BROADCAST_IP "192.168.10.255"
 13 #define BROADCAST_IP "192.168.122.255"
 14 
 15 int main()
 16 {
 17     int sockfd;
 18     struct sockaddr_in servaddr, clieaddr;
 19     char buf[MAXLINE];
 20 
 21     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 22 
 23     memset(&servaddr, 0, sizeof(servaddr));
 24     servaddr.sin_family = AF_INET;
 25     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 26     servaddr.sin_port = htons(SERV_PORT);
 27 
 28     bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
 29 
 30     int flag = 1;
 31     setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
 32 
 33     memset(&clieaddr, 0, sizeof(clieaddr));
 34     clieaddr.sin_family = AF_INET;
 35     inet_pton(AF_INET, BROADCAST_IP, &clieaddr.sin_addr.s_addr);
 36     clieaddr.sin_port = htons(CLIE_PORT);
 37 
 38     int i = 0;
 39     while(1) {
 40         sprintf(buf, "Drink %d glasses of water\n", i++);
 41         sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&clieaddr, sizeof(clieaddr));
 42         sleep(1);
 43     }
 44     close(sockfd);
 45 
 46     return 0;
 47 }
 48 

3、客户端代码

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <unistd.h>
  4 #include <cstring>
  5 #include <arpa/inet.h>
  6 #include <sys/socket.h>
  7 
  8 #define SERV_PORT 8000
  9 #define CLIE_PORT 9000
 10 #define MAXLINE 4096
 11 
 12 int main()
 13 {
 14     struct sockaddr_in localaddr;
 15     int confd;
 16     ssize_t len;
 17     char buf[MAXLINE];
 18 
 19     confd = socket(AF_INET,SOCK_DGRAM, 0);
 20 
 21     memset(&localaddr, 0, sizeof(localaddr));
 22     localaddr.sin_family = AF_INET;
 23     localaddr.sin_port = htons(CLIE_PORT);
 24     inet_pton(AF_INET, "0.0.0.0", &localaddr.sin_addr.s_addr);
 25 
 26     int ret = bind(confd, (struct sockaddr *)&localaddr, sizeof(localaddr));
 27     if(ret == 0)
 28         printf("bind ok...\n");
 29 
 30     while(1) {
 31         len = recvfrom(confd, buf, sizeof(buf), 0, NULL, 0);
 32         write(STDOUT_FILENO, buf, len);
 33     }
 34     close(confd);
 35 
 36     return 0;
 37 }

三、组播C/S模型

1、说明

在组播C/S模型中,服务器也是数据发送方,故与广播类似,服务器在发送数据前要先确定数据接收方(组播组内客户端)的IP地址(即组播的IP地址)和端口号,且也要设置开放组播权限,此外,服务器还要定义并初始化组播结构体 struct ip_mreqn 对象,然后通过此对象以及 setsockopt 函数建立并初始化组播组。

关于客户端,与广播C/S模型同理,客户端中要显示绑定(bind)套接字描述符和本地IP地址+所确定的接收数据的端口号,IP地址和端口号的要求也与广播C/S模型相同,故此处不再赘述。与广播C/S模型不同的是,客户端还要定义并初始化组播结构体 struct ip_mreqn 对象,然后通过此对象以及 setsockopt 函数加入服务器建立的组播组。

关于组播结构体 struct ip_mreqn 对象的初始化,需要进行初始化的是结构体中的 imr_multiaddr 成员(用组播 IP 地址)、imr_address 成员(用服务器 / 客户端的本地任意可用IP地址)、imr_ifindex 成员(主机网卡的索引号)。

2、服务器代码

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <unistd.h>
  4 #include <arpa/inet.h>
  5 #include <sys/socket.h>
  6 #include <cstring>
  7 #include <net/if.h>
  8 
  9 #define SERV_PORT 8000
 10 #define CLIE_PORT 9000
 11 #define MAXLINE 1500
 12 
 13 #define GROUP "239.0.0.2"
 14 
 15 int main()
 16 {
 17     int sockfd;
 18     struct sockaddr_in serv_addr, clie_addr;
 19     char buf[MAXLINE] = "youngchan\n";
 20     struct ip_mreqn group;  //multicast structure
 21 
 22     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 23 
 24     memset(&serv_addr, 0, sizeof(serv_addr));
 25     serv_addr.sin_family = AF_INET;
 26     serv_addr.sin_port = htons(SERV_PORT);
 27     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 28 
 29     bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
 30 
 31     inet_pton(AF_INET, GROUP, &group.imr_multiaddr);   //set multicast address
 32     inet_pton(AF_INET, "0.0.0.0", &group.imr_address);   //set any local IP address
 33     group.imr_ifindex = if_nametoindex("ens33");   //Returns the index of the corresponding network card name
 34 
 35     setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));   //Set multicast permissions
 36 
 37     memset(&clie_addr, 0, sizeof(clie_addr));
 38     clie_addr.sin_family = AF_INET;
 39     inet_pton(AF_INET, GROUP, &clie_addr.sin_addr.s_addr);
 40     clie_addr.sin_port = htons(CLIE_PORT);
 41 
 42     int i = 0;
 43     while(1) {
 44         sprintf(buf, "youngchan %d\n", i++);
 45         sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
 46         sleep(1);
 47     }
 48 
 49     close(sockfd);
 50 
 51     return 0;
 52 }

3、客户端代码

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <unistd.h>
  4 #include <arpa/inet.h>
  5 #include <sys/socket.h>
  6 #include <net/if.h>
  7 #include <cstring>
  8 
  9 #define SERV_PORT 8000
 10 #define CLIE_PORT 9000
 11 
 12 #define GROUP "239.0.0.2"
 13 
 14 int main()
 15 {
 16     struct sockaddr_in localaddr;
 17     int confd;
 18     ssize_t len;
 19     char buf[BUFSIZ];
 20 
 21     struct ip_mreqn group;
 22 
 23     confd = socket(AF_INET, SOCK_DGRAM, 0);
 24 
 25     memset(&localaddr, 0, sizeof(localaddr));
 26     localaddr.sin_family = AF_INET;
 27     localaddr.sin_port = htons(CLIE_PORT);
 28     //inet_pton(AF_INET, "0.0.0.0", &localaddr.sin_addr.s_addr);  // Equivalent to the next line of code
 29     localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 30 
 31     bind(confd, (struct sockaddr *)&localaddr, sizeof(localaddr));
 32 
 33     inet_pton(AF_INET, GROUP, &group.imr_multiaddr);   //set multicast address
 34     inet_pton(AF_INET, "0.0.0.0",  &group.imr_address);   //set any local IP adress
 35     group.imr_ifindex = if_nametoindex("ens33");   //Returns the index of the corresponding network card name 
 36 
 37     setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));   // Join a multicast group
 38 
 39     while(1) {
 40         len = recvfrom(confd, buf, sizeof(buf), 0, NULL, 0);
 41         write(STDOUT_FILENO, buf, len);
 42     }
 43     close(confd);
 44 
 45     return 0;
 46 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值