1. 程式人生 > >基於UDP協議的網絡程序

基於UDP協議的網絡程序

UDP socket

下圖是基於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協議的網絡程序