TCP通信中的分段错误

时间:2008-11-21 13:26:44

标签: c segmentation-fault

我在以下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 个答案:

答案 0 :(得分:10)

在初始化m之前,您正在呼叫strcat( m, "NICK" );。在strcat之前,请尝试m[0] = '\0';memset( m, 0, sizeof( m ) );,或将第一个strcat更改为strcpy

此外,在套接字上发送NICK线路后,再次呼叫strcat,这会将USER线路附加到NICK线路。同样,您应该将第一个strcat更改为strcpy

答案 1 :(得分:5)

Graeme is correct with the "what",所以这里是“为什么”,以防你不熟悉。在C中,字符串被定义为由空字符'\ 0'终止的一系列字符。字符串具有终止字符的原因是代码可以找出字符串逻辑结束的位置。即使你声明一个这样的字符串:

 char m[1024];

在C中没有办法让给定m的代码能够找出它分配了1024个字节,更不用说弄清楚这些字节中有多少是有意义的,而没有一个空字符来划分有意义字节的结尾

strcat是一个可以处理两个C字符串的函数,因此它希望它的两个参数都符合规范,即有一个空字节标记有意义输入的结束。如果m未初始化,则其内容是随机的,因此空字节可以是任何位置,或者根本不存在空字节。为了连接,strcat将查看第一个参数的字符,试图找到标记字符串结尾的空字节。如果在未初始化的数组中偶然没有空字节,它将很乐意继续查找过去字符串的结尾,进入可能对应于其他变量的任意内存位置,或内部堆栈信息等。

当strcat最终找到一个空字节时,它会愉快地写出从该位置开始的第二个参数的内容,如果该位置超过m的结尾,它将覆盖其他一些变量或其他信息。如果它是一个分配给其他变量的内存地址,那么问题可能是沉默的,很难找到。幸运的是,在你的情况下,它是一个不应写入的位置,所以你的程序崩溃了一个明显的错误。

这也提示你应该使用strncat而不是strcat。 strncat允许您指定要创建的字符串的最大长度。我之前注意到strcat无法确定m是否有1024个字节分配给它,所以如果你试图将m与m长于1023个字符的东西连接起来,你最终会遇到相同的内存覆盖问题。 strncat接受第三个参数,该参数指定结果字符串的最大长度,并且有一个返回代码,用于指示串联是否因为达到此长度而被截断。

答案 2 :(得分:4)

这里已经是正确答案的原因和原因。要回答你的“分段错误...”,我建议使用调试器。使用调试信息编译您的程序,您将看到它发生的位置(让它运行并在崩溃时查看堆栈跟踪)。答案很可能也会回答原因和内容(在这种情况下已经回答过)

答案 3 :(得分:2)

调高编译器的警告级别:

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

getaddrinfo的第二个参数是const char *,而不是int。将PORT更改为NULL并在connect之前修改addrinfo结构中的端口号。

答案 4 :(得分:1)

使用valgrind。 Valgrind非常善于在您的代码中查找资源泄漏和其他常见的人为错误类型的错误行为。

答案 5 :(得分:0)

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