Linux——传输层基于UDP协议的套接字编程实例

UDP

1.含义:udp协议是传输层的一种协议。(User Datagram Protocol用户数据报协议)

2.特点:无连接、不可靠、面向数据报。

  无连接:不用向服务端建立连接

  不可靠:数据传输的过程是一个不可靠的--数据可能会丢失

  面向数据报:数据报的传输方式

3.流程:

4.实现:

(1)首先封装一个UdpSocket类来实现其基本的功能

//传输层基于UDP协议的网络通信                             
//    1.创建套接字                
//    2.绑定地址信息 
//    3.发送数据
//    4.接收数据                    
//    5.关闭套接字                
#include <iostream>       
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string>
using namespace std;  
                                
#define CHECK_RET(q) if((q) == false){return -1;}
       
class UdpSocket{     
  public:         
    //将_sockfd的初始值给-1,
    UdpSocket():_sockfd(-1){}
                 
    //1.创建套接字
    bool Socket(){
      //socket(int domain, int type, int proto);
      //    domain:地址域的类型--ipv4/ipv6               
      //    type  :套接字类型--数据报/字节流
       //    proto :协议的类型--udp/tcp
       //  返回值:
       //    成功--0;失败--<-1;
       _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
       if(_sockfd < 0){
         cerr << "socket error\n";
         return false;
       } 
       return true;
     }
 
     //2.绑定地址信息
     bool Bind(string& ip, u_int16_t port){
       sockaddr_in addr;
       addr.sin_family = AF_INET;
       addr.sin_addr.s_addr = inet_addr(&ip[0]);
       addr.sin_port = htons(port);
       socklen_t len = sizeof(addr);
       //bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
       //    sockfd    :套接字描述符                                        
       //    addr      :地址信息
       //    addrlen   :地址结构的大小--字节
       //  返回值:
       //    成功--0; 失败--》-1;
       int ret = bind(_sockfd, (sockaddr*)&addr, len);
      if(ret < 0){
        cerr << "bind error\n";
        return false;
      }
      return true;
    }

    //3.发送数据
    bool Send(string& buf, string& ip, u_int16_t& port){
      //sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
     //     sockfd    :套接字描述符
     //     buf       :发送的字符串的首地址
     //     len       :发送的数据的长度
     //     flags     :标志位,0--阻塞发送
     //     dest_addr :目的端地址
     //     addrlen   :地址大小
     //   返回值:
     //     成功--返回已发送的字符数目;失败--》-1
      sockaddr_in addr;                                                                                             
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = inet_addr(&ip[0]);
      addr.sin_port = htons(port);
      socklen_t len = sizeof(addr);
      int ret = sendto(_sockfd, &buf[0], buf.size(), 0, (sockaddr*)&addr, len);
      if(ret < 0){
        cerr << "send error\n";
        return false;
      }
      return true;
    }

    //4.接收数据
    bool Recv(string& buf, string& ip, uint16_t& port){
      //recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
      //    sockfd      :套接字描述符
      //    buf         :接收到接收缓冲区
      //    len         :要接收的数据大小
      //    flags       :标志位,0--阻塞接收
      //    src_addr    :源端地址
      //    addrlen     :地址大小;源端地址非空--就是源端地址的大小;源端地址空--他就是NULL
      //  返回值:
      //    > 0 :接收到的字节数
      //    ==0 :表示连接已经断开
      //    -1  :接收错误
      char tmp[1024] = {0};
      sockaddr_in addr;                                                                                         
      socklen_t len = sizeof(addr);
      int ret = recvfrom(_sockfd, tmp, 1024, 0, (sockaddr*)&addr, &len);
      if(ret < 0 ){
        cerr << "recvfrom error\n";
        return false;
      }else if(ret == 0){
        cout << "peer shutdown\n";
        return false;
      }
      ip = inet_ntoa(addr.sin_addr);
      port = ntohs(addr.sin_port);
      buf.assign(tmp,ret);
      return true;
    }

    //5.关闭套接字
    bool Close(){
      if(_sockfd < 0){
        cerr << "close error\n";
        return false;
      }
      close(_sockfd);
      return true;
    }

    ~UdpSocket(){                                                                    
      Close();
    }
  private:
    int _sockfd;    //套接字描述符--文件描述符(网络通信的操作句柄)
};

(2)服务端程序

//传输层基于UDP协议的服务端程序                                  
//    1.创建套接字
//    2.绑定地址信息
//    3.接收客户端发送的数据
//    4.发送数据                
//    5.关闭套接字
#include "udp_socket.hpp"
                
int main(int argc, char* argv[]){
  //判断参数是否足够
  if(argc != 3){                 
    cout << "./udp_srv 192.168.136.146 9000\n";
    return -1;      
  }            
  string ip = argv[1];                  
  uint16_t port = atoi(argv[2]);         
                
  UdpSocket sock;
  //1.创建套接字
  CHECK_RET(sock.Socket());
  //2.绑定地址信息                      
  CHECK_RET(sock.Bind(ip, port));
  while(1){    
    //3.循环收发数据
    string buf;
    CHECK_RET(sock.Recv(buf, ip, port));
    cout << "client say:" << buf << endl;
    buf.clear();
    cin >> buf;

    //4.发送数据
    CHECK_RET(sock.Send(buf, ip, port)); 
  }
  sock.Close();
  return 0;
}

(3)客户端程序

//传输层基于UDP协议的客户端                                             
#include "udp_socket.hpp"
                
int main(int argc, char* argv[]){            
  //判断参数是否足够
  if(argc != 3){
    cout << "./udp_cli serverip serverport";
    return -1;                  
  }              
  string ip = argv[1];
  uint16_t port = atoi(argv[2]);
  UdpSocket sock;                   
  //1.创建套接字                   
  CHECK_RET(sock.Socket());
  //2.绑定地址信息--可以不用手动绑定
  //CHECK_RET(sock.Bind("192.168.136.148", 8000));
  //进入循环收发数据
  while(1){                             
    string buf; 
    cin >> buf;                                                 
    CHECK_RET(sock.Send(buf, ip, port));
    buf.clear();
    //buf.clear();    //发完以后缓冲区需要清空吗????????
    CHECK_RET(sock.Recv(buf, ip, port));
    cout << "server say:" << buf << endl;
  }
  sock.Close();
  return 0;
}                     

5.UDP协议的报头信息

16bitUDP长度:报头+数据;也就是说udp所能发送的数据长度最大是64k-8(2^16  -  8);

16位的校验和:int sum = 0;sum +=~data[i];  

发布了58 篇原创文章 · 获赞 43 · 访问量 4393

猜你喜欢

转载自blog.csdn.net/Wz_still_shuai/article/details/101220459