Linux下使用C/C++进行UDP网络编程详解

 更新时间:2024年10月30日 09:48:54   作者:袁本美  
UDP 是User Datagram Protocol 的简称,中文名是用户数据报协议,是一种无连接、不可靠的协议,本文主要介绍了如何在Linux下使用C/C++进行UDP网络编程,有需要的可以了解下

UDP 是User Datagram Protocol 的简称,中文名是用户数据报协议,是一种无连接、不可靠的协议,同样它也是工作在传顺层。它只是简单地实现从一端主机到另一端主机的数据传输功能,这些数据通过 IP 层发送,在网络中传输,到达目标主机的顺序是无法预知的,因此需要应用程序对这些数据进行排序处理,这就带来了很大的不方便,此外,UDP 协议更没有流量控制、拥塞控制等功能,在发送的一端,UDP 只是把上层应用的数据封装到UDP 报文中,在差错检测方面,仅仅是对数据进行了简单的校验,然后将其封装到 IP 数据报中发送出去。而在接收端,无论是否收到数据,它都不会产生一个应答发送给源主机,并且如果接收到数据发送校验错误,那么接收端就会丢弃该UDP 报文,也不会告诉源主机,这样子传输的数据是无法保障其准确性的,如果想要其准确性,那么就需要应用程序来保障了。

UDP 协议的特点:

  • 无连接、不可靠;
  • 尽可能提供交付数据服务,出现差错直接丢弃,无反馈;
  • 面向报文,发送方的UDP 拿到上层数据直接添加个UDP 首部,然后进行校验后就递交给 IP 层而接收的一方在接收到UDP 报文后简单进行校验,然后直接去除数据递交给上层应用;
  • 速度快,因为UDP 协议没有TCP 协议的握手、确认、窗口、重传、拥塞控制等机制,UDP 是一个无状态的传输协议,所以它在传递数据时非常快,即使在网络拥塞的时候UDP 也不会降低发送的数据。 UDP 虽然有很多缺点,但也有自己的优点,所以它也有很多的应用场合,因为在如今的网络环境下, UDP 协议传输出现错误的概率是很小的,并且它的实时性是非常好,常用于实时视频的传输,比如直播、网络电话等,因为即使是出现了数据丢失的情况,导致视频卡帧,这也不是什么大不了的事情,所以,UDP协议还是会被应用与对传输速度有要求,并且可以容忍出现差错的数据传输中。

在Linux使用socket网络编程实现udp通信流程如下:

1.  初始化socket

int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); 
if(sock_fd < 0){
    perror("failed to open socket");
    return -1;
}

2.  绑定IP和端口号

/** 绑定IP和端口号 */ 
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP
server_addr.sin_port = htons(8888);       // 指定端口号
int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret < 0)
{
    perror("failed to bind");
    close(sock_fd);
    return -1;
}

3. 设置组播接收(可选)

std::string multi_addr = "224.0.0.10";
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str());
mreq.imr_interface.s_addr = INADDR_ANY;
ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq));
if (0 >ret)
{
    perror("set socket multicast error");
    return false;
}

4. 设置接收超时(可选)

/** 设置接收超时(可选) */
int mill_sec = 2000;  // 毫秒
struct timeval time_out;
time_out.tv_sec = mill_sec / 1000;
time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000;
ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval));
if(ret < 0)
{
    perror("udp setTimeOut error!");
}

5.  发送数据

unsigned char buf[1024];
std::string ip = "192.168.1.10";
int port = 1234;
struct sockaddr_in client{};
memset(&client, 0, sizeof(client));
client.sin_addr.s_addr = inet_addr(ip.c_str());
client.sin_family = AF_INET;
client.sin_port = htons(port);
ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr));
if(ret < 0){
    perror("udpServer send error!");
}else{
    std::cout << "send success!" << std::endl;
}

6. 接收数据

unsigned char buffer[1024];
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
while(true)
{
    memset(buffer, 0, sizeof(buffer));
    ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len);
    if(len > 0)
    {
        std::cout << "received message len : " << len << std::endl;
    }else{
        perror("recv error");
    }
}

7. 完整代码

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netdb.h>
#include <net/if.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <thread>
 
void recv_func(int sock_fd_)
{
unsigned char buffer[1024];
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
while(true)
{
    memset(buffer, 0, sizeof(buffer));
    ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len);
    if(len > 0)
    {
        std::cout << "received message len : " << len << std::endl;
    }else{
        perror("recv error");
    }
}
}
 
int main(int agrc, char** argv)
{
    int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); 
    if(sock_fd < 0){
        perror("failed to open socket");
        return -1;
    }
 
    /** 绑定IP和端口号 */ 
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP
    server_addr.sin_port = htons(8888);       // 指定端口号
    int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(ret < 0)
    {
        perror("failed to bind");
        close(sock_fd);
        return -1;
    }
 
    /** 设置组播接收 (可选)*/
std::string multi_addr = "224.0.0.10";
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str());
mreq.imr_interface.s_addr = INADDR_ANY;
ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq));
if (0 >ret)
{
    perror("set socket multicast error");
    return false;
}
 
/** 设置接收超时(可选) */
int mill_sec = 2000;  // 毫秒
struct timeval time_out;
time_out.tv_sec = mill_sec / 1000;
time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000;
ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval));
if(ret < 0)
{
    perror("udp setTimeOut error!");
}
 
 
    /** 开启线程接收 */
    std::thread recv_t(recv_func, sock_fd);
    recv_t.detach();
 
    /** 主线程发送 */
    while (true)
    {
unsigned char buf[1024];
std::string ip = "192.168.1.10";
int port = 1234;
struct sockaddr_in client{};
memset(&client, 0, sizeof(client));
client.sin_addr.s_addr = inet_addr(ip.c_str());
client.sin_family = AF_INET;
client.sin_port = htons(port);
ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr));
if(ret < 0){
    perror("udpServer send error!");
}else{
    std::cout << "send success!" << std::endl;
}
        
        usleep(50*1000);
    }
}

8. 编译运行

# 编译
g++ udp_main.cpp -o main -lpthread
# 运行
./main

以上就是Linux下使用C/C++进行UDP网络编程详解的详细内容,更多关于C++ UDP网络编程的资料请关注脚本之家其它相关文章!

相关文章

  • C++实现LeetCode(647.回文子字符串)

    C++实现LeetCode(647.回文子字符串)

    这篇文章主要介绍了C++实现LeetCode(647.回文子字符串),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++通信新特性协程详细介绍

    C++通信新特性协程详细介绍

    这篇文章主要给大家分享得是C++的特性协程Coroutine,下面文章内容我们将来具体介绍什么是协程,协程得好处等知识点,需要的朋友可以参考一下
    2022-10-10
  • 深入解析C++ Data Member内存布局

    深入解析C++ Data Member内存布局

    本篇文章是对C++中的Data Member内存布局进行了详细的分析介绍,需要的朋友参考下
    2013-07-07
  • C++:函数对象,STL提供的函数对象,函数适配器详解

    C++:函数对象,STL提供的函数对象,函数适配器详解

    这篇文章主要介绍了C++:函数对象,STL提供的函数对象,函数适配器的使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-08-08
  • C++构造函数详解

    C++构造函数详解

    这篇文章主要介绍了C++构造函数详解,上一篇文章我们介绍了定义了类,在使用之前,往往还需要对类进行初始化。这篇介绍的就是对类进行初始化的方法,需要的朋友可以参考一下
    2022-01-01
  • C++基于文件流与armadillo读取mnist示例详解

    C++基于文件流与armadillo读取mnist示例详解

    这篇文章主要给大家介绍了关于C++基于文件流与armadillo读取mnist的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • C 语言编写一个计算器界面(可视化界面和多功能)

    C 语言编写一个计算器界面(可视化界面和多功能)

    今天给大家分享一个计算器功能,主要功能有加法减法乘除法求余功能,用户可以在主菜单选择需要计算的功能,接下来根据用户输入的数字进行计算输出结果,喜欢的朋友拿去用吧
    2021-06-06
  • C语言自增(++)和自减(--)实例详解

    C语言自增(++)和自减(--)实例详解

    本篇文章主要介绍了C语言的自增和自减的基本知识,并附有代码示例,以便大家理解,有需要的朋友可以看下
    2016-07-07
  • C语言数据结构之双链表&循环链表&静态链表详解

    C语言数据结构之双链表&循环链表&静态链表详解

    这篇文章主要为大家详细介绍了C语言数据结构中双链表&循环链表&静态链表的原理与使用,文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-09-09
  • c++将vector迭代器转换为指针的实现方式

    c++将vector迭代器转换为指针的实现方式

    这篇文章主要介绍了c++将vector迭代器转换为指针的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论