2009-07-25 4 views
1

대학 프로젝트 용 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 래퍼에 방법을 읽어 의 일종을하는 것입니다. 메시지 대기열의 데이터를 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; 
} 

세그멘테이션 결함이거나 완전히 손상된 데이터입니다 (이 데이터에는 때로는 원하는 데이터가 포함되어 있음).

+2

C++의 경우 항상 new를 사용하십시오. malloc을 절대로 사용하지 마십시오. –

+1

그리고 가능한 경우 새로운 것을 피하십시오. –

+0

"문제는 malloc 호출 인 것 같습니다." 왜 이걸 생각하니? 어떤 오류/진단을 받고 있습니까? 이것은 런타임 오류 또는 컴파일 시간 오류입니까? 더 많은 정보가 필요합니다. –

답변

3

std::string 멤버가 들어있는 구조체에 대한 포인터를 msgrcv으로 전달할 수 없습니다. 이는 인터페이스 계약에 위배됩니다.크기 msgrcv에 세번째 파라미터이고 msgrcv 필요에 전달

두번째 파라미터 struct { long mtype; char mdata[size]; }; 형태의 '보통'C 구조체를 저장하기에 충분한 공간이 버퍼 가리.

불행하게도이 버퍼의 크기를 결정하는 데는 size이 포함될 수 있지만 이러한 종류의 인터페이스를 제공하는 시스템에는없는 것으로 가정해야합니다. 이 표준 크기를 확인하는 데 표준 offsetof 매크로를 사용할 수 있습니다.

vector은 해당 구성 요소를 연속적으로 저장하므로 vectorchar 크기로 조정하고 버퍼를 유지하는 데 사용할 수 있습니다. 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 해킹 호환되는 데이터 배열에 데이터 팩을 가지고, 다른 방법으로 이동합니다. 다른 사람들이 포인터를 가지고 있기 때문에 좋은 인터페이스는 아니지만, 구현에 정의 된 동작 및 정렬 문제를 살펴볼 때 이와 같은 것이 작동해야합니다.

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(); 
    } 
} 
+0

내가보기에 이걸 좋아해. 그러나 그것도 작동하지 않습니다. 내 문제는 데이터를 보내는 것과 동일한 문제가있는 것으로 보입니다. std :: string을 보내는 비슷한 방법을 게시 하시겠습니까? 나는 그것을 스스로 시도했지만, 이것이 효과가 없었다. – Koraktor

+0

모든 도움을 주셔서 다시 한 번 감사드립니다. send 메소드의 작동 구현을 이미 발견했습니다. 놀랍게도 수신 방법이 예상대로 작동하지 않았습니다. 데이터가 올바르게 전송되었지만 벡터 내의 데이터가 손상되었습니다 . 위에서 malloc 구현 (또는 calloc 또는 새 char [])을 사용할 때 (msg를 밖으로 박멸 한) msg.mdata에서 올바른 데이터를 얻었고이를 std :: string에 넣을 수 있습니다. 그러나 문자열을 반환하려고하자마자 나는 명백한 소스가없는 꽤 비밀스러운 SIGSEGV를 얻는다. 프로젝트가 마감일에 가까워지고 있다는 것에 너무 슬퍼합니다. 결코 바른 길을 찾지 못할 것 같습니다. – Koraktor

+0

보내기 방법을 게시하면 도움이 될만한 기회가 생깁니다. 손상된 데이터의 의미는 무엇입니까? char의 벡터는 버퍼 일 뿐이므로 전송 된 메시지의 내용 만 포함하지는 않습니다. 추출 된 문자열 *은 정확해야하지만 데이터를 볼 수 없으면 알 수 없습니다. SIGSEGV를 올렸을 때 스택 트레이스는 어떻게 생겼을까요? –

1

여기에 an example for SyS입니다. 나는 그것이 도움이되기를 바랍니다.

malloc을 사용하는 방법은 정확하지만 IPC를위한 메모리 할당을 할 때는 매우주의해야합니다. 다른 프로세스가 메모리를 어떻게 관리하는지 확인해야합니다 (바이트 정렬, 크기, 플랫폼 ...)

코드에서 mtype의 목적은 무엇입니까? 받은 크기에이 mtype이 고려됩니까? 아니면 단지 mdata의 크기입니까?

업데이트 : mtype이 메시지의 일부입니까?

그렇다면 :

msgsize 크기 = *를 sizeof (문자) +는 sizeof (긴)

= PMSG의 malloc (msgsize);

msgrcv (MSGQ_ID, pmsg, msgsize, MSQ_ID, 0);

아니라면

msg.data = (숯 *)의 malloc (*의 크기는 sizeof (숯));

msgrcv (MSGQ_ID, msg.data, size, MSQ_ID, 0);

데이터가 힙에 할당되는 동안 mtype은 스택에 할당됩니다. msgreceive가 주어진 포인터에 일종의 memcpy를 만들면 문제가 발생합니다.

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; 
} 



1

근본 원인 원인은 방어 적이기이 구조체의 크기보다 큰 메시지를 복사하려고 할 것입니다. 구조체에는 long (4 바이트) 및 std : 문자열을위한 공간 만 있습니다. 문자열은 가변 크기 일 수 있지만 문자열로만 사용됩니다 (자동으로 메모리를 할당하고 포인터를 유지 관리합니다). 성병 : 문자열 변수는이 (간체) 예에서만 8 바이트 길이 MSG에

struct { 
    unsigned int stringlength; 
    char *pString; 
} 

에 코드를 복사처럼 보일 것이다. 포인터를 데이터로 덮어 씁니다.이 데이터는 나중에 메모리 위치로 해석됩니다.

솔루션 용액 헤더 (MTYPE) 및 크기 바이트의 메시지를 모두 저장하기에 충분히 큰 메모리 블록을 할당한다. 임시 공간 일 뿐이므로 스택 변수를 사용하는 것이 좋습니다. 그래서

struct msg { 
     long mtype; 
     char data[ size ]; 
    } 

나는 그것이 글로벌 상수 인 경우 더 독특하고 meaningfull 뭔가 크기를 교체하는 것이 좋습니다.크기은 바이트로 표시됩니다. 크기가자인 std :: string을 보내야하는 경우 최소 sizeof (std :: string) +1 (구조 오버 헤드 및 \ 0 문자)으로 배열 길이를 늘려야합니다. 더 나은 조금.

1

세그먼트 화 오류 또는 손상된 데이터의 이유는 msgrcv()msgsnd() 함수에 부적절한 메시지 구조를 사용하고 있기 때문입니다. 이러한 기능은 다음과 같은 구조를 사용이 필요합니다

struct msgbuf { 
    long mtype;  /* message type, must be > 0 */ 
    char mtext[1]; /* message data */ 
}; 

하지만 당신의 Message 구조가 힙에 메모리를 할당하지만, msgbuf 구조가 mtext[0]에서 시작하는 메모리 영역을 기대 std::string이 포함되어 있습니다. mtext 멤버는 포인터가 아니며 구조체 내부의 char 배열입니다. 크기가 불명하므로 배열은 1 크기의 배열로 선언되지만 실제로는 호출자가 더 큰 버퍼를 제공해야합니다. 이 기술은 일부 Windows API에서도 사용됩니다. 메시지를 수신 할 수있는 내용은

, 당신은 그런 다음이 msgmsgrcv()에 전달 될 수있는 버퍼

static const unsigned int BufferSize = 256; 
char* buffer = new char[ sizeof(long) + BufferSize ]; 
msgbuf* msg = reinterpret_cast< msgbuf* >(buffer); 

이 필요합니다. 그리고 버퍼를 delete[] 잊지 마세요.

또한 메시지를 보낼 때 0 문자가 명시 적으로 작성되지 않으면 버퍼에서 수신 된 텍스트가 null로 끝나지 않습니다. 이러한 null이 아닌 null로 끝나는 텍스트는 std::string (예 : std::string(msg->mtext, len) 또는 버퍼에 1 바이트를 0으로 예약하여 메시지를받은 후 쓸 수 있습니다. msgrcv()

일반 문자 배열 대신 std :: vector를 사용하면 항목이 저장 될 수 있습니다 연속적인 메모리 범위. 다음 코드는 메시지 수신 루프에서 사용할 수 있습니다. 메시지 수신 루프에서는 메시지가 적합하지 않으면 자동으로 버퍼 크기를 늘립니다.

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 
    ... 
}