基于UDP协议的网络程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于UDP协议的网络程序相关的知识,希望对你有一定的参考价值。

下图是基于UDP协议的客户端/服务器程序的一般流程:

图1.1 UDP协议通信流程

技术分享图片

UDP套接口是无连接的、不可靠的数据报协议;

既然他不可靠为什么还要用呢?其一:当应用程序使用广播或多播时只能使用UDP协议;其二:由于他是无连接的,所以速度快。因为UDP套接口是无连接的,如果一方的数据报丢失,那另一方将无限等待,解决办法是设置一个超时。

建立UDP套接口时socket函数的第二个参数应该是SOCK_DGRAM,说明是建立一个UDP套接口;由于UDP是无连接的,所以服务器端并不需要listen或accept函数。

使用UDP套接字编程可以实现基于TCP/IP协议的面向无连接的通信,它分为服务器端和客户端两部分,其主要实现过程如图1.1所示。

下面介绍UDP网络编程的详细函数和过程:

 1、socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。

-----------------------------------------------------------------
 #include <sys/socket.h>
 int socket(int family,int type,int protocol);    
      返回:非负描述字---成功   -1---失败
 -----------------------------------------------------------------

第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。

2、bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。

-------------------------------------------------------------------
#include <sys/socket.h>  
 int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen);
 返回:0---成功   -1---失败 
 -------------------------------------------------------------------

  第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。

3、recvfrom函数:UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明目的地址。

-------------------------------------------------------------------
#include <sys/types.h>  
#include <sys/socket.h>  
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
 返回接收到数据的长度---成功   -1---失败 
 -------------------------------------------------------------------

  前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。最后两个参数类似于accept的最后两个参数。

4、sendto函数:UDP使用sendto()函数发送数据,他类似于标准的write(),但是在sendto()函数中要指明目的地址。

-------------------------------------------------------------------
#include <sys/types.h>  
#include <sys/socket.h>  
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
 返回发送数据的长度---成功   -1---失败 
 -------------------------------------------------------------------

  前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。参数to指明数据将发往的协议地址,他的大小由addrlen参数来指定。

server.c程序源码:

  #include <stdio.h>                                                                                                                 
  #include <sys/socket.h>
  #include <stdlib.h>
  #include <netinet/in.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <string.h>
 
  #define _MAXLINE_ 80
  #define _SERV_PORT_  8000
  
  int main()
  {
      struct sockaddr_in server_addr,client_addr;
      socklen_t cliaddr_len;
      int sockfd;
      char buf[_MAXLINE_];
      char str[INET_ADDRSTRLEN];
      int i, n;
  
      sockfd = socket(AF_INET, SOCK_DGRAM, 0);
      if(sockfd < 0){
          printf("create socket error!...\n");
          return -1;
      }
  
      bzero(&server_addr,sizeof(server_addr));
      server_addr.sin_family = AF_INET;
      server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
      server_addr.sin_port = htons(_SERV_PORT_);
  
      if(bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
          printf("bind error!...");
          close(sockfd);
          return 1;
      }
  
      printf("Accepting connections ...\n");
      while(1){
          cliaddr_len = sizeof(client_addr);
          //n = recvfrom(sockfd, buf, _MAXLINE_, 0,(struct sockaddr *)&client_addr, &cliaddr_len);
          n = recvfrom(sockfd, buf, _MAXLINE_, 0,(struct sockaddr *)&client_addr, &cliaddr_len);
          if(n == -1){
              printf("recvfrom error...\n");
              exit(1);
          }
          printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &client_addr.sin_addr, 
                  str,sizeof(str)),ntohs(client_addr.sin_port));
  
          for(i = 0; i < n; i++){
              buf[i] = toupper(buf[i]);
          }
          n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&client_addr, sizeof(client_addr));
          if(n == -1){
              printf("sendto error...\n");
          }
      }
      return 0;
  }

UDP客户端程序设计

client.c程序源码:

   #include <stdio.h>                                                                                                                                       
   #include <string.h>
   #include <unistd.h>
   #include <netinet/in.h>
   
   #define _MAXLINE_ 80
   #define _SERV_PORT_  8000
   
   int main(int argc, int* argv[])
  {
      struct sockaddr_in servaddr;
      int sockfd, n;
      char buf[_MAXLINE_];
      char str[INET_ADDRSTRLEN];
      socklen_t servaddr_len;
  
      sockfd = socket(AF_INET, SOCK_DGRAM, 0);

      bzero(&servaddr, sizeof(servaddr));
      servaddr.sin_family = AF_INET;
      inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
      servaddr.sin_port = htons(_SERV_PORT_);
  
      while(fgets(buf, _MAXLINE_, stdin)!= NULL){
          n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)                  &servaddr, sizeof(servaddr));
          if(n == -1){
              printf("sendto error\n");
          }
          n = recvfrom(sockfd, buf, _MAXLINE_, 0, NULL, 0);
          if(n == -1){
              printf("recvfrom error\n");
          }
          write(STDOUT_FILENO, buf, n);
      }
  
      close(sockfd);
      return 0;
  }


运行展示,client发送小写字母,server收到之后进行大小写转换,发回到client,结果如下:

技术分享图片


以上是关于基于UDP协议的网络程序的主要内容,如果未能解决你的问题,请参考以下文章

基于 UDP 协议的网络编程

网络 -- UDP知识

网络 -- UDP知识

java 网络编程 基于UDP协议的通信

Java中基于UDP协议的的网络编程(基础篇)

Raknet是一个基于UDP网络传输协议的C++网络库(还有一些其它库,比如nanomsg,fastsocket等等)