Push Call в STL Queue‹std::string› вызывает Segfault, когда строка считывается из сокета

Я использую очередь STL в качестве входной очереди, она содержит std::strings, которую я назвал String с помощью typedef. Я читаю входную строку из сокета, используя сокеты Беркли. Он считывается в буферный массив символов, а затем используется для установки строки, которая передается в очередь. Это происходит только для входной очереди - выходная очередь, которая не получает свои строки из прочитанного сокета, работает нормально.

Вот соответствующий код:

// Read from this socket's descriptor and send the input
// to its associated player for queueing and parsing.
void Socket::Read() {
 char buf[READ_SIZE + 1];

 int n = 0;
 if ((n = read(descriptor, buf, READ_SIZE)) < 0) {
  try {
   handleSocketError(__FILE__, __LINE__);
  }
  catch (...) {
   throw ;
  }
 }
 else if(n > 0) {
  buf[n] = 0;
  stripNewline(buf);
  log->log("Input received in Socket::Read: %s.", buf);
  String in = buf;
  p->input(in);
 }
}

Функция stripNewline — это служебная функция, которая удаляет символы новой строки с конца полученного ввода. Я вставил это, чтобы помочь в отладке, его не было, когда впервые появился segfault:

// A utility function to strip the newlines off the end of
// a string.
void Socket::stripNewline(char *buf) {
 for(int i = strlen(buf); i > 0 && (buf[i] == '\n' || buf[i] == '\r' || buf[i] == 0); i--) {
   buf[i] = 0;
 }
}

Здесь происходит ввод и он подается в p->input в виде строки. p->input просто помещает входную строку в очередь:

// Push the String in to the tail of the input queue.
void Player::input(String in) {
 log->log("Player is sending input: %s.", in.c_str());
 std::cout << in << std::endl;
 inQ.push(in);
}

Входящая очередь определяется здесь внутри класса игрока вместе с исходящей очередью, которая работает нормально:

std::queue<String> inQ;
std::queue<String> outQ;

Строка определяется просто как определение типа std::string:

typedef std::string String;

EDIT: исправлено обратное typedef, что я получаю за то, что пишу его из памяти, когда отвлекаюсь, это было правильно в коде.

Вывод до ошибки сегментации и вывод catchsegv следующий:

Sat Oct 24 11:02:34 2009:: New connection, waking up.
Sat Oct 24 11:02:34 2009:: Connection attempt begun.  Connection in the read set.
Sat Oct 24 11:02:34 2009:: Player has received output: Welcome to Muddy Reality Alpha version!
.
Sat Oct 24 11:02:35 2009:: Input received in Socket::Read: test.
Sat Oct 24 11:02:35 2009:: Player is sending input: test.
test
Segmentation fault
*** Segmentation fault
Register dump:

 EAX: 0000000c   EBX: 00000080   ECX: 00000000   EDX: 0000000c
 ESI: bfdbf080   EDI: 080497e0   EBP: bfdbee38   ESP: bfdbee20

 EIP: 0805640f   EFLAGS: 00010282

 CS: 0073   DS: 007b   ES: 007b   FS: 0000   GS: 0033   SS: 007b

 Trap: 0000000e   Error: 00000004   OldMask: 00000000
 ESP/signal: bfdbee20   CR2: 00000024

 FPUCW: ffff037f   FPUSW: ffff0000   TAG: ffffffff
 IPOFF: 00000000   CSSEL: 0000   DATAOFF: 00000000   DATASEL: 0000

 ST(0) 0000 0000000000000000   ST(1) 0000 0000000000000000
 ST(2) 0000 0000000000000000   ST(3) 0000 0000000000000000
 ST(4) 0000 0000000000000000   ST(5) 0000 0000000000000000
 ST(6) 0000 0000000000000000   ST(7) 0000 0000000000000000

Backtrace:
/lib/libSegFault.so[0xb7f9e100]
??:0(??)[0xb7fa3400]
/usr/include/c++/4.3/bits/stl_queue.h:226(_ZNSt5queueISsSt5dequeISsSaISsEEE4pushERKSs)[0x805647a]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/player.cpp:73(_ZN6Player5inputESs)[0x805377c]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:159(_ZN6Socket4ReadEv)[0x8050698]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:413(_ZN12ServerSocket4ReadEv)[0x80507ad]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:300(_ZN12ServerSocket4pollEv)[0x8050b44]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/main.cpp:34(main)[0x8049a72]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7d1b775]
/build/buildd/glibc-2.9/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8049801]

Насколько я могу судить, String в порядке, поэтому я понятия не имею, что может привести к тому, что очередь задохнется. Я собираюсь продолжать копаться в этом и посмотреть, что у меня получится, но я был бы очень признателен за любую информацию, которую сообщество Stack Overflow может предложить по этому поводу.

ОТРЕДАКТИРОВАНО, чтобы добавить результаты продолжающегося тыка:

Я пробовал два других метода помещения buf в строку, которая идет на p-> input:

p->input(String(buf));

а также

String in;
in.assign(buf);
p->input(in);

У обоих был одинаковый результат. Я попытался отправить буфер в стандартный вывод посимвольно, чтобы убедиться, что не прокрались странные символы:

 printf("Printing buf to determine sanity: \n");
 for(int i = 0; buf[i] != 0; i++) {
  printf("%d: %c\n", i, buf[i]);
 }

Результатом чего было:

Printing buf to determine sanity: 
0: T
1: e
2: s
3: t

Так что идей пока нет. Это все локальная статическая память, так что это не проблема с динамической памятью, если только она не действительно странная (конечно, динамическая память === странные проблемы, так что это все еще возможно).

Далее: изначально size_t (целочисленный тип без знака) сравнивается для значений меньше нуля. Изменен как на ssize_t (целочисленный тип со знаком), так и на просто int без изменений - все равно вылетает. Спасибо, Крис, не ответ, но все же хороший улов!

Ответ: я по глупости в своей программе забыл установить Player *p в классе Socket. P — это обратная связь между Socket и Player, содержащим его. Я предположил, что с p все в порядке, потому что мы делали это так глубоко в плеере перед сбоем, и, следовательно, это должно было быть что-то со строкой или очередью. Дурак я. Спасибо Крис!


person Daniel Bingham    schedule 24.10.2009    source источник
comment
Учитывая вашу дополнительную отладочную печать, похоже, что сбой произошел не из-за того, что read возвращает ‹0, поэтому я думаю, вам нужно опубликовать больше кода, например, где хранится inQ.   -  person CB Bailey    schedule 24.10.2009
comment
Откуда p в классе Socket. Он случайно не нулевой или недействительный?   -  person CB Bailey    schedule 24.10.2009
comment
Хм... если бы p было нулевым или недопустимым, он должен был бы разбиться, даже не достигнув ввода. Я все равно проверю. Я сделал еще немного отладки, я опубликую результаты:   -  person Daniel Bingham    schedule 24.10.2009
comment
Почему? inQ.push - это первый раз, когда какая-либо переменная-член или виртуальная функция Player используется, поэтому я ожидаю, что именно здесь произойдет сбой, если p недействителен. Или log является переменной-членом?   -  person CB Bailey    schedule 24.10.2009
comment
О, святая корова - ты назвал это. Я предположил, что поскольку очередь вывода работает, обратная связь между плеером и сокетом установлена. Но очередь вывода никогда не использовала эту ссылку, поэтому она ее не обнаружила. Я сбит с толку тем, как он попал в player, прежде чем понял, что player == 0, но это произошло. Player *p существует в классе Socket. Player в некотором роде является оболочкой для сокета, созданной с помощью: new Player(new Socket(descriptor)); Эти конструкторы должны были установить обратную ссылку, но я, должно быть, забыл эту часть. Хотите добавить это к своему ответу, и я приму?   -  person Daniel Bingham    schedule 24.10.2009
comment
Или если input не является виртуальной функцией. Учитывая все это, вы обязательно должны опубликовать определения классов для Player и Socket.   -  person CB Bailey    schedule 24.10.2009


Ответы (1)


size_t — беззнаковый тип, nsize_t, поэтому это сравнение никогда не будет верным.

if ((n = read(descriptor, buf, READ_SIZE)) < 0) {

Если read возвращает -1, то этот else if попытается манипулировать буфером как действительно большим буфером:

else if(n > 0) {

Я не уверен, что проблемы, которые я выделил, вызывают проблему, но их стоит исправить.

Изменить

Хорошо, оказывается, что проблема была не в этом, а в предположении, что с момента сбоя могло быть (и было!) что указатель Player p был нулевым.

person CB Bailey    schedule 24.10.2009
comment
Я подозреваю, что typedef - это просто неправильная транскрипция, но хороший улов на size_t против int. - person Adam Rosenfield; 24.10.2009
comment
Удален typedef комментарий; теперь это исправлено в Q. - person CB Bailey; 24.10.2009
comment
Хороший улов на size_t против int, однако, это не был ответ. Я изменил его на ssize_t и int безрезультатно. Я отредактирую код выше, чтобы отразить это. Кроме того, обратное typedef было просто неправильной транскрипцией. Сообщение было отредактировано. Спасибо хоть! - person Daniel Bingham; 24.10.2009