我有两个进程通过使用socketpair()和SOCK_SEQPACKET创建的一对套接字进行通信。像这样:
int ipc_sockets[2];
socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, ipc_sockets);
据我了解,我应该在收到SOCK_SEQPACKET记录时在“struct msghdr”的msg_flags成员中看到MSG_EOR。我在sendmsg()中设置MSG_EOR以确保记录标记为MSG_EOR,但在recvmsg()中接收时我看不到它。我甚至试图在发送记录之前在msg_flags字段中设置MSG_EOR,但这根本没有区别。
我想我应该看到MSG_EOR,除非记录被缩短,例如一个信号,但我没有。那是为什么?
我在下面粘贴了我的发送和接收代码。
谢谢, 朱
int
send_fd(int fd,
void *data,
const uint32_t len,
int fd_to_send,
uint32_t * const bytes_sent)
{
ssize_t n;
struct msghdr msg;
struct iovec iov;
memset(&msg, 0, sizeof(struct msghdr));
memset(&iov, 0, sizeof(struct iovec));
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE_SIZEOF_INT];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
memset(msg.msg_control, 0, sizeof(control_un.control));
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = fd_to_send;
#else
msg.msg_accrights = (caddr_t) &fd_to_send;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = data;
iov.iov_len = len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
#ifdef __linux__
msg.msg_flags = MSG_EOR;
n = sendmsg(fd, &msg, MSG_EOR);
#elif defined __APPLE__
n = sendmsg(fd, &msg, 0); /* MSG_EOR is not supported on Mac
* OS X due to lack of
* SOCK_SEQPACKET support on
* socketpair() */
#endif
switch (n) {
case EMSGSIZE:
return EMSGSIZE;
case -1:
return 1;
default:
*bytes_sent = n;
}
return 0;
}
int
recv_fd(int fd,
void *buf,
const uint32_t len,
int *recvfd,
uint32_t * const bytes_recv)
{
struct msghdr msg;
struct iovec iov;
ssize_t n = 0;
#ifndef HAVE_MSGHDR_MSG_CONTROL
int newfd;
#endif
memset(&msg, 0, sizeof(struct msghdr));
memset(&iov, 0, sizeof(struct iovec));
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE_SIZEOF_INT];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
memset(msg.msg_control, 0, sizeof(control_un.control));
#else
msg.msg_accrights = (caddr_t) &newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = buf;
iov.iov_len = len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (recvfd)
*recvfd = -1;
n = recvmsg(fd, &msg, 0);
if (msg.msg_flags) { // <== I should see MSG_EOR here if the entire record was received
return 1;
}
if (bytes_recv)
*bytes_recv = n;
switch (n) {
case 0:
*bytes_recv = 0;
return 0;
case -1:
return 1;
default:
break;
}
#ifdef HAVE_MSGHDR_MSG_CONTROL
if ((NULL != (cmptr = CMSG_FIRSTHDR(&msg)))
&& cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (SOL_SOCKET != cmptr->cmsg_level) {
return 0;
}
if (SCM_RIGHTS != cmptr->cmsg_type) {
return 0;
}
if (recvfd)
*recvfd = *((int *) CMSG_DATA(cmptr));
}
#else
if (recvfd && (sizeof(int) == msg.msg_accrightslen))
*recvfd = newfd;
#endif
return 0;
}
答案 0 :(得分:5)
使用SOCK_SEQPACKET unix域套接字消息被剪短的唯一方法是你给recvmsg()的缓冲区不够大(在这种情况下你会得到MSG_TRUNC)。
POSIX说SOCK_SEQPACKET套接字必须在记录末尾设置MSG_EOR,但Linux unix域套接字不能。
(参考:POSIX 2008 2.10.10表示SOCK_SEQPACKET必须支持记录,2.10.6表示接收器通过MSG_EOR标志可以看到记录边界。)
“记录”对于给定协议的意义取决于要定义的实现。
如果Linux确实为unix域套接字实现了MSG_EOR,我认为唯一明智的方法就是说每个数据包本身都是一个记录,所以总是设置MSG_EOR(或者在不设置MSG_TRUNC时总是设置它),所以无论如何它都不会提供信息。
答案 1 :(得分:2)
这不是MSG_EOR的用途。
请记住,套接字API是对许多不同协议的抽象,包括UNIX文件系统套接字,套接字对,TCP,UDP和许多不同的网络协议,包括X.25和一些完全被遗忘的协议。
MSG_EOR用于表示对底层协议有意义的记录结束。即它将消息传递给下一层,即“这完成了一条记录”。这可能会影响例如缓冲,导致冲洗缓冲区。但如果协议本身没有“记录”的概念,则没有理由期望该标志被传播。
其次,如果使用SEQPACKET,必须立即读取整个消息。如果不这样做,剩余部分将被丢弃。这是记录在案的。特别是,MSG_EOR 不 一个标志,告诉您这是数据包的最后一部分。
建议:您显然是在编写非SEQPACKET版本,以便在MacOS上使用。我建议您转储SEQPACKET版本,因为它只会使维护和编码负担加倍。 SOCK_STREAM适用于所有平台。
答案 2 :(得分:0)
阅读文档时,SOCK_SEQPACKET
与SOCK_STREAM
有两个不同的方面。首先-
用于固定最大长度的数据报的顺序,可靠,基于双向连接的数据传输路径;消费者需要在每次输入系统调用时读取整个数据包。
-
socket(2)
来自Linux联机帮助页项目
aka
对于基于消息的套接字,例如SOCK_DGRAM和SOCK_SEQPACKET,应在单个操作中读取整个消息。如果消息太长而无法容纳在提供的缓冲区中,并且在flags参数中未设置MSG_PEEK,则应丢弃多余的字节,并在msghdr结构的msg_flags成员中设置MSG_TRUNC。
-
recvmsg()
在POSIX标准中。
从这个意义上讲,它类似于SOCK_DGRAM
。
第二,每个“数据报”(Linux)/“消息”(POSIX)都带有一个称为MSG_EOR
的标志。
但是,针对SOCK_SEQPACKET
的Linux AF_UNIX
未实现MSG_EOR
。当前文档与现实不符:-)
据称某些SOCK_SEQPACKET
实现实现了另一个实现。某些同时实现。这样就涵盖了所有可能的不同组合:-)
[1]面向分组的协议通常使用具有以下特征的分组级别读取: 截断/丢弃语义,没有MSG_EOR。 X.25,蓝牙,IRDA, 和Unix域套接字以这种方式使用SOCK_SEQPACKET。
[2]面向记录的协议通常使用字节流读取和MSG_EOR -没有数据包级别的可见性,没有截断/丢弃。 DECNet和ISO TP这样使用SOCK_SEQPACKET。
[3]数据包/记录混合通常使用带有截断/的SOCK_SEQPACKET 丢弃数据包级别的语义,并记录终止数据包 标有MSG_EOR。 SPX和XNS SPP通过这种方式使用SOCK_SEQPACKET。
https://mailarchive.ietf.org/arch/msg/tsvwg/9pDzBOG1KQDzQ2wAul5vnAjrRkA
您已显示了第1段的示例。
第2款也适用于SOCK_SEQPACKET
as defined for SCTP。尽管默认情况下,它在每个MSG_EOR
上都设置了sendmsg()
。禁用此功能的选项称为SCTP_EXPLICIT_EOR
。
与文档最一致的第3款似乎是最晦涩的案例。
甚至文档也无法与自己保持一致。
SOCK_SEQPACKET套接字类型类似于SOCK_STREAM类型,并且也是面向连接的。 这两种类型之间的唯一区别在于,使用SOCK_SEQPACKET类型维护记录边界。可以使用一个或多个输出操作发送一条记录,并使用一个或多个输入操作接收一条记录,但是单个操作绝不会传输多条记录的一部分。接收者可以通过recvmsg()函数返回的已接收消息标志中的MSG_EOR标志来看到记录边界。 -POSIX标准