Ошибка сегментации в связи TCP - PullRequest
3 голосов
/ 21 ноября 2008

Я получаю ошибку сегментации в следующем коде C:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT  6667
#define MAXDATASIZE 1024

int bot_connect(char *hostname);

int bot_connect(char *hostname) {

  int sockfd, numbytes, s;
  char buf[MAXDATASIZE];
  struct addrinfo hints, *servinfo, *p;
  int rv;
  char m[1024];
  char *message;
  char *nick = "Goo";
  char *ident = "Goo";
  char *realname = "Goo";

  memset(&hints,0,sizeof hints);
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  rv = getaddrinfo(hostname, PORT, &hints, &servinfo);

  if (rv != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
  }

  for (p = servinfo; p != NULL; p = p->ai_next) {
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == -1) {
      perror("Client: socket");
      continue;
    }

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
      close(sockfd);
      perror("Client: connect");
      continue;
    }

    break;
  }

  if (p == NULL) {
    fprintf(stderr, "Client: failed to connect \n");
    return 2;
  }

  freeaddrinfo(servinfo);

  strcat(m, "NICK ");
  strcat(m, nick);
  message = m;
  s = send(sockfd, message, strlen(message), 0);

  strcat(m, "USER ");
  strcat(m, ident);
  strcat(m, " * * :");
  strcat(m, realname);
  message = m;
  s = send(sockfd, message, strlen(message), 0);

  message = "JOIN #C&T";
  s = send(sockfd, message, strlen(message), 0);

  close(sockfd);
}

Я знаю, что вы получаете ошибки сегментации при попытке сделать что-то с памятью, что вам запрещено делать, например, изменить память только для чтения, но, насколько мне известно, эта программа этого не делает. Кто-нибудь знает, откуда происходит ошибка сегментации?

Ответы [ 6 ]

10 голосов
/ 21 ноября 2008

Вы звоните strcat( m, "NICK" ); до инициализации m. перед strcat попробуйте m[0] = '\0'; или memset( m, 0, sizeof( m ) ); или измените первое значение strcat на strcpy

Кроме того, после отправки строки NICK в гнездо, вы снова вызываете strcat, что добавит строку USER к строке NICK. Опять же, вам, вероятно, следует изменить первое значение strcat на strcpy

5 голосов
/ 21 ноября 2008

Грэм правильно с «что» , поэтому вот «почему», если вы незнакомы. В C строка определяется как последовательность символов, оканчивающаяся нулевым символом '\ 0'. Причина, по которой строка имеет завершающий символ, заключается в том, что код может определить, где строка логически заканчивается. Даже если вы объявите строку, подобную этой:

 char m[1024];

В C нет способа, чтобы код, заданный m, мог понять, что ему выделено 1024 байта, не говоря уже о том, сколько из этих байтов имеет смысл, не имея нулевого символа, который разграничивает конец значимых байтов .

strcat - это функция, которая работает с двумя строками C, поэтому она ожидает, что оба ее аргумента будут соответствовать спецификации, что есть нулевой байт, отмечающий конец значимого ввода. Если m неинициализировано, его содержимое является случайным, поэтому нулевой байт может быть где угодно или нулевого байта не может быть вообще. Для конкатенации strcat просматривает символы первого аргумента, пытаясь найти нулевой байт, отмечающий конец строки. Если, случайно, в неинициализированном массиве нет нулевого байта, он с радостью продолжит поиск за конца строки в произвольных ячейках памяти, которые могут соответствовать другим переменным или внутренней информации стека и т. Д.

Когда strcat, наконец, найдет нулевой байт, он с радостью запишет содержимое второго аргумента, начиная с этого местоположения, и, если это местоположение находится за концом m, он перезапишет какую-то другую переменную или другую часть информации. Если это адрес памяти, который назначен какой-то другой переменной, проблема может быть молчаливой и ее очень сложно найти. К счастью, в вашем случае это было местоположение, в которое не нужно было писать, поэтому ваша программа вылетела с явной ошибкой.

Это также вызывает замечание, что вам действительно следует использовать strncat вместо strcat. strncat позволяет указать максимальную длину создаваемой строки. Ранее я отмечал, что strcat не может определить, что для m выделено 1024 байта, поэтому, если вы попытаетесь объединить m с чем-то, что сделает m длиннее 1023 символов, у вас возникнет та же проблема перезаписи памяти. strncat принимает третий аргумент, который задает максимальную длину результирующей строки и имеет код возврата, который будет указывать, была ли конкатенация урезана из-за достижения этой длины.

4 голосов
/ 21 ноября 2008

Вот уже правильные ответы на вопросы, почему и что. Чтобы ответить на ваше "где ошибка сегментации ...", я рекомендую отладчик. Скомпилируйте вашу программу с отладочной информацией, и вы увидите, где это происходит (просто дайте ей запуститься и посмотрите на трассировку стека при сбое) Ответ, скорее всего, также ответит «почему» и «что» (что в данном случае уже дано)

2 голосов
/ 21 ноября 2008

Увеличьте уровень предупреждения на вашем компиляторе:

rv = getaddrinfo(hostname, PORT, &hints, &servinfo);

Второй параметр для getaddrinfo это const char *, а не int. Измените PORT на NULL и измените номер порта в структуре addrinfo перед connect.

1 голос
/ 21 ноября 2008

Используйте valgrind . Valgrind чрезвычайно хорош в поиске утечек ресурсов и других типичных ошибок человеческого поведения в вашем коде.

0 голосов
/ 14 марта 2013

Массив m содержит мусор перед использованием. Следующее будет работать:

strcpy(m, "NICK ");
strcat(m, nick);
message = m;
s = send(sockfd, message, strlen(message), 0);

strcpy(m, "USER ");
strcat(m, ident);
strcat(m, " * * :");
strcat(m, realname);
message = m;
s = send(sockfd, message, strlen(message), 0);

Также будет работать следующее:

sprintf(m, "NICK %s", nick);
message = m;
s = send(sockfd, message, strlen(message), 0);

sprintf(m, "USER %s * * :%s", ident, realname);
s = send(sockfd, message, strlen(message), 0);
...