Skip to main content
GameDev.net gamedev.net
🔒 Locked

Basic WinSock/BSD TCP connection

Started by Gumgo Aug 22, 2011 at 11:44 PM 3 replies 2k views
Original Post
Gumgo
Gumgo
Firstly, my apologies for posting a boring "debug my code" topic, I've just been struggling with this for a few days and can't seem to identify the problem. Hopefully it's something quick and easy.

Anyway, I've written some basic TCP server and client code using the WinSock (and BSD sockets) API. Most of it is straight from Beej's TCP stream server tutorial. Unfortunately, I can't seem to get the client and server to connect on localhost. Here's what I've got:

WINSOCK/BSD COMPATIBILITY FIXES:
[source lang="cpp"]

#ifdef _WIN32
// The type for a socket handle.
typedef SOCKET SocketHandle;

// An invalid socket handle.
static const SocketHandle INVALID_SOCKET_HANDLE = INVALID_SOCKET;
#else
typedef SocketHandle int;
static const SocketHandle INVALID_SOCKET_HANDLE = -1;
#endif

// Returns the last error code.

int socketCheckError() {
#ifdef _WIN32
return WSAGetLastError();
#else
return errno;
#endif
}

#ifdef _WIN32

// Converts an error code the form for the appropriate platform.
#define socketErrorCode( code ) WSA ## code
#else
#define socketErrorCode( code ) code
#endif
[/source]

SERVER:
[source lang="cpp"]
// INITIALIZATION CODE:


LogThread::get().info() << "Attempting to initialize listening socket on port " << port;


// first get information about the server

addrinfo hints;
addrinfo * serverInfo;
SocketHandle socketId;

memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

int result;

std::stringstream ss;
ss << port;
std::string portStr = ss.str();

result = getaddrinfo( NULL, portStr.c_str(), &hints, &serverInfo );
if (result) {
LogThread::get().error() << "Failed to initialize listening socket: could not get network info";
return false;
}

// try to bind to a socket

addrinfo * p;
for (p = serverInfo; p != NULL; p = p->ai_next) {
socketId = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
if (socketId == INVALID_SOCKET_HANDLE)
continue;

int intYes = 1;
if (setsockopt( socketId, SOL_SOCKET, SO_REUSEADDR, (char*)&intYes, sizeof( int ) )) {
LogThread::get().error() << "Failed to initialize listening socket: could not set socket options";
closesocket( socketId );
freeaddrinfo( serverInfo );
return false;
}

// set the socket to be nonblocking

#ifdef _WIN32
u_long ulongYes;
if (ioctlsocket( socketId, FIONBIO, &ulongYes )) {
#else
if (fcntl( socketId, F_SETFL, O_NONBLOCK )) {
#endif
LogThread::get().error() << "Failed to initialize listening socket: could not set socket to nonblocking mode";
closesocket( socketId );
freeaddrinfo( serverInfo );
return false;
}

if (bind( socketId, p->ai_addr, p->ai_addrlen )) {
closesocket( socketId );
continue;
}

break;
}

if (p == NULL) {
LogThread::get().error() << "Failed to initialize listening socket: could not find appropriate settings";
freeaddrinfo( serverInfo );
return false;
}

freeaddrinfo( serverInfo );

// listen on the socket

if (listen( socketId, SOMAXCONN )) {
LogThread::get().error() << "Failed to initialize listening socket: could not listen on socket";
closesocket( socketId );
return false;
}

listenSocketId = socketId;

LogThread::get().info() << "Initialized listening socket";
return true;

// NETWORK LISTENING CODE:


if (!init()) {
// ... blah, error stuff ...
} else {
while (!quit) {
// set up the read set for select and have it time out after 1 second (so we can quit rather than wait forever)
fd_set readSet;
FD_ZERO( &readSet );
FD_SET( listenSocketId, &readSet );
timeval timeout;
timeout.tv_usec = 0;
timeout.tv_sec = 1;
int selectResult = select( (int)(listenSocketId + 1), &readSet, NULL, NULL, &timeout );
// if selectResult is 0 we timed out, so loop again... otherwise:
if (selectResult < 0)
LogThread::get().error() << "select() failed";
else if (selectResult > 0) {
if (!FD_ISSET( listenSocketId, &readSet ))
LogThread::get().warning() << "select() returned without timing out but no network activity occurred";
else {
LogThread::get().info() << "Client connection attempt detected";
sockaddr_storage clientInfo;
int clientInfoSize = (int)sizeof( clientInfo );
SOCKET clientSocketId = accept( listenSocketId, (sockaddr*)&clientInfo, &clientInfoSize );
if (clientSocketId == INVALID_SOCKET_HANDLE)
LogThread::get().info() << "Failed to connect to client";
else {
char ip[INET_ADDRSTRLEN > INET6_ADDRSTRLEN ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN];
ip[0] = '?'; // in case getting the IP fails
ip[1] = '\0;';
if (clientInfo.ss_family == AF_INET)
inet_ntop( clientInfo.ss_family, &((sockaddr_in*)&clientInfo)->sin_addr, ip, sizeof( ip ) );
else if (clientInfo.ss_family == AF_INET6)
inet_ntop( clientInfo.ss_family, &((sockaddr_in6*)&clientInfo)->sin6_addr, ip, sizeof( ip ) );
LogThread::get().info() << "Connected to client " << ip << "; assigned ID: " << clientSocketId;
boost::mutex::scoped_lock lock( childThreadCountMutex );
++childThreadCount;
// create a thread to handle the client request
boost::thread connectionThread( (NetworkConnectionThread(
clientSocketId,
boost::function ( boost::bind( &NetworkListenerThread::onChildThreadTerminate, this ) ) )) );
}
}
}
if (stopSignal)
quit = true;
}


closesocket( listenSocketId );
LogThread::get().info() << "Terminated listening socket";
}

[/source]

CLIENT (just up to connect):
[source lang="cpp"]
std::vector ret;


// get info about the server

addrinfo hints;
addrinfo * serverInfo;
SocketHandle socketId;

memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

int result;

std::stringstream ss;
ss << port;
std::string portStr = ss.str();

result = getaddrinfo( ip.c_str(), portStr.c_str(), &hints, &serverInfo );
if (result)
return ret;

// try to connect

addrinfo * p;
for (p = serverInfo; p != NULL; p = p->ai_next) {
socketId = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
if (socketId == INVALID_SOCKET_HANDLE)
continue;

if (connect( socketId, p->ai_addr, p->ai_addrlen )) {
closesocket( socketId );
continue;
}

break;
}

// the connection now made if p != NULL
[/source]

(Also, I am calling WSAStartup() before this on Windows, so that isn't the problem.)

I'm attempting to run the server program on my computer and then connect using the client to 127.0.0.1. I'm using the same port on both and I've tried several with no luck. The sockets seem to be being initialized fine (functions are all returning 0 which is no error) but connect() always fails and select() never returns anything except for 0, i.e. timeout. I've also tried without using select() but instead just having it go straight to accept() but that also didn't work (accept simply never returned). Does anything look weird with the code I have? Thanks.
hplus0603
hplus0603
[font="Franklin Gothic Medium"]

Firstly, my apologies for posting a boring "debug my code" topic, I've just been struggling with this for a few days and can't seem to identify the problem. Hopefully it's something quick and easy.



[/font] u_long ulongYes; [color="#006699"]if [font="Franklin Gothic Medium"][font="Courier New"](ioctlsocket( socketId, FIONBIO, &ulongYes )) {
[/font]

You'll actually want to initialize ulongYes to some value. Also, if you're using select(), you don't want to be using non-blocking I/O -- especially not on the accepting socket.

If you start up the server, and run "netstat -na," do you see your server listening on the TCP port and interface you're expecting? Is it bound to 0.0.0.0 or some specifc interface?

If you run WireShark while running the client, what packets do you see the client send? (This may need two machines, virtual machines, to show anything on Windows, depending on whether WinPCap manages to capture the local interface).



[/font]
enum Bool { True, False, FileNotFound };
Gumgo
Gumgo
Oops! That uninitialized variable was definitely unintentional! Regarding nonblocking, the reason I am doing that is so I am able to gracefully terminate. select() has its own blocking options separate from nonblocking mode (at least as far as I can tell) and I don't want to block on accept in case something happens between select and accept and the client disconnects... though I guess in that case accept would just return -1 or something, so maybe it's not an issue.

Anyway, "netstat -na" reveals that the socket is bound to the port I'm using (11652) with... no IP address? Here's the line:


TCP [::]:11652 [::]:0 LISTENING


There are other listening sockets that either have 127.0.0.1 or 0.0.0.0 in place of the [font="'Courier New"][::][/font]. Any idea what this means?
hplus0603
hplus0603

There are other listening sockets that either have 127.0.0.1 or 0.0.0.0 in place of the [font="Courier New"][::][/font]. Any idea what this means?


This means you've bound only to the IPv6 interface. That's because you're only accepting the first result, and are not specifying explicitly IPv4 when you ask for address info.

I would just fill out a sockaddr_in directly. Clear it out, set sin_family to AF_iNET and sin_port to htons(port) and bind to that.
enum Bool { True, False, FileNotFound };
Gumgo
Gumgo
And that was the problem! Thanks hplus0603!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.