1

I am trying to write a basic socket application that will accept a string from a client and do some work on data structures I have in memory. I am trying to make use of C++11 because I'd like to have language support for multithreading and I've found an odd problem. I have code that creates a listen socket on port 1234, and accepts one connection, prints out a dummy string and closes it. If I compile it with:

g++ -o socket_test socket_test.cpp

it works perfectly, but if I compile it with:

g++ -std=c++11 -o socket_test socket_test.cpp

it compiles and runs fine, but if I try to connect to the port, it bounces back with connection refused. Test code is here:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

using namespace std;

int main()
{
  cout << "Hello world, I'm going to open a socket on port 1234 now!" << endl;

  int m_socket = 0;
  int m_client = 0;
  int port = 1234;
  sockaddr_in serv_addr, client_addr;
  socklen_t client_len;

  m_socket = socket(AF_INET, SOCK_STREAM, 0);
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(port);
  bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
  cout << "Listen returns: " << listen(m_socket, SOMAXCONN) << endl;
  m_client = accept(m_socket, (struct sockaddr *)&client_addr, &client_len);
  write(m_client, "Numa numa\n", 10);
  close(m_client);
  close(m_socket);

  return 0;
}

My build environment is OSX 10.9.2, using g++ as follows:

battra:socket_test matt$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr 
--with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/
MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.2.0
Thread model: posix
battra:socket_test matt$ 
Matt K
  • 13
  • 3
  • 7
    Please learn about *return value checking and error handling*. There's no point in wasting time on undebuggable code. – Kerrek SB Jun 29 '14 at 21:39
  • This is a stripped down test version, the full code behaves the same way and checks all the return values, no error codes come up. I also just added a print out of the port bound after calling listen() and the C++11 version is always a randomly assigned port in the upper 60000s, the other version is always port 1234. So that seems to be the problem, I am still not sure why bind() is grabbing ports differently between the two versions. – Matt K Jun 29 '14 at 22:08

2 Answers2

8

This problem is a classic example of why it's a bad idea to import an entire namespace. Eventually, the contents of that imported namespace will change - in this case, with the addition of std::bind in C++11 - and the unqualified names that worked just fine before silently change meaning.

Had you been checking the return value of bind:

cout << "Bind returns: "
     << bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr))
     << endl;

the resulting compile error would have made the problem more readily apparent:

main.cpp:24:8: error: invalid operands to binary expression ('basic_ostream<char, std::__1::char_traits<char> >' and '__bind<int &, sockaddr *, unsigned long>')

   << bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr))
   ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The simple solution is to use the qualified name ::bind to make it clear that you mean bind from the global namespace. The better solution is to remove using namespace std from your program altogether and avoid similar problems in the future.

Casey
  • 41,449
  • 7
  • 95
  • 125
1

I suspect that the problem has nothing to do with the fact that you are using -std=c++11, and more with the fact that you ran that version second.

If you try running the first version twice, you will probably notice the same error on the second run, because the port was not yet released from the first run.

If you change the port number and run again, it should once again work.

To deal with this problem, you should take a look at the discussion here.

I modified your program to use SO_REUSEADDR and it seems to work consistently with and without the c++11 option. Note that I am running on Ubuntu, so your OS X may behave differently.

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

using namespace std;

int main()
{
  cout << "Hello world, I'm going to open a socket on port 1234 now!" << endl;

  int m_socket = 0;
  int m_client = 0;
  int port = 1236;
  sockaddr_in serv_addr, client_addr;
  socklen_t client_len;


  m_socket = socket(AF_INET, SOCK_STREAM, 0);
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(port);

  // Set to reuse
  int so_reuseaddr = 1;
  setsockopt(m_socket,SOL_SOCKET,SO_REUSEADDR,
          &so_reuseaddr,
          sizeof so_reuseaddr);


  bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
  cout << "Listen returns: " << listen(m_socket, SOMAXCONN) << endl;
  m_client = accept(m_socket, (struct sockaddr *)&client_addr, &client_len);
  write(m_client, "Numa numa\n", 10);
  close(m_client);
  close(m_socket);

  return 0;
}
Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • I did think of that, the same occurs if I wait several minutes between runs, and the first versions runs successively without problems. I've waited up to 30 minutes between tests and it still occurs. – Matt K Jun 29 '14 at 22:00
  • @user3788528, Did you try changing the port? It is quite possible that the new standard changes the default options for binding. – merlin2011 Jun 29 '14 at 22:01
  • I did try changing the port anyway, no difference, but as I posted above I added a print out of the port bound after listen and it's not 1234 in the C++11 version, it seems to be a randomly assigned port. Not sure why. – Matt K Jun 29 '14 at 22:09
  • @user3788528, Please see the update and see if that works for you. I do not have OS X to test on but it works on Ubuntu. – merlin2011 Jun 29 '14 at 22:15
  • 2
    I just tried it and I'm seeing the same behavior, odd that it's different on OS X, though I think I solved the problem. It seems C++11 has a new `bind()` function, which seems to be taking priority here. If I change the call to `::bind()` it works. I added some extra code to print out the port it was bound to, without the :: change the C++11 code was binding to a random port, I suspect because `listen()` was implicitly binding a port after the wayward `bind()` call did nothing useful for sockets. With the global namespace being made explicit it seems to work consistently. – Matt K Jun 29 '14 at 22:38
  • @user3788528, I'm glad you solved your problem. You should write your own answer and accept it. I am not too surprised that Linux is different than OS X because OS X is based on BSD. – merlin2011 Jun 29 '14 at 22:39
  • I'll definitely do that, but the system won't allow it for quite some time after the initial post of a new user it seems. I'll do it when the time limit expires though. – Matt K Jun 29 '14 at 22:41