我必须使用SysV消息队列为大学项目发送和接收动态数据。
数据的长度在单独的消息中传输,size
因此已知。
这就是我尝试接收数据的方式。我不得不承认我不是C ++专家,特别是在内存分配方面。
<击> 撞击>
<击>struct {
long mtype;
char *mdata;
} msg;
msg.mdata = (char *)malloc(size * sizeof(char));
msgrcv(MSGQ_ID, &msg, size, MSG_ID, 0);
问题似乎是 malloc
电话,但我不知道该怎么做。
修改
我尝试的是在消息队列周围的OO包装器中使用某种 read 方法。我想将消息队列中的数据读入char[]
或std::string
。我现在看起来(简化)就像这样。
bool Wrapper::read(char *data, int length)
{
struct Message {
long mtype;
std::string mdata;
};
Message msg;
msg.mdata = std::string(size, '\0');
if(msgrcv(MSGQ_ID, &msg, size, MSG_ID, 0) < 0)
{
return false;
}
memcpy(data, msg.mdata.c_str(), msg.mdata.size());
return true;
}
我得到的只是分段错误或完全损坏的数据(尽管这些数据有时包含我想要的内容)。
答案 0 :(得分:3)
您无法将包含std::string
成员的结构的指针传递给msgrcv
,这违反了接口合同。
传递给msgrcv
的第二个参数需要指向一个缓冲区,该缓冲区有足够的空间来存储struct { long mtype; char mdata[size]; };
形式的“普通”C结构,其中size是msgrcv
的第三个参数
不幸的是,由于可能的对齐问题,确定此缓冲区的大小可能取决于size
,但您必须假设它不在提供此类接口的系统上。您可以使用标准offsetof
宏来帮助确定此大小。
当vector
连续存储其组件时,一旦知道缓冲区的大小,就可以调整vector
char
的大小并使用它来保存缓冲区。使用vector
可以免除您free
或delete[]
手动缓冲的义务。
你需要做这样的事情。
std::string RecvMessage()
{
extern size_t size; // maximum size, should be a parameter??
extern int MSGQ_ID; // message queue id, should be a parameter??
extern long MSG_ID; // message type, should be a parameter??
// ugly struct hack required by msgrcv
struct RawMessage {
long mtype;
char mdata[1];
};
size_t data_offset = offsetof(RawMessage, mdata);
// Allocate a buffer of the correct size for message
std::vector<char> msgbuf(size + data_offset);
ssize_t bytes_read;
// Read raw message
if((bytes_read = msgrcv(MSGQ_ID, &msgbuf[0], size, MSG_ID, 0)) < 0)
{
throw MsgRecvFailedException();
}
// a string encapsulates the data and the size, why not just return one
return std::string(msgbuf.begin() + data_offset, msgbuf.begin() + data_offset + bytes_read);
}
另一方面,您只需将数据打包到msgsnd接口所需的struct
hack兼容数据阵列中。正如其他人指出的那样,它不是一个好的界面,而是掩盖了实现定义的行为和对齐问题,这样的事情应该有效。
e.g。
void SendMessage(const std::string& data)
{
extern int MSGQ_ID; // message queue id, should be a parameter??
extern long MSG_ID; // message type, should be a parameter??
// ugly struct hack required by msgsnd
struct RawMessage {
long mtype;
char mdata[1];
};
size_t data_offset = offsetof(RawMessage, mdata);
// Allocate a buffer of the required size for message
std::vector<char> msgbuf(data.size() + data_offset);
long mtype = MSG_ID;
const char* mtypeptr = reinterpret_cast<char*>(&mtype);
std::copy(mtypeptr, mtypeptr + sizeof mtype, &msgbuf[0]);
std::copy(data.begin(), data.end(), &msgbuf[data_offset]);
int result = msgsnd(MSGQ_ID, &msgbuf[0], msgbuf.size(), 0);
if (result != 0)
{
throw MsgSendFailedException();
}
}
答案 1 :(得分:1)
这是an example for SyS。我希望它会有所帮助。
使用malloc的方式似乎是正确的,但在为IPC进行内存分配时应该非常小心。您应该检查其他进程如何管理内存(字节对齐,大小,平台......)
在你的代码中,mtype的目的是什么?您收到的尺寸是否考虑了这个mtype?或者它只是mdata的大小?
更新:mtype是消息的一部分吗?
如果是这样的话:
msgsize = size * sizeof(char)+ sizeof(long)
pmsg = malloc(msgsize);
msgrcv(MSGQ_ID,pmsg,msgsize,MSQ_ID,0);
如果不是
msg.data =(char *)malloc(size * sizeof(char));
msgrcv(MSGQ_ID,msg.data,size,MSQ_ID,0);
当数据在堆上分配时,mtype在堆栈上被分配。如果msgreceive在给定指针上产生一种memcpy,则会引起一些麻烦。
答案 2 :(得分:1)
你似乎在自由地混合C和C ++,所以我也会这样做。 请注意,您可能应该完全用C编写函数,将其放在自己的转换单元中,然后从C ++调用它,但这应该可以解决您的问题。 似乎你的混乱的根源可以说是msgrcv的API设计不佳。你不能让msg结构的数据成员成为指针或引用或除了原始内存块以外的任何东西 - msgrcv将把数据直接放入结构中。
#include <stddef.h> #include <stdlib.h> #include <string.h> /* Caller must ensure that data points to at least length chars */ bool read_msg( char *data, int length ) { bool status = false; struct msg { long mtype; char data[ 1 ]; } *m = (struct msg *)malloc( length + offsetof( struct msg, data )); if( m != NULL ) { if( msgrcv( MSGQ_ID, m, length, MSQ_ID, 0 ) == length ) { memcpy( data, m->data, length ); status = true; } } free( m ); return status; }
答案 3 :(得分:1)
根本原因 您的segementation错误的原因是memcpy尝试复制大于结构大小的消息。你的struct只有很长的空间(4个字节)和std:string。字符串可以是可变大小,但仅在用作字符串时(它将自动为此分配内存并维护指针)。 std:string变量看起来像
struct {
unsigned int stringlength;
char *pString;
}
您的代码复制到msg中,在此(简化)示例中只有8个字节长。然后用数据覆盖指针,数据稍后被解释为内存位置。
<强>解决方案强> 一种解决方案是分配足够大的内存块来保存标头(mtype)和 size 字节的消息。由于它只是临时空间,我建议使用堆栈变量。所以
struct msg {
long mtype;
char data[ size ];
}
如果它是一个全局常量,我建议用更独特和有意义的东西替换 size 。请注意, size 以字节表示;如果你需要发送一个 size 字符的std :: string,你需要增加数组长度至少sizeof(std :: string)+1(对于结构开销和\ 0字符);更好一点。
答案 4 :(得分:1)
分段错误或数据损坏的原因是您正在为msgrcv()
和msgsnd()
函数使用不正确的消息结构。这些函数需要使用以下结构:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
但是你的Message
结构包含一个std::string
,它在堆上分配内存,但msgbuf
结构需要一个从mtext[0]
开始的内存区域。 mtext
成员不是指针,而是结构中的char数组。由于大小未知,数组被声明为1大小的数组,但实际上调用者应提供更大的缓冲区。此技术也用于某些Windows API。
为了能够接收消息,您需要一个缓冲区
static const unsigned int BufferSize = 256;
char* buffer = new char[ sizeof(long) + BufferSize ];
msgbuf* msg = reinterpret_cast< msgbuf* >( buffer );
然后可以将此msg
传递给msgrcv()
。并且不要忘记delete[]
缓冲区。
另请注意,缓冲区中接收的文本不会以空值终止,除非在发送消息时显式写入0字符。这样的非空终止文本可用于从中构造std::string
- 例如std::string( msg->mtext, len )
或在缓冲区中为0保留一个字节,并在收到msgrcv()
您可以使用std :: vector来保证项目存储在连续的内存范围内,而不是普通的char数组。可以在消息接收循环中使用以下代码,如果消息不适合,则会自动增加缓冲区大小。
static const unsigned int InitialBufferSize = 32;
std::vector<char> buffer( InitialBufferSize );
msgbuf* msg = new (&buffer[0]) msgbuf();
// reserve space in the vector for the mtype member of msgbuf structure
std::vector<char>::size_type available = buffer.size() - sizeof(long);
for(;;)
{
const ssize_t count = ::msgrcv( MSGQ_ID, msg, available, 0, 0 );
if( -1 == count )
{
if( E2BIG == errno )
{
buffer.resize( buffer.size() * 2 );
msg = new (&buffer[0]) msgbuf();
available = buffer.size() - sizeof(long);
continue;
}
perror( "Failed to read message from queue" );
break;
}
// handle the received message
...
}