Linux udp poll函数

一、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) {

bufferlen = '\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 bufferBUFFER_SIZE;

struct pollfd fds1;

char *str = "HI";

register_signal_handler(SIGINT,

sigint_handler);

if (argc != 3) {

printf("%s\n",

argv0);

exit(EXIT_FAILURE);

}

memset(&server_addr, 0,

sizeof(server_addr));

server_addr.sin_family = AF_INET;

validate_convert_port(argv1,

&server_addr);

validate_convert_addr(argv2,

&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) {

bufferlen = '\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 bufferBUFFER_SIZE;

struct pollfd fds1;

register_signal_handler(SIGINT,

sigint_handler);

if (argc != 2) {

printf("%s ",

argv0);

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(argv1,

&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));

fds0.fd = server_socket;

fds0.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函数而已。

相关推荐
不会就选b4 小时前
算法日常・每日刷题--<链表>3
数据结构·算法·链表
冰封之寂6 小时前
Docker 资源管理终极指南:CPU、内存与 IO 的精细化控制
linux·运维·服务器·docker·云计算
稚南城才子,乌衣巷风流6 小时前
块状链表:数据结构详解与实现
数据结构·链表
xiaoye-duck7 小时前
《Linux系统编程》Linux 系统多线程(五):<线程同步与互斥>线程互斥(上):从条件变量到生产者消费者模型详解
linux·线程
云计算磊哥@8 小时前
运维开发宝典058-大型网站nginx服务器管理全集4
服务器·nginx·运维开发
一叶龙洲8 小时前
向日葵远程Ubuntu,支持隐私屏
linux·运维·ubuntu
日拱一卒——功不唐捐9 小时前
红黑树删除(C语言)
c语言·数据结构
酷可达拉斯9 小时前
Linux操作系统-shell编程之Grep与正则表达式
linux·运维·服务器·正则表达式·centos
不如语冰9 小时前
AI大模型入门-Python进阶-上下文管理与with语句
开发语言·数据结构·数据库·人工智能·pytorch·redis·python
寒水馨9 小时前
Linux下载、安装llama.cpp-b10068(附安装包llama-b10068-bin-ubuntu-vulkan-x64.tar.gz)
linux·ubuntu·llm·llama·本地部署·llama.cpp·推理引擎