Skip to content
Advertisement

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)

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 << "ttt[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 PROGRAMnn";
close(client);
return 0;
}

I am assuming you don’t need the server.cpp since it’s all good and waiting for incoming connections.

Thanks!

Advertisement

Answer

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. 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. 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). 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). Both are resolved during compilation so there is no performance cost.

2018 Addendum:

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.

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. And you have to make sure you get to that delete[] to prevent a leak. 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. 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. Then “127.0.0.1” is written over whatever happened to be pointed to by 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:

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. char * ip = "127.0.0.1"; is flat-out illegal in modern 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.

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 does not work in nice, defined messages. 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. Do some reading on TCP and streaming data.

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). Further, you don’t know how many characters were input without counting them yourself. Painful and slow. Fortunately there is 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. Or more. Yuck. Using outbuf from above,

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.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement