2015-11-25 3 views
1

Я хочу реализовать очередь сообщений с Perl. Я получаю данные от stdin и отправляю их в очередь.Очередь сообщений в Perl

структура моего сообщения является

struct message => { 
    mtype  => '$', 
    buffer_size => '$', 
    last_message => '$', 
    buff   => '$', 
}; 

я должен получить данные из очереди с программой C. Моя программа C работает хорошо, но теперь, когда я запускаю его, чтобы получить данные из очереди показывает мне что-то вроде этого

age=HASH(0x1daa088) 1936942445  4000   

Я читал куски данных с 4000-байтовый буфер, который я печатать на стандартный вывод. Но вместо age=HASH(0x1daa088) 1936942445 программа должна печатать размер полученного сообщения.

Что здесь произошло? Это потому, что сообщение в C является структурой, а в Perl это хэш?

Мой код C:

#include <stdio.h> 
#include <stdlib.h> 
#include <linux/ipc.h> 
#include <linux/msg.h> 
#include <time.h> 
#include <string.h> 

#define bufsize 4000 

struct mymsgbuf { 
    long mtype;   /* Message type */ 
    int  buffer_size; 
    char buff[bufsize]; 
    int  last_message; 
} msg; 

int read_message(int qid, long type, struct mymsgbuf *qbuf) { 

    int result, length; 

    /* The length is essentially the size of the structure minus sizeof(mtype)*/ 

    length = sizeof(struct mymsgbuf) - sizeof(long); 

    if ((result = msgrcv(qid, qbuf, length, type, MSG_NOERROR)) == -1) { 
     return(-1); 
    } 

    fprintf(stderr, "\t%d\t\t%d\t\t%d \n", qbuf->buffer_size, bufsize, qbuf->last_message); 

    write(1,qbuf->buff,qbuf->buffer_size); 

    return(result); 
} 

int open_queue(key_t keyval) { 

    int qid; 

    if ((qid = msgget(keyval, 0660)) == -1) { 
     return(-1); 
    } 

    return(qid); 
} 

main() { 

    int  qid; 
    key_t msgkey; 
    msg.last_message = 0; 

    /* Generate our IPC key value */ 
    msgkey = ftok("/home/joobeen/Desktop/learning", 'm'); 

    /* Open/create the queue */ 
    if ((qid = open_queue(msgkey)) == -1) { 
     perror("open_queue"); 
     exit(1); 
    } 

    fprintf(stderr, "byte received:\tbuffer_size:\tlast_message:\n"); 

    /* Bombs away! */ 
    while (1) { 

     if ((read_message(qid,0, &msg)) == -1) { 
      perror("receive_message"); 
      exit(1); 
     } 
     if (msg.last_message == 1) 
      break; 
    } 

    return 0; 
} 

Мой Perl-код:

use strict; 
use warnings; 

use IPC::SysV qw(IPC_PRIVATE IPC_CREAT S_IRUSR S_IWUSR ftok); 
use IPC::Msg; 
use Class::Struct; 

struct message => { 
    mtype  => '$', 
    buffer_size => '$', 
    last_message => '$', 
    buff   => '$', 
}; 

my $key_in = ftok("/home/joobeen/Desktop/learning", 'm'); 
my ($buffer) = ""; 
my $buf_size = 4000; 
my $file  = shift @ARGV; 
my $ifh; 
my $is_stdin = 0; 
my $type_sent = 1; 
my $last; 

if (defined $file) { 
    open $ifh, "<", $file or die $!; 
} 
else { 
    $ifh = *STDIN; 
    $is_stdin++; 
} 

my $ipc_id = msgget($key_in, IPC_CREAT | S_IRUSR | S_IWUSR); 
my $msg = message->new(
    mtype  => 1, 
    last_message => 0 
); 

print "\tbyte sent\tbuffer_size\tlast_message\n"; 

while (<$ifh>) { 

    $last = read($ifh, $buffer, $buf_size); 

    $msg->buff($buffer); 
    $msg->buffer_size($buf_size); 

    if ($last < $buf_size) { 
     $msg->last_message(1); 
    } 

    msgsnd($ipc_id, pack("l! a*", $type_sent, $msg), 0); 

    print "\t", $last, "\t\t", $buf_size, "\t\t", $msg->last_message, "\n"; 

} 

close $ifh unless $is_stdin; 
+2

Пожалуйста, покажите свой код Perl и C; мы не можем сказать очень без него. Какой модуль Perl вы используете для предоставления ключевого слова 'struct'? Это [Класс :: Struct] (http://metacpan.org/module/Class::Struct)? – Borodin

+1

@Borodin Я использую класс :: struct.edit мой вопрос, и теперь вы можете видеть мой код. – farzane

ответ

4

Ваш код имеет несколько проблем. Я не буду исправлять их все для вас, но я могу дать вам некоторые рекомендации о том, как реализовать IPC в целом.

Чтение двоичных данных непосредственно в C-структурах является чрезвычайно хрупким. Вы должны заботиться о порядке байтов, заполнении структуры и размере таких типов, как int или long. В зависимости от вашей платформы оба этих типа могут быть 32-битными или 64-битными и маленькими или большими. Поэтому, прежде всего, вам нужна точная спецификация «по-проводному протоколу» ваших сообщений. Чтобы упростить ситуацию, давайте использовать сообщения фиксированного размера:

mtype: 32-bit unsigned integer, little endian 
buffer_size: 32-bit unsigned integer, little endian 
buffer: 4000 bytes 
last_message: 32-bit unsigned integer, little endian 

Это всего лишь пример. Вы также можете использовать большие числа endian, как и большинство сетевых протоколов по историческим причинам. Если вы хотите использовать IPC только на одном компьютере, вы также можете указать собственный порядок байтов.

Теперь длина сообщения фиксирована до 4012 байт. Чтобы декодировать такое сообщение переносимым способом в C, вы должны прочитать его в массив char и извлечь каждое поле отдельно. Вы знаете смещение и размер каждого поля.

Кодирование такого сообщения в Perl легко с помощью функции pack:

my $msg = pack('V V a4000 V', $mtype, $buffer_size, $buffer, $last); 

Там нет необходимости Class::Struct. Этот модуль не выполняет то, что вы ожидаете.

+0

Спасибо за ваш полезный ответ. Я делаю то, что сказал и меняю свои c и perl code.i отправлю мои сообщения как этот msgsnd ($ ipc_id, pack ('VV a4000 V', $ type_sent, $ buf_size, $ buffer, $ last_message), 0); можете ли вы сказать мне, что это правда или нет? – farzane

Смежные вопросы