我只是在一些网站上学习Linux上的套接字编程,下面是我在服务器端使用TCP:的代码的一些部分
#define BufferLength 100
#define SERVPORT 3111
int main()
{
/* Variable and structure definitions. */
int sd, sd2, rc, length = sizeof(int);
int totalcnt = 0, on = 1;
char temp;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
struct sockaddr_in their_addr;
fd_set read_fd;
/* Get a socket descriptor */
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Server-socket() error");
exit (-1);
}
else
printf("Server-socket() is OKn");
/* Allow socket descriptor to be reusable */
if((rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0)
{
perror("Server-setsockopt() error");
close(sd);
exit (-1);
}
else
printf("Server-setsockopt() is OKn");
/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("Using %s, listening at %dn", inet_ntoa(serveraddr.sin_addr), SERVPORT);
/* continue */
}
当我做最后一行(printf("使用……")时,我遇到了分段错误,为什么?谢谢
如图所示的代码未命中#include
的任何标头,因此由于一些未定义的符号,目前无法编译。
然而,如果您错过了对代码引用的任何库函数进行原型化,则它将进行编译,这将导致任何函数被假定返回int
。
后一个事实可能是致命的,也可能不是致命的。
至少在64位系统上,在inet_ntoa()
用作printf()
的参数的情况下,这是致命的,因为在64位的系统上,它很可能返回64位(char
-指针)值(但返回32位int
)。因此(假设原型未命中),当生成代码时,编译器假设inet_ntoa()
返回32位int
,这将导致"截断"返回地址的最高有效32位。尝试从这样一个"瘫痪"(因此(很可能)无效的地址printf()
会引发未定义的行为,在您的情况下会导致观察到的分段违规。
要解决此问题,请添加相关原型(用于inet_ntoa()
),方法是添加:
#include <arpa/inet.h>
编译器应该警告你这一点。要启用gcc的所有编译器警告,请使用选项-Wall -Wextra -pedantic
。认真对待这些警告。
看起来inet_ntoa()可能以某种方式返回NULL,导致在printf()中取消引用时出现segfault。我找不到一个直接的引用来明确说明这在Linux版本的inet_ntoa中是可能的,但我找到了几个人提出了这一说法,这是该代码中唯一一个指针被取消引用的地方。
这个问题底部的答案是:inet_ntoa的分段错误导致inet_ntoa可以返回NULL。然而,根据他的参考链接,我找不到关于这一事实的真实陈述。
MSDN上有一篇文章(这很有启发性,但当然不直接适用于Linux代码)明确指出inet_ntoa()可以在这里返回NULL:https://msdn.microsoft.com/en-us/library/windows/desktop/ms738564%28v=vs.85%29.aspx