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

news2025/2/18 10:11:36

一、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 1024

int 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_in
  server_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 1024

int 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_in
  server_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.coloradmin.cn/o/2297775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法17(力扣217)存在重复元素

1、问题 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 2、示例 &#xff08;1&#xff09; 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;…

NO.16十六届蓝桥杯备战|for循环|七道习题|ceil|floor|pow(C++)

for循环 for循环语法形式 for 循环是三种循环中使⽤最多的&#xff0c; for 循环的语法形式如下&#xff1a; //形式1 for(表达式1; 表达式2; 表达式3) 语句;//形式2 //如果循环体想包含更多的语句&#xff0c;可以加上⼤括号 for(表达式1; 表达式2; 表达式3) { …

深度学习实战基础案例——卷积神经网络(CNN)基于DenseNet的眼疾检测|第4例

文章目录 前言一、数据准备二、项目实战2.1 设置GPU2.2 数据加载2.3 数据预处理2.4 数据划分2.5 搭建网络模型2.6 构建densenet1212.7 训练模型2.8 结果可视化 三、UI设计四、结果展示总结 前言 在当今社会&#xff0c;眼科疾病尤其是白内障对人们的视力健康构成了严重威胁。白…

(一)Axure制作移动端登录页面

你知道如何利用Axure制作移动端登录页面吗&#xff1f;Axure除了可以制作Web端页面&#xff0c;移动端也是可以的哦&#xff0c;下面我们就一起来看一下Axure制作移动端登录页面的过程吧。 第一步&#xff1a;从元件中拖入一个矩形框&#xff0c;并设置其尺寸为&#xff1a;37…

【Linux】【进程】epoll内核实现

【Linux】【进程】epoll内核实现 1 epoll提供的三个函数 1.1 epoll_create(int size); epoll_create()成功返回内核事件表的文件描述符&#xff0c;失败返回-1size 参数现在并不起作用 1.2 epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epoll_ctl()成…

ICRA-2025 | 具身导航如何跨越地形障碍?SARO:通过视觉语言模型实现地形穿越

作者&#xff1a;Shaoting Zhu, Derun Li, Linzhan Mou, Yong Liu, Ningyi Xu, Hang Zhao 单位&#xff1a;清华大学交叉信息研究院&#xff0c;上海交通大学电子信息与电气工程学院&#xff0c;浙江大学计算机科学与技术学院&#xff0c;宾夕法尼亚大学GRASP实验室&#xff0…

当 LSTM 遇上 ARIMA!!

大家好&#xff0c;我是小青 ARIMA 和 LSTM 是两种常用于时间序列预测的模型&#xff0c;各有优劣。 ARIMA 擅长捕捉线性关系&#xff0c;而 LSTM 擅长处理非线性和长时间依赖的关系。将ARIMA 和 LSTM 融合&#xff0c;可以充分发挥它们各自的优势&#xff0c;构建更强大的时…

终结磁盘空间紧张局面,针对性处理重复、无用文件

软件介绍 在如今这个数字化浪潮汹涌的时代&#xff0c;咱们的电脑存储空间就像一个杂乱无章的储物间&#xff0c;被各种各样的重复文件塞得满满当当。这些重复文件&#xff0c;犹如隐藏在暗处的 “空间小偷”&#xff0c;悄无声息地吞噬着宝贵的硬盘空间&#xff0c;使得原本井…

高校LabVIEW开发调试中的常见问题

在高校进行LabVIEW开发调试时&#xff0c;常常面临硬件选型不当、方案设计不合理、布线不专业以及人员流动性强等问题。这些问题可能影响项目的进展和质量。本文将总结这些问题&#xff0c;并给出具体的解决方案&#xff0c;帮助学生和团队更高效地开展开发工作。 ​ 1. 硬件选…

【故障处理】- RMAN-06593: platform name ‘Linux x86 64-bitElapsed: 00:00:00.00‘

【故障处理】- RMAN-06593: platform name Linux x86 64-bitElapsed: 00:00:00.00 一、概述二、报错原因三、解决方法 一、概述 使用xtts迁移&#xff0c;在目标端进行恢复时&#xff0c;遇到RMAN-06593: platform name Linux x86 64-bitElapsed: 00:00:00.00’报错。 二、报错…

K8S下载离线安装包所需文件

下载相关文件 官网下载地址集合https://kubernetes.io/zh-cn/releases/download/ 下载相关镜像 官网镜像描述 所有 Kubernetes 容器镜像都被部署到 registry.k8s.io 容器镜像仓库。 容器镜像支持架构registry.k8s.io/kube-apiserver:v1.32.0amd64, arm, arm64, ppc64le, …

如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…

ArcGIS注册开发账号及API KEY

注册与激活 Sign up | ArcGIS Location Platform 填写信息&#xff0c;然后邮箱收到激活邮件&#xff0c;激活&#xff0c;再补充信息。 参考 Tutorial: Create an API key | Documentation | Esri Developer 产生API KEY Tutorial: Create an API key | Documentation |…

java八股---java面向对象

面向对象 面向对象概述 面向对象和面向过程的区别 面向过程&#xff1a; 优点&#xff1a;性能比面向对象高&#xff0c;因为类调用时需要实例化&#xff0c;开销比较大&#xff0c;比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发&#xff0c;性能…

《Deepseek入门到精通》2.0版本《Deepseek赋能职场应用》清华大学

&#x1f680; 《Deepseek入门到精通》2.0版本重磅发布&#xff01; &#x1f4da; 全新升级&#xff0c;赋能职场应用&#xff01; 经过多次改版与优化&#xff0c;《Deepseek入门到精通》2.0版本已经正式上线&#xff01;这不仅是一份技术指南&#xff0c;更是你提升职场竞争…

使用c++实现红黑树的构建和插入

1.红黑树简介&#xff1a; 红黑树实际上和AVL都属于一棵用于存储数据的平衡二叉搜索树&#xff0c;但是这棵树并不是使用平衡因子去维持平衡的&#xff0c;而是结合限制条件对结点标红标黑去让树达到类似平衡的效果。 2.红黑树的限制条件和效率分析&#xff1a; 2.1限制条件…

在大型语言模型(LLM)框架内Transformer架构与混合专家(MoE)策略的概念整合

文章目录 传统的神经网络框架存在的问题一. Transformer架构综述1.1 transformer的输入1.1.1 词向量1.1.2 位置编码&#xff08;Positional Encoding&#xff09;1.1.3 编码器与解码器结构1.1.4 多头自注意力机制 二.Transformer分步详解2.1 传统词向量存在的问题2.2 详解编解码…

Jenkins项目CICD流程

Jenkins项目流程:1.配置git环境 git config --...2.把前后端的目录初始化位本地工作目录 #git init3.提交到本地git #git add ./ git commit -m "" git tag v14.然后提交到远程git(通过,用户,群组,项目,管理项目)git remote add origin http://...git push -…

【IDEA】2017版本的使用

目录 一、常识 二、安装 1. 下载IDEA2017.exe 2. 安装教程 三、基本配置 1. 自动更新关掉 2. 整合JDK环境 3. 隐藏.idea文件夹和.iml等文件 四、创建Java工程 1. 新建项目 2. 创建包结构&#xff0c;创建类&#xff0c;编写main主函数&#xff0c;在控制台输出内容。…

Git指南-从入门到精通

代码提交和同步命令 流程图如下&#xff1a; 第零步: 工作区与仓库保持一致第一步: 文件增删改&#xff0c;变为已修改状态第二步: git add &#xff0c;变为已暂存状态 bash $ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ g…