3. 基于UDP协议的网络程序

下图是典型的UDP客户端/服务器通讯过程(该图出自[UNPv13e])。

图 37.3. UDP通讯流程

UDP通讯流程

以下是简单的UDP服务器和客户端程序。

  1. /* server.c */
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <netinet/in.h>
  5. #include "wrap.h"
  6.  
  7. #define MAXLINE 80
  8. #define SERV_PORT 8000
  9.  
  10. int main(void)
  11. {
  12. struct sockaddr_in servaddr, cliaddr;
  13. socklen_t cliaddr_len;
  14. int sockfd;
  15. char buf[MAXLINE];
  16. char str[INET_ADDRSTRLEN];
  17. int i, n;
  18.  
  19. sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
  20.  
  21. bzero(&servaddr, sizeof(servaddr));
  22. servaddr.sin_family = AF_INET;
  23. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  24. servaddr.sin_port = htons(SERV_PORT);
  25.  
  26. Bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  27.  
  28. printf("Accepting connections ...\n");
  29. while (1) {
  30. cliaddr_len = sizeof(cliaddr);
  31. n = recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr *)&cliaddr, &cliaddr_len);
  32. if (n == -1)
  33. perr_exit("recvfrom error");
  34. printf("received from %s at PORT %d\n",
  35. inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
  36. ntohs(cliaddr.sin_port));
  37.  
  38. for (i = 0; i < n; i++)
  39. buf[i] = toupper(buf[i]);
  40. n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
  41. if (n == -1)
  42. perr_exit("sendto error");
  43. }
  44. }
  1. /* client.c */
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <netinet/in.h>
  6. #include "wrap.h"
  7.  
  8. #define MAXLINE 80
  9. #define SERV_PORT 8000
  10.  
  11. int main(int argc, char *argv[])
  12. {
  13. struct sockaddr_in servaddr;
  14. int sockfd, n;
  15. char buf[MAXLINE];
  16. char str[INET_ADDRSTRLEN];
  17. socklen_t servaddr_len;
  18.  
  19. sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
  20.  
  21. bzero(&servaddr, sizeof(servaddr));
  22. servaddr.sin_family = AF_INET;
  23. inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  24. servaddr.sin_port = htons(SERV_PORT);
  25.  
  26. while (fgets(buf, MAXLINE, stdin) != NULL) {
  27. n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
  28. if (n == -1)
  29. perr_exit("sendto error");
  30.  
  31. n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
  32. if (n == -1)
  33. perr_exit("recvfrom error");
  34.  
  35. Write(STDOUT_FILENO, buf, n);
  36. }
  37.  
  38. Close(sockfd);
  39. return 0;
  40. }

由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现。

编译运行server,在两个终端里各开一个client与server交互,看看server是否具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,看此时client还能否和server联系上。和前面TCP程序的运行结果相比较,体会无连接的含义。