C++ 套接字 - 客户端给出分段错误(linux)

Posted

技术标签:

【中文标题】C++ 套接字 - 客户端给出分段错误(linux)【英文标题】:C++ Sockets - Client gives segmentation fault (linux) 【发布时间】:2015-07-10 22:32:56 【问题描述】:

我创建了一个服务器/客户端连接。服务器和客户端都在正确编译,但是当我运行客户端时,它给了我一个Segmentation Fault (core dumped)

我不知道我的内存分配做错了什么。该程序没有悬空或任何东西。我认为我的程序正在写入内存的只读部分,或者可能正在访问不可用的内存。

如果有人能告诉我错误在哪里,我将不胜感激。

client.cpp

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;

int main() 
    char a;
    int client;
    int portNum = 1500;
    int bufsize = 1024;
    char* buffer = new char (bufsize);
    bool isExit = false;
    char* ip;
    strcpy(ip, "127.0.0.1");

struct sockaddr_in direc;

if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    cout << "Error creating socket..." << endl;
    exit(0);


cout << "Enter # to end call" << endl;
cout << "\t\t\t[s] to begin with" << endl;
cin >> a;

cout << "Socket created successfully..." << endl;
direc.sin_family = AF_INET;
direc.sin_port = htons(portNum);
inet_pton(AF_INET, ip, &direc.sin_addr);

if (connect(client,(struct sockaddr *)&direc, sizeof(direc)) == 0)
    cout << "Connection to the server " << inet_ntoa(direc.sin_addr) << endl;

cout << "Awaiting confirmation from the server..." << endl;
recv(client, buffer, bufsize, 0);

cout << "Response received: " << buffer;
cout << "\nRemember to put an asterisk at the end to send a message * \n Enter # to terminate the connection" << endl;

do 
    cout << "Enter a message: ";
    do 
        cin >> buffer;
        send(client, buffer, bufsize, 0);
        if (*buffer == '#') 
            send(client, buffer, bufsize, 0);
            *buffer = '*';
            isExit = true;
        
     while (*buffer != 42);

    cout << "Mensage received: ";
    do 
        recv(client, buffer, bufsize, 0);
        cout << buffer << " ";
        if (*buffer == '#') 
            *buffer = '*';
            isExit = true;
        

     while (*buffer != 42);
    cout << endl;

 while (!isExit);
cout << "Connection terminated. END PROGRAM\n\n";
close(client);
return 0;

我假设您不需要 server.cpp,因为它一切正常并正在等待传入连接。

谢谢!

【问题讨论】:

char* ip; strcpy(ip, "127.0.0.1"); 从来没有为指针分配任何存储空间,从来没有将它指向任何东西,然后将数据复制到那个未定义的区域。咔嚓! ...而char* buffer = new char (bufsize); 与您的想法不同(() [])。而你正好有 0 delete[]。 ...I don't know what I am doing wrong with my memory allocations. 一切。任何地方都没有正确的分配。 为什么不直接写char *ip = "127.0.0.1"; 该缓冲区位同上。大小是已知的,所以只需char buffer[1024]; cin &gt;&gt; buffer; 如果用户输入超过 1024 个字符怎么办? send(client, buffer, bufsize, 0); 只写了整个缓冲区,而不是用户输入的内容。 【参考方案1】:

这段代码有很多问题,但直接和致命的错误是:

int bufsize = 1024;
char* buffer = new char (bufsize);

分配 1 个字符并尝试将 bufsize 的值存储到其中。 bufsize 太大,因此被截断为 0。最终结果,缓冲区指向单个字符,而不是 1024 的数组,并且该单个值设置为 0。当您尝试将 bufsize 字节读入缓冲区时,您几乎肯定会超出该单个字符和behaviour is undefined。很可能它要么破坏了一些其他程序数据(并可能在以后引起问题),要么写入无效内存并立即崩溃。

我相信你的意思

int bufsize = 1024;
char* buffer = new char[bufsize];

相反,

char buffer[1024]; 

会做你想做的。代替bufsize,使用sizeof(buffer)。此外,通常更可取的是:

在文件的顶部,包含以下内容:

#define BUFSIZE 1024

然后

char buffer[BUFSIZE]; 

现在您可以使用BUFSIZEsizeof(buffer)。两者都在编译期间解决,因此没有性能成本。

2018 年附录:

constexpr int BUFSIZE = 1024;

在现代 C++(C++11 或更高版本)中具有相同的效果,并且没有 #define 中宏替换的缺点。

这两种选择的美妙之处在于内存是自我管理的。 char* buffer = new char[bufsize]; 需要在代码中的某处使用 delete[] buffer 才能将内存放回原处。而且您必须确保到达delete[] 以防止泄漏。除非必须,否则不要使用指针和动态分配。

接下来,

char* ip;
strcpy(ip, "127.0.0.1");

分配一个未初始化的指针ip。 if contains 的地址很可能由堆栈上发生的任何垃圾组成,并且不指向有效的char 数组。然后将“127.0.0.1”覆盖ip 所指向的任何内容。与早先超出缓冲区末尾的效果类似。

同样,我们确切地知道ip 将指向什么,因此修复很简单:

char * ip = "127.0.0.1";

我更喜欢

char ip[] = "127.0.0.1";

但我没有理由这样做。

2018 年附录:我现在有理由这样做了。 char * ip = "127.0.0.1"; 在现代 C++ 中是完全非法的。字符串文字是常量数组,如果将指针用于修改字符串文字,将它们分配给非常量指针可能会导致很多问题。在过去,我们只是忽略了这个问题,从不写信给文字。除非您稍后进行了一些抽象并花费数天或数周进行调试。如果有可能发生突变,最好从源头切断问题并将文字复制到可变数组中。如果可以的话,最好在整个代码中保留const correct。

接下来,

recv(client, buffer, bufsize, 0);

有两个问题:

它丢弃读取的字节数和返回的错误代码。程序不知道它是否由于套接字错误而读取了任何内容,或者它是否获得了整个消息、部分消息或多于消息。

这也说明了对 TCP 工作原理的误解。 TCP 在好的、已定义的消息中不起作用。写入套接字的数据可能会与其他消息一起打包到同一个出站数据包中。它可能被拆分为多个将在不同时间到达的数据包。这背后的逻辑超出了 *** 的范围。阅读 TCP 和流数据。

但是等等!还有更多!

cin >> buffer;

如果用户输入 1024 个或更多字符,即使固定为预期大小,buffer 也会溢出(不要忘记需要空终止符)。此外,如果不自己计算,您不知道输入了多少个字符。痛苦而缓慢。幸好有std::string

std::string outbuf;
cin >> outbuf;

一口气解决了这两个问题。它调整自己的大小并记录其内容。整洁吧?

send(client, buffer, bufsize, 0);

即使用户输入较少,也会发送 1024 字节的数据。或者更多。呸。从上面使用 outbuf,

send(client, outbuf.c_str(), outbuf.length(), 0);

每次写入正确数量的字符,但如果要保留字符串的终止空值,则必须发送 outbuf.length() + 1 字符。

【讨论】:

哇,这就是我所说的详细而详尽的答案。 +1,当之无愧!

以上是关于C++ 套接字 - 客户端给出分段错误(linux)的主要内容,如果未能解决你的问题,请参考以下文章

C++ 服务器 Linux 机器上的分段错误 - 适用于 Mac

大型二维数组给出分段错误

尝试在 C++ 原始套接字上分配帧 ethh->h_dest 时出现分段错误

linux (g++) 上的分段错误,但 Mac OS 上没有。?

执行 push_back 后,集合的 C++ 向量给出分段错误

C++ 11 Boost 1.65 recursive_directory_iterator 给出分段错误错误