深入理解Linux下UDP.recvmsg的使用方法 (linux udp recvmsg)

UDP是一种无连接的传输协议,可以直接向目标IP地址和端口发送数据,但是接收数据需要使用recvfrom或者recvmsg函数。其中,recvmsg函数可以获取更多的信息,比如源IP地址和端口号等,这在网络编程中非常有用。本文将深入探讨Linux下UDP.recvmsg函数的使用方法。

1. UDP.recvmsg函数的定义

下面是UDP.recvmsg函数的定义:

“`c

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

“`

其中,

– sockfd:套接字描述符;

– msg:指向msghdr结构体的指针,包含了接收到的数据和其他相关信息;

– flags:控制函数调用的行为。

2. msghdr结构体

在使用UDP.recvmsg函数时,需要定义一个msghdr结构体来保存接收到的数据和相关信息。msghdr结构体的定义如下:

“`c

struct msghdr {

void *msg_name; /* optional address */

socklen_t msg_namelen; /* size of address */

struct iovec *msg_iov; /* scatter/gather array */

size_t msg_iovlen; /* # elements in msg_iov */

void *msg_control; /* ancillary data, see below */

size_t msg_controllen; /* ancillary data buffer len */

int msg_flags; /* flags on received message */

};

“`

其中,

– msg_name:源IP地址和端口号;

– msg_namelen:msg_name缓冲区的长度;

– msg_iov:指向一个或多个iovec结构体的指针,表示一组分散的缓冲区;

– msg_iovlen:msg_iov缓冲区中元素的数量;

– msg_control:与接收到的数据相关的辅助数据;

– msg_controllen:msg_control缓冲区的长度;

– msg_flags:MSG_PEEK等标志。

3. recvmsg函数的使用方法

下面是UDP.recvmsg函数的详细使用方法:

(1)创建套接字

在使用recvmsg函数之前,需要先创建一个UDP套接字。以下是创建套接字的示例代码:

“`c

int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

if (sockfd

perror(“socket() error”);

exit(EXIT_FLURE);

}

“`

在创建套接字时,我们指定了协议簇为IPv4(AF_INET)、套接字类型为UDP(SOCK_DGRAM)以及协议为UDP(IPPROTO_UDP)。这样我们就创建了一个UDP套接字。

(2)设置套接字选项

在接收UDP数据时,我们需要设置SO_REUSEADDR套接字选项来允许多个套接字绑定到同一个端口。以下是设置SO_REUSEADDR选项的示例代码:

“`c

int optval = 1;

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))

perror(“setsockopt() error”);

exit(EXIT_FLURE);

}

“`

(3)绑定套接字

在接收UDP数据之前,我们需要将套接字绑定到一个端口上。以下是绑定套接字的示例代码:

“`c

struct sockaddr_in server_addr = {0};

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(8888);

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))

perror(“bind() error”);

exit(EXIT_FLURE);

}

“`

在绑定套接字时,我们指定了端口号为8888,并将IP地址设置为INADDR_ANY,表示绑定到所有可用IP地址上。

(4)接收UDP数据

接下来,我们将详细介绍如何使用UDP.recvmsg函数来接收UDP数据。

我们首先定义一个iovec结构体,用于保存接收到的数据:

“`c

char buf[512] = {0};

struct iovec iov;

iov.iov_base = buf;

iov.iov_len = sizeof(buf);

“`

接着,我们定义一个msghdr结构体,用于保存接收到的数据和相关信息:

“`c

struct msghdr msg = {0};

msg.msg_name = NULL;

msg.msg_namelen = 0;

msg.msg_iov = &iov;

msg.msg_iovlen = 1;

“`

在定义msghdr结构体时,我们将msg_name设置为NULL,表示不获取源IP地址和端口号。如果需要获取源IP地址和端口号,则需要定义一个sockaddr_in结构体,并将它赋给msg_name指针。

然后,我们调用UDP.recvmsg函数来接收UDP数据:

“`c

ssize_t n = recvmsg(sockfd, &msg, 0);

“`

在调用UDP.recvmsg函数时,我们将定义好的msghdr结构体作为参数传入。函数返回的是接收到的数据长度(即缓冲区中的字节数)。

我们可以使用memcmp函数来比较接收到的数据和期望的数据是否相同。以下是完整的接收UDP数据的示例代码:

“`c

char expect[512] = “Hello, world!”;

char buf[512] = {0};

struct iovec iov;

iov.iov_base = buf;

iov.iov_len = sizeof(buf);

struct msghdr msg = {0};

msg.msg_name = NULL;

msg.msg_namelen = 0;

msg.msg_iov = &iov;

msg.msg_iovlen = 1;

ssize_t n = recvmsg(sockfd, &msg, 0);

if (n

perror(“recvmsg() error”);

exit(EXIT_FLURE);

} else if (n == 0) {

printf(“recvmsg() returned 0\n”);

} else {

if (memcmp(buf, expect, strlen(expect)) == 0) {

printf(“Received: %s”, buf);

} else {

printf(“Received unexpected data\n”);

}

}

“`

4.

UDP.recvmsg函数是一个非常有用的函数,可以获取更多的信息,比如源IP地址和端口号等。在网络编程中,我们可以使用UDP.recvmsg函数接收UDP数据并进行相应的处理。本文详细介绍了Linux下UDP.recvmsg函数的使用方法,包括创建套接字、设置套接字选项、绑定套接字、接收UDP数据等步骤。

相关问题拓展阅读:

  • 如何用recvmsg和sendmsg来编写sctp的程序
  • 在Linux 上,编写一个每秒接收 100万UDP数据包的程序究竟有多难

如何用recvmsg和sendmsg来编写sctp的程序

I.

msghdr 与 cmsghdr

struct msghdr {

void *msg_name;

intmsg_namelen;

struct iovec * msg_iov;

unsigned long msg_iovlen;

void *msg_control;

unsigned long msg_controllen;

unsignedmsg_flags;

};

结构成员可以分为下面的四组:

1.

套接口地址成员 msg_name 与吵凳 msg_namelen ; msg_name

指向要发送或接收信息的套接口地址。 msg_namelen 指明了这个套接口地址的长度。

msg_name 在调用 recvmsg 时指向接收地址,在调用 sendmsg 时指向目的地址。

2.

I/O 向量引用 msg_iov 与 msg_iovlen 它是实际的数据缓冲区,这个

msg_iovlen 是 msg_iov 的个数,不是长度。 msg_iov 成员指向一个

struct iovec 数组。

3.

附属数据缓冲区成员 msg_control 与 msg_controllen ,msg_control 指向附属数据缓冲区,而

msg_controllen 指明了缓冲区大小。

4.

接收信息标记位 msg_flags

struct iovec

{

void *iov_base;

unsigned long iov_len;

};

有了这个 iovec ,就可以使用 readv 和 writev 函数在一次函数调用中读取或是写入多个缓冲区,显然比多次 read , write 更有效率。 readv 和 writev 的函数原型如下:

#include

ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);

ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);

struct cmsghdr {

unsigned long cmsg_len; intcmsg_level;

intcmsg_type;

};

·cmsg_len 附属数据的字节数,这包含结构头的尺寸,这个值是由 CMSG_LEN() 宏计算的;

·cmsg_level 表明了原始的协议级绝察别 ( 例如, SOL_SOCKET) ;

·cmsg_type 表明了升宏旅控制信息类型 ( 例如, SCM_RIGHTS ,附属数据对象是文件描述符; SCM_CREDENTIALS ,附属数据对象是一个包含证书信息的结构 )

msghdr 和 cmsghdr: Linux 系统为这些结构提供了一系列的宏来简化我们的工作,这些宏可以在不同的 UNIX 平台之间进行移植:

#include

struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);

输入参数:指向 struct msghdr 结构的指针; 返回指向附属数据缓冲区内的之一个附属对象的 struct cmsghdr 指针。如果不存在附属数据对象则返回的指针值为 NULL 。

struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);

输入参数:指向 struct msghdr 结构的指针,指向当前 struct cmsghdr 的指针;

这个用于返回下一个附属数据对象的 struct cmsghdr 指针,如果没有下一个附属数据对象,这个宏就会返回 NULL

size_t CMSG_SPACE(size_t length);

输入参数:附属数据缓冲区中的对象大小; 计算 cmsghdr 头结构加上附属数据大小,并包括对其字段和可能的结尾填充字符,注意 CMSG_LEN() 值并不包括可能的结尾填充字符。 CMSG_SPACE() 宏对于确定所需的缓冲区尺寸是十分有用的。 注意如果在缓冲区中有多个附属数据,一定要同时添加多个 CMSG_SPACE() 宏调用来得到所需的总空间。

void *CMSG_DATA(struct cmsghdr *cmsg)

输入参数:指向 cmsghdr 结构的指针 ;

返回跟随在头部以及填充字节之后的附属数据的之一个字节 ( 如果存在 ) 的地址。

size_t CMSG_LEN(size_t length)

输入参数:附属数据缓冲区中的对象大小; 计算 cmsghdr

头结构加上附属数据大小,包括必要的对其字段,这个值用来设置 cmsghdr 对象的 cmsg_len 成员.

int sendmsg(int, const struct msghdr *msg, unsigned int

flags)

int recvmsg(int s, struct msghdr *msg, unsigned int flags)

s, 套接字通道,对于 sendmsg 是发送套接字,对于 recvmsg 则对应于接收套接字;

msg ,信息头结构指针;

flags , 可选的标记位, 这与 send 或是 sendto

函数调用的标记相同。

函数的返回值为实际发送 / 接收的字节数。否则返回 -1 表明发生了错误

sctp

从协议栈的角度看,于tcp,udp同等级别的层次上的。具有tcp和udp不同的优点。从传输的角度看,它可以象tcp那样点对点的那样传输,也可以像udp那样一对多那样传输,但这两种方式对于sctp来说都是建立连接的。下面就介绍这两种方式的编程方式:

UDP那样点对多点的方式(代码片段):

sctpSK = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);

error = sctp_bindx(sctpSK, (struct sockaddr *)&tempSockAddr,

1, SCTP_BINDX_ADD_ADDR);

listen(sctpSK, 1);

typedef union {

struct sctp_initmsg init;

struct sctp_sndrcvinfo sndrcvinfo;

} _sctp_cmsg_data_t;

// buffer and variables used to read data from recvmsg control

messags

char

incmsg;

// data

buffer

struct

iovec iov;

// the

main msghdr structure used to receive/send messages

struct

msghdr inmessage;

struct

sockaddr_in msg_name;

memset(&inmessage, 0, sizeof(inmessage));

iov.iov_base = malloc(SCTP_RX_BUFFER_SIZE);

if

(iov.iov_base == NULL)

{

exit(EXIT_FAILURE);

}

iov.iov_len = SCTP_RX_BUFFER_SIZE;

inmessage.msg_name = &msg_name;

inmessage.msg_namelen = sizeof(msg_name);

inmessage.msg_iov = &iov;

inmessage.msg_iovlen = 1; //

number of iovs in the inmessage

inmessage.msg_control = incmsg;

inmessage.msg_controllen =

sizeof(incmsg);

received = recvmsg(sctpSK, &inmessage, MSG_WAITALL);

解析这个收到到的inmessage相应的代码如下:

union sctp_notification {

struct

{

__u16

sn_type;

__u16 sn_flags;

__u32 sn_length;

}

sn_header;

struct

sctp_assoc_change sn_assoc_change;

struct

sctp_paddr_change sn_paddr_change;

struct

sctp_remote_error sn_remote_error;

struct

sctp_send_failed sn_send_failed;

struct

sctp_shutdown_event sn_shutdown_event;

struct

sctp_adaption_event sn_adaption_event;

struct

sctp_pdapi_event sn_pdapi_event;

在Linux 上,编写一个每秒接收 100万UDP数据包的程序究竟有多难

udp是数据报协议,一次发送只要不超过65535字节(一般为6000字节以下才能发送成功),协议栈就加上udp头一次发送,当然IP层会分片。但接收端肯定是一次收到或者没收到,而不是培毕局分为两数大次或多次收到。你可以用select,epoll这些多路IO就知道,它配让只。

linux udp recvmsg的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux udp recvmsg,深入理解Linux下UDP.recvmsg的使用方法,如何用recvmsg和sendmsg来编写sctp的程序,在Linux 上,编写一个每秒接收 100万UDP数据包的程序究竟有多难的信息别忘了在本站进行查找喔。

香港服务器首选树叶云,2H2G首月10元开通。
树叶云(www.IDC.Net)提供简单好用,价格厚道的香港/美国云服务器和独立服务器。IDC+ISP+ICP资质。ARIN和APNIC会员。成熟技术团队15年行业经验。

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/162898.html<

(0)
运维的头像运维
上一篇2025-03-18 21:53
下一篇 2025-03-18 21:54

相关推荐

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注