首页 > C语言网络编程:UDP通信实现

C语言网络编程:UDP通信实现

文章目录

          • UDP的特点:
          • UDP的用途
          • UDP编程模型
          • UDP通信代码实现

UDP的特点:
  1. udp 协议是一种无链接的不可靠传输协议,且UDP每次发送到分组数据大小都是固定的,它的主要特点如下:

    • 不建立连接
    • 没有应答机制
    • 不会根据网络状况的好坏调整分组数据的大小

    UDP之所以为不可靠传输协议,主要还是因为UDP没有链接和应答机制,导致UDP在发送数据的时候根本不清楚通信线路的连接情况,所有通过UDP发送数据的时候无法保证数据一定能够发送给对方。因此UDP时不可靠的传输协议

  2. UDP协议每次传输数据时,必须指定对方的IP和端口号

    因为udp没有连接的特性,所有UDP协议没有自动记录对方ip和端口号的特点,因为每次发送数据事,应用程序必须亲自指定对方的ip和端口号,只有这样才能将数据发送给对方。

    如果是跨网通信,指定的ip就是对方的路由器公网ip

UDP的用途
  1. 数据量打但是允许数据丢失的传输服务(音视频传输)
  2. 开发监控视频相关得网络传输数据程序时使用
UDP编程模型

UDP 编程模型非常简单,通信双发的实现基本一致,且没有服务器的概念,如下图

在这里插入图片描述

  1. 建立socket通信

    这里需要注意socket的内几个参数设置

    第一个参数仍然为tcp/ip协议族:AF_INET

    第二个参数为传输格式:SOCK_DGRAM
  2. 建立绑定,使用bind函数和自己的ip和端口进行绑定
  3. 使用sendto函数向对方发送数据,这里需要注意的是sendto函数需要填写对方ip和端口号的参数,因为udp通信时需要明确对方设备的ip和端口,所以发送数据包的时候需要对对方的ip和端口号进行指定

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,

    const struct sockaddr *dest_addr, socklen_t addrlen);
  4. 最后接收信息的时候会接收到从对方的ip和端口发送过来的信息,因为消息的接收需要知道它的来源,而struct sockaddr将会指定消息的来源。

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

    struct sockaddr *src_addr, socklen_t *addrlen);

PS:两个通信进程在同一主机不能出现端口冲突,所以以下代码测试是在同一主机上,此时需要设置不同的端口。

UDP通信代码实现

UDP_A.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define IP "192.168.102.174"
#define PORT 6000typedef struct data { char name[30];unsigned int num;
}Data;void print_err(char *str, int line, int err_no) { printf("%d, %s :%s
",line,str,strerror(err_no));_exit(-1);
}int cfd = -1;
//接收线程函数
void *receive(void *pth_arg) { int ret = 0;Data stu_data = { 0};struct sockaddr_in addr0 = { 0};int addr0_size = sizeof(addr0);//从对端ip和端口号中接收消息,指定addr0用于存放消息while(1) { bzero(&stu_data, sizeof(stu_data));ret = recvfrom(cfd, &stu_data, sizeof(stu_data),0, (struct sockaddr *)&addr0, &addr0_size);	if (-1 == ret) { print_err("recv failed",__LINE__,errno);}else if (ret > 0){ printf("student number = %d student name = %s 
",ntohl(stu_data.num),stu_data.name);//打印对方的消息和端口号printf("ip %s,port %d
",inet_ntoa(addr0.sin_addr),ntohs(addr0.sin_port));}}
}int main()
{ int ret = -1;//创建tcp/ip协议族,指定通信方式为无链接不可靠的通信cfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == cfd) { print_err("socket failed", __LINE__, errno);}//进行端口号和ip的绑定struct sockaddr_in addr;addr.sin_family = AF_INET; //设置tcp协议族addr.sin_port = htons(PORT); //设置端口号addr.sin_addr.s_addr = inet_addr(IP); //设置ip地址ret = bind(cfd, (struct sockaddr*)&addr, sizeof(addr));if ( -1 == ret) { print_err("bind failed",__LINE__,errno);}//创建线程函数,用于处理数据接收pthread_t id;ret = pthread_create(&id,NULL,receive,NULL);if (-1 == ret) print_err("pthread_create failed", __LINE__, errno);struct sockaddr_in addr0;addr0.sin_family = AF_INET; //设置tcp协议族addr0.sin_port = htons(7000); //设置端口号addr0.sin_addr.s_addr = inet_addr(IP); //设置ip地址Data std_data = { 0};//发送消息while (1) { bzero(&std_data, sizeof(std_data));printf("stu name:
");scanf("%s",std_data.name);printf("stu num:
");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);//发送消息时需要绑定对方的ip和端口号	ret = sendto(cfd, (void *)&std_data,sizeof(std_data), 0, (struct sockaddr *)&addr0, sizeof(addr0));if ( -1 == ret) { print_err("accept failed", __LINE__, errno);}	}return 0;
}

另一端的实现基本和UDP_A.c一样,因为是本机测试,只有在发送的时候设置的端口号不一样。如果跨网通信,则需要设置发送时指定的ip为 对方路由器的公网ip

UDP_B.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define IP "192.168.102.174"
#define PORT 7000typedef struct data { char name[30];unsigned int num;
}Data;void print_err(char *str, int line, int err_no) { printf("%d, %s :%s
",line,str,strerror(err_no));_exit(-1);
}int cfd = -1;
void *receive(void *pth_arg) { int ret = 0;Data stu_data = { 0};struct sockaddr_in addr0 = { 0};int addr0_size = sizeof(addr0);while(1) { bzero(&stu_data, sizeof(stu_data));ret = recvfrom(cfd, &stu_data, sizeof(stu_data),0, (struct sockaddr *)&addr0, &addr0_size);	if (-1 == ret) { print_err("recv failed",__LINE__,errno);}else if (ret > 0){ printf("student number = %d student name = %s 
",ntohl(stu_data.num),stu_data.name);printf("ip %s,port %d
",inet_ntoa(addr0.sin_addr),ntohs(addr0.sin_port));}}
}int main()
{ int ret = -1;cfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == cfd) { print_err("socket failed", __LINE__, errno);}struct sockaddr_in addr;addr.sin_family = AF_INET; //设置tcp协议族addr.sin_port = htons(PORT); //设置端口号addr.sin_addr.s_addr = inet_addr(IP); //设置ip地址ret = bind(cfd, (struct sockaddr*)&addr, sizeof(addr));if ( -1 == ret) { print_err("bind failed",__LINE__,errno);}pthread_t id;ret = pthread_create(&id,NULL,receive,NULL);if (-1 == ret) print_err("pthread_create failed", __LINE__, errno);struct sockaddr_in addr0;addr0.sin_family = AF_INET; //设置tcp协议族addr0.sin_port = htons(6000); //设置端口号addr0.sin_addr.s_addr = inet_addr(IP); //设置ip地址Data std_data = { 0};while (1) { bzero(&std_data, sizeof(std_data));printf("stu name:
");scanf("%s",std_data.name);printf("stu num:
");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);ret = sendto(cfd, (void *)&std_data,sizeof(std_data), 0, (struct sockaddr *)&addr0, sizeof(addr0));if ( -1 == ret) { print_err("accept failed", __LINE__, errno);}	}return 0;
}

编译运行:

gcc UDP_B.c -o B -pthread
gcc UDP_A.c -o A -pthread

运行如下:

在这里插入图片描述

更多相关:

  • 下面的源码给出了使用boost::asio进行域名解析的方法. //g++ -g resolver_demo.cpp -o resolver_demo -lboost_system -lpthread //#include #include #include

  • pc 端配置 点击pc端无线链接图标编辑链接以太网编辑IPv4设置方法:与其他计算机共享根据putty获取的动态ip使用ssh登录 解决 ip 无法登录问题 设置 pc 端 ip 和开发板 ip 在同一个网段 比如开发板 ip 是 10.42.0.123 可以设置自己机器的 ip 为 10.42.0.11 sudo ifcon...

  • 原文:TCP/IP 简介     第一节:TCP/IP 简介 第二节:TCP/IP 寻址 第三节:TCP/IP 协议 第四节:TCP/IP 邮件     TCP/IP 是用于因特网 (Internet) 的通信协议。     计算机通信协议 计算机通信协议是对那些计算机必须遵守以便彼此通信的规则的描述。     什么是 TC...

  • #保存一万条命令记录 sed -i 's/^HISTSIZE=1000/HISTSIZE=10000/g' /etc/profile#在/etc/profile的文件尾部添加如下行数配置信息 ######jiagu history xianshi######### USER_IP=`who -u am i 2>/dev/null |...

  • TCP/IP基础概念及通信过程举例   出现 上个世纪60年代,由于中央集中式网络的容灾性较弱,以美国国防部为中心的一家组织研究出分组交换网络。后来为了验证分组交换技术的实用性,ARPANET出现了,并且在3年内逐渐发展,由4个节点发展至34个节点。20世纪70年代前半叶,ARPANET一个机构研制出了TCP/IP,1982年具体规范...

  • 关于点云的分割算是我想做的机械臂抓取中十分重要的俄一部分,所以首先学习如果使用点云库处理我用kinect获取的点云的数据,本例程也是我自己慢慢修改程序并结合官方API 的解说实现的,其中有很多细节如果直接更改源程序,可能会因为数据类型,或者头文件等各种原因编译不过,会导致我们比较难得找出其中的错误,首先我们看一下我自己设定的一个场景,...

  • /* 使用正态分布变换进行配准的实验 。其中room_scan1.pcd room_scan2.pcd这些点云包含同一房间360不同视角的扫描数据 */ #include #include #include #include

  • #include #include #include #include ...

  • #include #include #include #include #include #include...

  • #include #include #include #include int main (int argc,...