简体   繁体   English

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

[英]C++ Sockets - Client gives segmentation fault (linux)

I created a server/client connection.我创建了一个服务器/客户端连接。 The server and client both are compiling correctly but when I run the client, it gives me a Segmentation Fault (core dumped)服务器和客户端都正确编译,但是当我运行客户端时,它给了我一个Segmentation Fault (core dumped)

I don't know what I am doing wrong with my memory allocations.我不知道我的内存分配做错了什么。 The program is not dangling or anything.该程序没有悬空或任何东西。 I think my program is writing to a read-only portion of the memory, or maybe accessing a memory that is not available.我认为我的程序正在写入内存的只读部分,或者可能访问不可用的内存。

If anyone can tell where is the bug I would really appreciate it.如果有人能告诉我错误在哪里,我将不胜感激。

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

I am assuming you don't need the server.cpp since it's all good and waiting for incoming connections.我假设您不需要 server.cpp,因为它一切正常并等待传入​​连接。

Thanks!谢谢!

There are a number of problems with this code, but the immediate and fatal errors are:这段代码有很多问题,但直接和致命的错误是:

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

Allocates 1 character and tries to store the value of bufsize into it.分配 1 个字符并尝试将bufsize的值存储到其中。 bufsize is too big, so it gets truncated to 0. End result, buffer points to a single character, not an array of 1024, and that single value is set to 0. When you attempt to read bufsize bytes into buffer, you almost certainly overrun that single character and the behaviour is undefined . bufsize太大,因此被截断为 0。最终结果,缓冲区指向单个字符,而不是 1024 的数组,并且该单个值设置为 0。当您尝试将bufsize字节读入缓冲区时,您几乎可以肯定超出该单个字符并且行为未定义 Most likely it either destroys some other program data (and possibly causes problems later) or writes into invalid memory and crash immediately.它很可能会破坏一些其他程序数据(并且可能会在以后引起问题)或写入无效内存并立即崩溃。

I believe you meant我相信你的意思

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

Instead,反而,

char buffer[1024]; 

Will do what you want.会做你想做的。 Instead of bufsize , use sizeof(buffer) .使用sizeof(buffer)代替bufsize Further the following is often preferable:此外,以下通常更可取:

Up at the top of the file, right under the includes:在文件的顶部,就在包含的下面:

#define BUFSIZE 1024

and then进而

char buffer[BUFSIZE]; 

Now you can use BUFSIZE or sizeof(buffer) .现在您可以使用BUFSIZEsizeof(buffer) Both are resolved during compilation so there is no performance cost.两者都在编译期间解决,因此没有性能成本。

2018 Addendum: 2018 年附录:

constexpr int BUFSIZE = 1024;

Will have the same effect in modern C++ (C++11 or newer) and does not have the the downsides of macro substitution from the #define .在现代 C++(C++11 或更高版本)中具有相同的效果,并且没有#define宏替换的缺点。

The beauty of both options is the memory is self-managed.这两个选项的美妙之处在于内存是自我管理的。 char* buffer = new char[bufsize]; requires a delete[] buffer somewhere in your code to put the memory back.需要在代码中的某处使用delete[] buffer来放回内存。 And you have to make sure you get to that delete[] to prevent a leak.而且您必须确保到达该delete[]以防止泄漏。 Don't use pointers and dynamic allocation unless you have to.除非必须,否则不要使用指针和动态分配。

Next,下一个,

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

allocates a pointer, ip that is uninitialized.分配一个未初始化的指针ip Most likely the address if contains is made up of whatever crap happened to be on the stack and does not point to a valid char array.最有可能的地址 if contains 由堆栈上的任何垃圾组成,并且不指向有效的char数组。 Then "127.0.0.1" is written over whatever happened to be pointed to by ip .然后“127.0.0.1”被写入ip所指向的任何内容。 Similar effect to overrunning the end of buffer earlier.与之前溢出缓冲区末尾的效果类似。

Again, we know exactly what ip is going to point at, so the fix is easy:同样,我们确切地知道ip将指向什么,因此修复很容易:

char * ip = "127.0.0.1";

I prefer我更喜欢

char ip[] = "127.0.0.1";

but I have no reason for doing so.但我没有理由这样做。

2018 Addendum: I now have a reason for doing so. 2018 年附录:我现在有这样做的理由。 char * ip = "127.0.0.1"; is flat-out illegal in modern C++.在现代 C++ 中是完全非法的。 String literals are constant arrays, and assigning them to non constant pointers can lead to much badness if the pointer is used to modify the string literal.字符串文字是常量数组,如果使用指针修改字符串文字,将它们分配给非常量指针会导致很多坏处。 In the old days we just ignored the problem and never wrote to the literal.在过去,我们只是忽略了这个问题,从来没有写过文字。 Except when you did a few abstractions later and spent days or weeks debugging.除非您稍后进行了一些抽象并花费了数天或数周的时间进行调试。 Better to just cut the problem off at the source and copy the literal to a mutable array if there is a chance that it might be mutated.如果有可能发生变异,最好从源头上解决问题并将文字复制到可变数组中。 Even better is to remain const correct throughout the code if you can.如果可以的话,更好的是在整个代码中保持const 正确

Next up,接下来,

recv(client, buffer, bufsize, 0);

Has two problems:有两个问题:

It discards the number of bytes read and the error codes returned.它丢弃读取的字节数和返回的错误代码。 The program has no idea if it read anything at all due to a socket error or if it got the entire message, part of the message or more than the message.程序不知道它是否因为套接字错误而读取任何内容,或者它是否获得了整个消息、部分消息或更多消息。

It also demonstrates a misunderstanding of how TCP works.这也说明了对 TCP 工作原理的误解。 TCP does not work in nice, defined messages. TCP 在良好的、定义的消息中不起作用。 Data written into the socket may be packed into the same out-bound packet with other messages.写入套接字的数据可能与其他消息打包到同一个出站数据包中。 It may be split up across multiple packets that will arrive at different times.它可能会被分成多个在不同时间到达的数据包。 The logic behind this is out of scope for StackOverflow.这背后的逻辑超出了 StackOverflow 的范围。 Do some reading on TCP and streaming data.阅读 TCP 和流数据。

But wait!可是等等! There is more!还有更多!

cin >> buffer;

Will overflow buffer even if fixed to the expected size if the user types in 1024 or more characters (do not forget the null terminator is required).如果用户输入 1024 个或更多字符(不要忘记需要空终止符),即使将buffer固定为预期大小,也会溢出buffer Further, you don't know how many characters were input without counting them yourself.此外,您不知道输入了多少字符而不自己计算它们。 Painful and slow.痛苦而缓慢。 Fortunately there is std::string .幸运的是有std::string

std::string outbuf;
cin >> outbuf;

Solves both problems in one shot.一次解决这两个问题。 It resizes itself and keeps a count of its contents.它会调整自身大小并对其内容进行计数。 Neat, huh?整洁吧?

send(client, buffer, bufsize, 0);

Will send 1024 bytes of data even if the user typed in less.即使用户输入较少,也将发送 1024 字节的数据。 Or more.或者更多。 Yuck.哎呀。 Using outbuf from above,从上面使用outbuf,

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

Writes the correct number of characters every time, but if you want to preserve the string's terminating null, you'll have to send outbuf.length() + 1 characters.每次写入正确数量的字符,但如果您想保留字符串的终止空值,则必须发送outbuf.length() + 1字符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM