简述:
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 }