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<