一、UDP
UDP:是一個支持無連接的傳輸協議,全稱是用戶數據包協議(User Datagram Protocol)。UDP協議無需像TCP一樣要建立連接后才能發送封裝的IP數據報,也是因此UDP相較於TCP效率更高一些,但是由於沒有建立連接,UDP只管發送數據,不管數據是否被接收,所以UDP傳輸數據是不安全的,容易丟包。
通信流程:
服務端
1、創建通信用套接字:socket(AF_INET, SOCK_DGRAM, 0); 和TCP不同之處在於第二個參數改為SOCK_DGRAM。
2、綁定套接字:bind(...);
3、通信:接收數據,recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);。發送數據,sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, const socklen_t *addrlen); 第五個參數都是另一端的信息,src_addr是傳出參數。
客戶端同服務端。
二、廣播
廣播:在IP子網內廣播數據包,所有在子網內部的主機都將收到這些數據包。廣播的使用范圍僅在本地子網中,通過路由器控制廣播的傳輸。廣播地址:xxx.xxx.xxx.255。
通信流程:
1、創建並綁定套接字,同UDP。
2、初始化客戶端信息,主要是設置客戶端ip為廣播地址。
3、開放服務器的廣播權限:int flag = 1; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
4、給客戶端(子網內的主機)發送數據。
客戶端同UDP,不能向服務器發送數據。
//server//
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> int main(int argc, const char* argv[]) { // 創建套接字 int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket error"); exit(1); } // 綁定server的iP和端口 struct sockaddr_in serv; memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_port = htons(8787); // server端口 serv.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(fd, (struct sockaddr*)&serv, sizeof(serv)); if(ret == -1) { perror("bind error"); exit(1); } // 初始化客戶端地址信息 struct sockaddr_in client; memset(&client, 0, sizeof(client)); client.sin_family = AF_INET; client.sin_port = htons(6767); // 客戶端要綁定的端口 // 使用廣播地址給客戶端發數據 inet_pton(AF_INET, "192.168.123.255", &client.sin_addr.s_addr); // 給服務器開放廣播權限 int flag = 1; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)); // 通信 while(1) { // 一直給客戶端發數據 static int num = 0; char buf[1024] = {0}; sprintf(buf, "hello, udp == %d\n", num++); int ret = sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&client, sizeof(client)); if(ret == -1) { perror("sendto error"); break; } printf("server == send buf: %s\n", buf); sleep(1); } close(fd); return 0; }
//client// #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> int main(int argc, const char* argv[]) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket error"); exit(1); } // 綁定iP和端口 struct sockaddr_in client; memset(&client, 0, sizeof(client)); client.sin_family = AF_INET; client.sin_port = htons(6767); inet_pton(AF_INET, "0.0.0.0", &client.sin_addr.s_addr); int ret = bind(fd, (struct sockaddr*)&client, sizeof(client)); if(ret == -1) { perror("bind error"); exit(1); } // 接收數據 while(1) { char buf[1024] = {0}; int len = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL); if(len == -1) { perror("recvfrom error"); break; } printf("client == recv buf: %s\n", buf); } close(fd); return 0; }
三、組播
組播:在發送者和每一接收者之間實現點對多點網絡連接。如果一台發送者同時給多個接收者傳輸相同的數據,也只需復制一份相同的數據包。它提高了數據傳送效率,減少了骨干網絡出現擁塞的可能性。組播不同於廣播的地方在於其不局限於局域網,而且其效率比廣播更高。
組播地址:224.0.0.0~224.0.0.255,預留組播地址(永久組地址),224.0.0.0保留不做分配,其它地址供路由器協議使用。224.0.1.0~224.0.1.255,公用組播地址,可以用於Internet,需要申請。224.0.2.0~238.255.255.255,用戶可用組播地址(臨時組地址),全網有效。239.0.0.0~239.255.255.255,本地管理組播地址, 僅在特定的本地范圍內有效。
通信流程:
1、創建並綁定套接字,同UDP。
2、初始化客戶端地址信息,設置組播地址和客戶端要綁定的端口。
3、開放組播權限
// 給服務器開放組播權限 struct ip_mreqn flag; // init flag inet_pton(AF_INET, "239.0.0.10", &flag.imr_multiaddr.s_addr); // 組播地址 inet_pton(AF_INET, "0.0.0.0", &flag.imr_address.s_addr); // 本地IP flag.imr_ifindex = if_nametoindex("ens33"); setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &flag, sizeof(flag));
客戶端,綁定端口要與服務端設置的相一致
客戶端加入到組播地址
// 加入到組播地址 struct ip_mreqn fl; inet_pton(AF_INET, "239.0.0.10", &fl.imr_multiaddr.s_addr); inet_pton(AF_INET, "0.0.0.0", &fl.imr_address.s_addr); fl.imr_ifindex = if_nametoindex("ens33"); setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &fl, sizeof(fl));
//server// #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> #include <net/if.h> int main(int argc, const char* argv[]) { // 創建套接字 int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket error"); exit(1); } // 綁定server的iP和端口 struct sockaddr_in serv; memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_port = htons(8787); // server端口 serv.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(fd, (struct sockaddr*)&serv, sizeof(serv)); if(ret == -1) { perror("bind error"); exit(1); } // 初始化客戶端地址信息 struct sockaddr_in client; memset(&client, 0, sizeof(client)); client.sin_family = AF_INET; client.sin_port = htons(6767); // 客戶端要綁定的端口 // 使用組播地址給客戶端發數據 inet_pton(AF_INET, "239.0.0.10", &client.sin_addr.s_addr); // 給服務器開放組播權限 struct ip_mreqn flag; // init flag inet_pton(AF_INET, "239.0.0.10", &flag.imr_multiaddr.s_addr); // 組播地址 inet_pton(AF_INET, "0.0.0.0", &flag.imr_address.s_addr); // 本地IP flag.imr_ifindex = if_nametoindex("ens33"); setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &flag, sizeof(flag)); // 通信 while(1) { // 一直給客戶端發數據 static int num = 0; char buf[1024] = {0}; sprintf(buf, "hello, udp == %d\n", num++); int ret = sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&client, sizeof(client)); if(ret == -1) { perror("sendto error"); break; } printf("server == send buf: %s\n", buf); sleep(1); } close(fd); return 0; }
//client// #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> #include <net/if.h> int main(int argc, const char* argv[]) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket error"); exit(1); } // 綁定iP和端口 struct sockaddr_in client; memset(&client, 0, sizeof(client)); client.sin_family = AF_INET; client.sin_port = htons(6767); // ........ inet_pton(AF_INET, "0.0.0.0", &client.sin_addr.s_addr); int ret = bind(fd, (struct sockaddr*)&client, sizeof(client)); if(ret == -1) { perror("bind error"); exit(1); } // 加入到組播地址 struct ip_mreqn fl; inet_pton(AF_INET, "239.0.0.10", &fl.imr_multiaddr.s_addr); inet_pton(AF_INET, "0.0.0.0", &fl.imr_address.s_addr); fl.imr_ifindex = if_nametoindex("ens33"); setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &fl, sizeof(fl)); // 接收數據 while(1) { char buf[1024] = {0}; int len = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL); if(len == -1) { perror("recvfrom error"); break; } printf("client == recv buf: %s\n", buf); } close(fd); return 0; }