56.1 UDP 编程模型
56.1.1 编程模型
UDP 协议称为用户数据报文协议,可靠性比 TCP 低,但执行效率高
56.1.2 API
(1)发送数据
- 函数参数:
- sockfs:套接字文件描述符
- buf:发送的数据
- len:发送的数据的大小,即多少个字节
- flags:一般设置为0
- dest_addr:接收方的地址
- addrlen:前面地址结构体 dest_addr 的大小
- msg:将发送的数据封装在 msghdr 的结构体中
- 返回值:返回值都一样,成功,则返回发送的字节数;出错,则返回-1
(2)接收数据
- 返回值:返回消息的字节数,无消息,返回0;出错返回 -1
56.2 例子
56.2.1 服务器编程
time_udp_server.c
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <arpa/inet.h> 4 #include <unistd.h> 5 #include <netdb.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <signal.h> 9 #include <string.h> 10 #include <time.h> 11 12 int sockfd; 13 14 void sig_handler(int signo) 15 { 16 if(signo == SIGINT){ 17 printf("server close "); 18 close(sockfd); 19 exit(1); 20 } 21 } 22 23 void out_addr(struct sockaddr_in *clientaddr) 24 { 25 char ip[16]; 26 int port; 27 memset(ip, 0, sizeof(ip)); 28 inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip)); 29 port = ntohs(clientaddr->sin_port); 30 printf("client: %s(%d) ", ip, port); 31 } 32 33 /** 和客户端进行通信 */ 34 void do_service(int fd) 35 { 36 struct sockaddr_in clientaddr; 37 socklen_t len = sizeof(clientaddr); 38 char buffer[1024]; 39 memset(buffer, 0, sizeof(buffer)); 40 /** 接收客户端的数据报文 */ 41 if(recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&clientaddr, &len) < 0){ 42 perror("recvfrom error"); 43 } 44 else{ 45 out_addr(&clientaddr); 46 printf("client send into: %s ", buffer); 47 48 /** 向客户端发送数据报文 */ 49 long int t = time(0); 50 char *ptr = ctime(&t); 51 ssize_t size = strlen(ptr) * sizeof(char); 52 if(sendto(sockfd, ptr, size, 0, (struct sockaddr *)&clientaddr, len) < 0){ 53 perror("sendto error"); 54 } 55 } 56 57 } 58 59 int main(int argc, char *argv[]) 60 { 61 if(argc < 2){ 62 printf("usage: %s port ", argv[0]); 63 exit(1); 64 } 65 66 if(signal(SIGINT, sig_handler) == SIG_ERR){ 67 perror("signal sigint error"); 68 exit(1); 69 } 70 71 /** 步骤1: 创建 socket */ 72 sockfd = socket(AF_INET, SOCK_DGRAM, 0); 73 if(sockfd < 0){ 74 perror("socket error"); 75 exit(1); 76 } 77 78 int ret; 79 int opt = 1; 80 /** 设置套接字选项, 让停掉的端口马上可以使用 */ 81 if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){ 82 perror("setsockopt error"); 83 exit(1); 84 } 85 86 /** 步骤2: 调用 bind 函数对 socket 和地址进行绑定 */ 87 struct sockaddr_in serveraddr; 88 memset(&serveraddr, 0, sizeof(serveraddr)); 89 serveraddr.sin_family = AF_INET; ///< ipv4 90 serveraddr.sin_port = htons(atoi(argv[1])); ///< port 91 serveraddr.sin_addr.s_addr = INADDR_ANY; ///<IP 92 if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){ 93 perror("bind error"); 94 exit(1); 95 } 96 97 /** 步骤3: 和客户端进行双向的数据通信 */ 98 while(1){ 99 do_service(sockfd); 100 } 101 102 return 0; 103 }
56.2.2 客户端编程
time_udp_client.c
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <arpa/inet.h> 4 #include <unistd.h> 5 #include <netdb.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <signal.h> 9 #include <string.h> 10 #include <time.h> 11 12 int main(int argc, char *argv[]) 13 { 14 if(argc < 3){ 15 printf("usage: %s ip port ", argv[0]); 16 exit(1); 17 } 18 19 /** 步骤1: 创建 socket */ 20 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); 21 if(sockfd < 0){ 22 perror("socket error"); 23 exit(1); 24 } 25 26 /** 步骤2: 调用 recvfrom 和 sendto 等函数和服务器端双向通信 */ 27 struct sockaddr_in serveraddr; 28 memset(&serveraddr, 0, sizeof(serveraddr)); 29 serveraddr.sin_family = AF_INET; ///< ipv4 30 serveraddr.sin_port = htons(atoi(argv[2])); ///< port 31 inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr); 32 char buffer[1024] = "hello world"; 33 /** 向服务器端发送数据报文 */ 34 if(sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){ 35 perror("sendto error"); 36 exit(1); 37 } 38 else{ 39 /** 接受服务器端发送的数据报文 */ 40 memset(buffer, 0, sizeof(buffer)); 41 if(recv(sockfd, buffer, sizeof(buffer), 0) < 0){ 42 perror("recv error"); 43 exit(1); 44 } 45 else{ 46 printf("%s", buffer); 47 } 48 } 49 50 close(sockfd); 51 52 return 0; 53 }
编译运行测试: