Linux下使用poll函数编写UDP客户端、服务器程序

一、UDP服务器与客户端的区别

对于UDP服务器与客户端,两者都可以通过sendto和recvfrom函数收发数据,它们的主要区别是:

1.服务器一般是等待并响应来自客户端的请求,客户端则是主动发送请求并且等待服务器的响应。

2.服务器端要将地址和端口号绑定,如果不绑定就无法使用recvfrom函数接受数据(也就是说服务器需要调用bind函数将一个套接字与一个地址绑定,而客户端不需要)。所以对于UDP,其服务器与其说是服务端不如说是后发端。

所以如果阅读一份源码,要快速判断其是UDP服务器还是客户端,一个简单的方法是查看代码中是否调用了bind函数就可以了。

二、UDP是否可以使用select/poll/epoll

UDP是一种无连接的传输协议,因此通常情况下没有必要使用多路复用。UDP只是逐个接收数据段(数据包),并按照其规则进行处理,其并不关心单个数据包所属的任何特定数据流或数据包。但是UDP是可以使用select/poll/epoll的,某些开源软件,比如FFmpeg内部使用了这些多路复用来处理UDP。

三、使用poll函数编写UDP客户端、服务器程序

代码摘自:《IPV4 UDP server client program with Poll system call》

UDP客户端代码udpClient.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <poll.h>#define BUFFER_SIZE 1024int client_socket = -1;static void sigint_handler(int signo)
{(void)close(client_socket);sleep(2);(void)printf("Caught sigINT!\n");exit(EXIT_SUCCESS);
}void validate_convert_port(
char *port_str,
struct sockaddr_in *sock_addr)
{int port;if (port_str == NULL) {perror("Invalid port_str\n");exit(EXIT_FAILURE);}if (sock_addr == NULL) {perror("Invalid sock_addr\n");exit(EXIT_FAILURE);}port = atoi(port_str);if (port == 0) {perror("Invalid port\n");exit(EXIT_FAILURE);}sock_addr->sin_port = htons((uint16_t)port);printf("Port: %d\n",ntohs(sock_addr->sin_port));
}void validate_convert_addr(
char *ip_str,
struct sockaddr_in *sock_addr)
{if (ip_str == NULL) {perror("Invalid ip_str\n");exit(EXIT_FAILURE);}if (sock_addr == NULL) {perror("Invalid sock_addr\n");exit(EXIT_FAILURE);}printf("IP Address: %s\n", ip_str);if (inet_pton(AF_INET, ip_str,&(sock_addr->sin_addr)) <= 0) {perror("Invalid address\n");exit(EXIT_FAILURE);}
}void recv_data(char *buffer)
{int ret, len;len = recvfrom(client_socket,buffer, BUFFER_SIZE, 0, NULL, NULL);if (len > 0) {buffer[len] = '\0';(void)printf("Received: %s\n",buffer);} else if (len == 0) {printf("Connection closed\n");exit(EXIT_FAILURE);}
}void register_signal_handler(
int signum,
void (*handler)(int))
{if (signal(signum, handler) == SIG_ERR){printf("Cannot handle signal\n");exit(EXIT_FAILURE);}
}int main(int argc, char *argv[])
{int ret, len;struct sockaddr_inserver_addr;char buffer[BUFFER_SIZE];struct pollfd fds[1];char *str = "HI";register_signal_handler(SIGINT,sigint_handler);if (argc != 3) {printf("%s<port-number><ip-addr>\n",argv[0]);exit(EXIT_FAILURE);}memset(&server_addr, 0,sizeof(server_addr));server_addr.sin_family = AF_INET;validate_convert_port(argv[1],&server_addr);validate_convert_addr(argv[2],&server_addr);client_socket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if (client_socket < 0) {perror("socket");return -1;}while (1) {ret = sendto(client_socket, str,strlen(str), 0,(struct sockaddr*)&server_addr,sizeof(server_addr));printf("sendbuffer = %s\n", str);if (ret < 0) {perror("send error\n");(void)close(client_socket);break;}fds[0].fd = client_socket;  fds[0].events = POLLIN;ret = poll(fds, 2, 1000);if (ret < 0) {perror("poll");(void)close(client_socket);break;}if (fds[0].revents & POLLIN) {recv_data(buffer);}}(void)close(client_socket);return 0;
}

UDP服务器代码udpServer.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <poll.h>#define BUFFER_SIZE 1024int server_socket = -1;static void sigint_handler(int signo)
{(void)close(server_socket);sleep(2);(void)printf("Caught sigINT!\n");exit(EXIT_SUCCESS);
}void register_signal_handler(
int signum,
void (*handler)(int))
{if (signal(signum, handler) == SIG_ERR) {printf("Cannot handle signal\n");exit(EXIT_FAILURE);}
}void validate_convert_port(
char *port_str,
struct sockaddr_in *sock_addr)
{int port;if (port_str == NULL) {perror("Invalid port_str\n");exit(EXIT_FAILURE);}if (sock_addr == NULL) {perror("Invalid sock_addr\n");exit(EXIT_FAILURE);}port = atoi(port_str);if (port == 0) {perror("Invalid port\n");exit(EXIT_FAILURE);}sock_addr->sin_port = htons((uint16_t)port);printf("Port: %d\n",ntohs(sock_addr->sin_port));
}void recv_send(
char *buffer,
struct sockaddr_in *client_addr)
{int len, ret;socklen_t client_addr_len = sizeof(client_addr);len = recvfrom(server_socket,buffer, BUFFER_SIZE, 0,(struct sockaddr*)&client_addr,&client_addr_len);if (len > 0) {buffer[len] = '\0';printf("Received: %s\n",buffer);memset(buffer, 0,sizeof(buffer));strncpy(buffer, "HELLO",strlen("HELLO") + 1);buffer[strlen(buffer) + 1] = '\0';ret = sendto(server_socket,buffer,strlen(buffer), 0,(struct sockaddr*)&client_addr,client_addr_len);if (ret < 0) {perror("sendto");exit(EXIT_FAILURE);}} else if (len < 0) {perror("recvfrom");exit(EXIT_FAILURE);}printf("Sentbuffer = %s\n",buffer);
}int main(int argc, char *argv[])
{int ret;struct sockaddr_inserver_addr,client_addr;char buffer[BUFFER_SIZE];struct pollfd fds[1];register_signal_handler(SIGINT,sigint_handler);if (argc != 2) {printf("%s <port-number>",argv[0]);exit(EXIT_FAILURE);}memset(&server_addr, 0,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr =INADDR_ANY;validate_convert_port(argv[1],&server_addr);server_socket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if (server_socket < 0) {perror("socket");return -1;}ret = bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr));if (ret < 0) {perror("bind");(void)close(server_socket);return -2;}printf("UDP listining\n");memset(fds, 0, sizeof(fds));fds[0].fd = server_socket;fds[0].events = POLLIN;while (1) {ret = poll(fds, 1, 1000);if (ret < 0) {perror("poll");break; }if (fds[0].revents & POLLIN) {recv_send(buffer, &client_addr);}}(void)close(server_socket);return 0;
}

编译:

gcc udpClient.c -o udpClient -g
gcc udpServer.c -o udpServer -g

运行:

客户端执行命令:

./udpClient 1234 127.0.0.1

服务器执行命令:

./udpServer 1234

运行后两者即可通讯。

从上面的代码可以看出来,上述UDP客户端和服务器都使用了poll函数监视文件描述符是否可读。区别在于服务器调用了bind函数将套接字与地址绑定,而客户端没有调用bind函数而已。

四、参考

《UDP服务器与客户端之间的区别?》

《UDP协议为什么分客户端和服务器端》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/17098.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

迅为RK3568开发板篇OpenHarmony实操HDF驱动配置LED-LED测试

将编译好的镜像全部进行烧写&#xff0c;镜像在源码根目录 out/rk3568/packages/phone/images/目录下。 烧写完成之后&#xff0c;在调试串口查看打印日志&#xff0c;如下图所示&#xff1a; 然后打开 hdc 工具&#xff0c;运行测试程序&#xff0c;输入“led_test 1”&…

在VS2022中配置DirectX12环境,并显示显示一个窗口

1.创建空项目并配置项目&#xff1a; 1.打开VS2022,创建C项目中的空项目 2.新建一个Main.cpp文件 3.配置项目 将属性页的C/C项中的语言栏的符合模式设置为否 再将链接器中的系统栏的子系统设置为窗口 设置完成&#xff01; 2.创建一个Windows窗口&#xff1a; 代码&#…

AI前端开发:蓬勃发展的机遇与挑战

人工智能&#xff08;AI&#xff09;领域的飞速发展&#xff0c;正深刻地改变着我们的生活方式&#xff0c;也为技术人才&#xff0c;特别是AI代码生成领域的专业人士&#xff0c;带来了前所未有的机遇。而作为AI应用与用户之间桥梁的前端开发&#xff0c;其重要性更是日益凸显…

DeepSeek+即梦 做AI视频

DeepSeek做AI视频 制作流程第一步&#xff1a;DeepSeek 生成视频脚本和分镜 第二步&#xff1a;生成分镜图片绘画提示词第三步&#xff1a;生成分镜图片第四步&#xff1a;使用可灵 AI 工具&#xff0c;将生成的图片转成视频。第五步&#xff1a;剪映成短视频 DeepSeek 真的强&…

数组练习(深入理解、实践数组)

1.练习1&#xff1a;多个字符从两端移动&#xff0c;向中间汇聚 编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() {//解题思路&#xff1a;//根据题意再…

学习threejs,使用HemisphereLight半球光

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.HemisphereLight 二、…

vue项目使用vite和vue-router实现history路由模式空白页以及404问题

开发项目的时候&#xff0c;我们一般都会使用路由&#xff0c;但是使用hash路由还是history路由成为了两种选择&#xff0c;因为hash路由在url中带有#号&#xff0c;history没有带#号&#xff0c;看起来更加自然美观。但是hash速度更快而且更通用&#xff0c;history需要配置很…

Fiori APP配置中的Semantic object 小bug

在配置自开发程序的Fiori Tile时&#xff0c;需要填入Semantic Object。正常来说&#xff0c;是需要通过事务代码/N/UI2/SEMOBJ来提前新建的。 但是在S4 2022中&#xff0c;似乎存在一个bug&#xff0c;即无需新建也能输入自定义的Semantic Object。 如下&#xff0c;当我们任…

芯片设计企业的IT支撑点

对于一个芯片设计企业&#xff0c;需要怎么样的IT支撑&#xff0c;这看起来并不是那么重要&#xff0c;并不影响芯片企业是否取得成功&#xff0c;但真正进入这个行业&#xff0c;你会发现&#xff0c;这里还是有一些门道的。 实际上&#xff0c;芯片设计企业对于IT的依赖很重&…

生成对抗网络入门:Mnist手写数字生成

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 理论基础 生成对抗网络(Generative Adversarial Networks,GAN)是近年来深度学习领域的一个热点方向。 GAN并不指代某一个具体的神经网络&#xff0c;而是指一类基于博弈思想而设计的神经网络。…

22.4、Web应用漏洞分析与防护

目录 Web应用安全概述DWASP Top 10Web应用漏洞防护 - 跨站脚本攻击XSSWeb应用漏洞防护 - SQL注入Web应用漏洞防护 - 文件上传漏洞Web应用漏洞防护 - 跨站脚本攻击XSS Web应用安全概述 技术安全漏洞&#xff0c;主要是因为技术处理不当而产生的安全隐患&#xff0c;比如SQL注入…

软件的生命周期和需求

什么是软件的生命周期? 定义(描述) --> 创建 --> 使用 --> 销毁 (这一整个过程就是事物的生命周期) 生命周期 那么软件的生命周期又分为哪些呢? 一共分为十步: 可行性研究: 通过分析软件开发要求,确定软件项目的性质、目标和规模,得出可行性研究报告,如果可行性研…

深入理解DeepSeek与企业实践(二):32B多卡推理的原理、硬件散热与性能实测

前言 在《深入理解 DeepSeek 与企业实践&#xff08;一&#xff09;&#xff1a;蒸馏、部署与评测》文章中&#xff0c;我们详细介绍了深度模型的蒸馏、量化技术&#xff0c;以及 7B 模型的部署基础&#xff0c;通常单张 GPU 显存即可满足7B模型完整参数的运行需求。然而&…

Java 字符编码与解码:深入理解 Charset 类

目录 引言 一、什么是字符集&#xff08;Charset&#xff09;&#xff1f; 二、Charset 类的核心功能 1. 获取字符集实例 2. 编码与解码 示例1&#xff1a;字符串转字节数组 示例2&#xff1a;处理不同字符集的乱码问题 3. 字符集检测与支持 三、Charset 类的常用方法…

Redis7.0八种数据结构底层原理

导读 本文介绍redis应用数据结构与物理存储结构,共八种应用数据结构和 一. 内部数据结构 1. sds sds是redis自己设计的字符串结构有以下特点: jemalloc内存管理预分配冗余空间二进制安全(c原生使用\0作为结尾标识,所以无法直接存储\0)动态计数类型(根据字符串长度动态选择…

本地Deepseek-r1:7b模型集成到Google网页中对话

本地Deepseek-r1:7b网页对话 基于上一篇本地部署的Deepseek-r1:7b&#xff0c;使用黑窗口对话不方便&#xff0c;现在将本地模型通过插件集成到Google浏览器中 安装Google插件 在Chrome应用商店中搜索page assis 直接添加至Chrome 修改一下语言 RAG设置本地运行的模型&#…

【设计模式】【行为型模式】观察者模式(Observer)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

gitlab Webhook 配置jenkins时“触发远程构建 (例如,使用脚本)”报错

报错信息&#xff1a; <html> <head> <meta http-equiv"Content-Type" content"text/html;charsetISO-8859-1"/> <title>Error 403 No valid crumb was included in the request</title> </head> <body><h2…

AI赋能前端开发:薪资潜力无限的未来

在当今竞争激烈的就业市场&#xff0c;掌握AI写代码工具等AI技能已经成为许多专业人士提升竞争力的关键。尤其在快速发展的前端开发领域&#xff0c;AI的应用更是日新月异&#xff0c;为开发者带来了前所未有的机遇。高薪职位对熟练掌握AI技术的前端开发者的需求与日俱增&#…

外包干了4年,技术退步太明显了。。。。。

先说一下自己的情况&#xff0c;本科生生&#xff0c;20年通过校招进入武汉某软件公司&#xff0c;干了差不多4年的功能测试&#xff0c;今年国庆&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能…