Advertisement

Winsock Async connection problem

Started by August 26, 2003 04:43 PM
2 comments, last by TDragon 21 years, 5 months ago
I'm programming a multiplayer RTS in C++. The engine is not server based, but establishes a two-way connection between the computers (just two for now). The sockets obviously need to be asynchronous at both ends, rather than the standard client-server setup, but I haven't yet found any actual code for a dual asynchronous connection. So I've cobbled something together myself, in hopes that it would work. And of course it doesn't. As near as I can tell, the "host" end is crashing as soon as it receives the "FD_ACCEPT" message, and the client end is receiving at least one "WSAEWOULDBLOCK" error, trying to reconnect, then failing. Here is the code. I am only a beginner at sockets programming, so there is probably some bad code in there. HOWEVER, please do not reply unless you are fairly certain you know what my problem is, or have identified some code that would cause other problems. This is a cut-and-paste from the source file "netcom.cpp". The struct named "connection1" is defined in "xrtse.h" and merely contains sockets for the local and remote computers, the connection status, and other unimportant info. Note that some lines are commented out. Some are because I don't want to mess with that area yet and haven't got that far anyway; others are in trying to figure out what's going wrong. Assume that anything having to do with the connecting process has been tried both uncommented and commented.

/* -- netcom.cpp --
 Game communications across the net for xRTSe
*/

#include "xrtse.h"


BOOL EstablishConnection(void)
 {
  HWND hwndProc;
  // Must be done at the beginning of every WinSock program

  WSADATA w;    // used to store information about WinSock version

  int keyp;

  int error = WSAStartup(0x0202, &w);   // Fill in w

  if (error)
   { // there was an error

    xrtse_error("Could not init Winsock2");
    return FALSE;
   }
  if (w.wVersion != 0x0202)
   { // wrong WinSock version!

    xrtse_error("Wrong winsock version!");
    WSACleanup(); // unload ws2_32.dll

    return FALSE;
   }

  hwndProc = CreateWindow(
   "WsockProcWnd", //name of window class

   "Winsock Proc", //title

   0, //normal window style

   0, //X COORD

   0, //Y COORD

   0, //WIDTH

   0, //HEIGHT

   HWND_DESKTOP, //no parent window

   NULL, //no menu override

   g_instance, //handle of this instance

   NULL);
  if (!hwndProc)
   {
    xrtse_error("Could not create proc window.");
    return FALSE;
   }

  clear_bitmap(screen);
  textout(screen, font, "Choose Connection Type:", 5, 5, makecol(255, 255, 255));
  textout(screen, font, "  1. Host -- You must provide your IP address to the other player.", 5, text_height(font) + 5, makecol(255, 255, 255));
  textout(screen, font, "  2. Client -- You must input the IP address provided by the other player.", 5, text_height(font) * 2 + 5, makecol(255, 255, 255));
  textout(screen, font, "(Press [Esc] to stop the connection process.)", 5, text_height(font) * 4 + 5, makecol(255, 255, 255));

  keyp = readkey();
  while ((keyp >> 8 != KEY_ESC) && (keyp >> 8 != KEY_1) && (keyp >> 8 != KEY_2))
    keyp = readkey();

  if (keyp >> 8 == KEY_ESC)
   {
    DestroyWindow(hwndProc);
    return FALSE;
   }
  if (keyp >> 8 == KEY_1)
   {
    if (!CreateHostConnection(hwndProc))
     {
      DestroyWindow(hwndProc);
      return FALSE;
     }
    return TRUE;
   }
  else //(keyp & 0xFF == '2')

   {
    if (!CreateClientConnection(hwndProc))
     {
      DestroyWindow(hwndProc);
      return FALSE;
     }
    return TRUE;
   }

  if (hwndProc)
    DestroyWindow(hwndProc);
  return TRUE;
 }


BOOL CreateHostConnection(HWND hwndProc)
 {
  MSG msg;
  sockaddr_in addr; // the address structure for a TCP socket


  connection1.type = CTYPE_HOST;
  connection1.status = CSTAT_NOTCONNECTED;
  connection1.can_send = FALSE;
  for (int i = 0; i < 99; i++)
    connection1.ip[i] = '\0';
  connection1.loc_speed = 0;
  connection1.rem_speed = 0;

  connection1.loc_socket = socket(AF_INET, SOCK_STREAM, 0); // Create socket

  if (connection1.loc_socket == INVALID_SOCKET)
   {
    xrtse_error("Could not create connection socket.");
    return FALSE;
   }

  WSAAsyncSelect(connection1.loc_socket, hwndProc, WM_WSAASYNC, FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE);

  addr.sin_family = AF_INET;      // Address family Internet

  addr.sin_port = htons(5001);   // Assign port 5001 to this socket

  addr.sin_addr.s_addr = htonl(INADDR_ANY);   // No destination

  if (bind(connection1.loc_socket, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
   { // error

    xrtse_error("Could not bind socket.");
    WSACleanup();  // unload WinSock

    return FALSE;         // quit

   }
  if (listen(connection1.loc_socket, 2) == SOCKET_ERROR)
   {
    xrtse_error("Could not listen at socket.");
    WSACleanup();
    return FALSE;
   }

  connection1.status = CSTAT_WAITING;
  clear_bitmap(screen);
  textout(screen, font, "Waiting for connection on port 5001 . .", 5, 5, makecol(255, 255, 255));

  while (TRUE)
   {
    if (connection1.status > CSTAT_WAITING)
      break;
    if (key[KEY_ESC])
     {
      closesocket(connection1.loc_socket);
      WSACleanup();
      return FALSE;
     }
    if (PeekMessage(NULL, hwndProc, 0, 0, PM_NOREMOVE))
     {
      while (PeekMessage(&msg, hwndProc, 0, 0, PM_REMOVE))
       {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
       }
     }
   }

  shutdown(connection1.loc_socket, SD_BOTH);
  closesocket(connection1.loc_socket);
  DestroyWindow(hwndProc);
  WSACleanup();

  return TRUE;
 }

LRESULT CALLBACK WSHostProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {

  switch(message)
   {
    case WM_WSAASYNC:
     {
      MessageBox(NULL, "Connect request received.", "xRTSE Info", MB_OK | MB_ICONINFORMATION);
      // what word?

      switch(WSAGETSELECTEVENT(lParam))
       {
        case FD_ACCEPT:
         {
          // check for an error

          if (!WSAGETSELECTERROR(lParam))
            return FALSE;
          sockaddr client_sock;
          int sock_size = (int)sizeof(client_sock);
          // process message

          connection1.rem_socket = accept(wParam, &client_sock, &sock_size);
          if (connection1.rem_socket == INVALID_SOCKET)
           {
            xrtse_error("Could not connect to remote client.");
            return FALSE;
           }
          connection1.status = CSTAT_CONNECTED;
          return 0;
         } //FD_ACCEPT


         /*
        case FD_WRITE:  // we can send data
         {
          // enter an infinite loop
          connection1.can_send = TRUE;
          while(TRUE)
           {
            // read in more data from the file and store it in packet.data.
            in.read((char*)&packet.data, MAX_PACKET_SIZE);

            // increment the amount of data sent
            data_sent += strlen(packet.data);

            // send the packet off to the Server if it is filled
            if (send(wparam, (char*)(&packet), sizeof(PACKET), 0) == SOCKET_ERROR)
             {
              // check if the network buffer is full and can send no more
              // data. If so then break from the loop
              if (WSAGetLastError() == WSAEWOULDBLOCK)
               {
                // break from the loop if buffer is full
                connection1.can_send = FALSE;
                break;
               }
              else // another error
               {
                // display an error message and clean up
                CleanUp();
                return 0;
               }
             }
           }
         }
          break; //FD_WRITE
          */
       }
     }

    case WM_PAINT:
      break;

    case WM_DESTROY: //terminate the program

      PostQuitMessage(0);
      break;

    default:
     //let windows process the rest

     return DefWindowProc(hWnd, message, wParam, lParam);
   }

  return 0;
 }


BOOL CreateClientConnection(HWND hwndProc)
 {
  MSG msg;
  sockaddr_in addr; // the address structure for a TCP socket

  sockaddr_in target;
  char rem_ip[100];
  int keyp;

  connection1.type = CTYPE_CLIENT;
  connection1.status = CSTAT_NOTCONNECTED;
  connection1.can_send = FALSE;
  for (int i = 0; i < 99; i++)
    connection1.ip[i] = '\0';
  connection1.loc_speed = 0;
  connection1.rem_speed = 0;

  for (int i = 0; i < 99; i++)
    rem_ip[i] = '\0';
  clear_bitmap(screen);
  textout(screen, font, "Enter host IP: _", 5, 5, makecol(255, 255, 255));
  while (TRUE)
   {
    keyp = readkey();
    if (keyp >> 8 == KEY_ESC)
      return FALSE;
    if (keyp >> 8 == KEY_BACKSPACE)
     {
      if (strlen(rem_ip) > 0)
        rem_ip[strlen(rem_ip) - 1] = '\0';
     }
    else if (keyp >> 8 == KEY_ENTER)
     {
      if (strlen(rem_ip) <= 0)
        return FALSE;
      else
        break;
     }
    else
      rem_ip[strlen(rem_ip)] = (char)(keyp & 0xFF);
    clear_bitmap(screen);
    textprintf(screen, font, 5, 5, makecol(255, 255, 255), "Enter host IP: %s_", rem_ip);
   }

  connection1.loc_socket = socket(AF_INET, SOCK_STREAM, 0); // Create socket

  if (connection1.loc_socket == INVALID_SOCKET)
   {
    xrtse_error("Could not create connection socket.");
    return FALSE;
   }
  //addr.sin_family = AF_INET;      // Address family Internet

  //addr.sin_port = htons(5001);   // Assign port 5001 to this socket

  //addr.sin_addr.s_addr = htonl(INADDR_ANY);   // No destination

  //if (bind(connection1.loc_socket, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)

  // { // error

  //  xrtse_error("Could not bind socket.");

  //  WSACleanup();  // unload WinSock

  //  return FALSE;         // quit

  // }

  WSAAsyncSelect(connection1.loc_socket, hwndProc, WM_WSAASYNC, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);

  connection1.status = CSTAT_WAITING;
  clear_bitmap(screen);
  textprintf(screen, font, 5, 5, makecol(255, 255, 255), "Connecting to %s on port 5001", rem_ip);

  target.sin_family = AF_INET;           // address family Internet

  target.sin_port = htons(5001);        // set server's port number

  target.sin_addr.s_addr = inet_addr((const char *)rem_ip);  // set server's IP

  if (connect(connection1.loc_socket, (LPSOCKADDR)⌖, sizeof(target)) == SOCKET_ERROR)
   { // an error connecting has occurred!

    if (WSAGetLastError() == WSAEWOULDBLOCK)
     {
      while (WSAGetLastError() == WSAEWOULDBLOCK)
       {
        textout(screen, font, "Blocked...trying again", 50, 50, makecol(255, 255, 255));
        rest(750);
        if (connect(connection1.loc_socket, (LPSOCKADDR)⌖, sizeof(target)) != SOCKET_ERROR)
          break;
        if (key[KEY_ESC])
          return FALSE;
       }
      if (WSAGetLastError() != WSAEWOULDBLOCK)
       {
        xrtse_error("Could not connect to host socket after block reroute.");
        WSACleanup();
        return FALSE;
       }
     }
    else
     {
      xrtse_error("Could not connect to host socket.");
      WSACleanup();
      return FALSE;
     }
   }

  while (TRUE)
   {
    if (connection1.status > CSTAT_WAITING)
      break;
    if (key[KEY_ESC])
     {
      closesocket(connection1.loc_socket);
      WSACleanup();
      return FALSE;
     }
    if (PeekMessage(NULL, hwndProc, 0, 0, PM_NOREMOVE))
     {
      while (PeekMessage(&msg, hwndProc, 0, 0, PM_REMOVE))
       {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
       }
     }
   }

  shutdown(connection1.loc_socket, SD_BOTH);
  closesocket(connection1.loc_socket);
  DestroyWindow(hwndProc);
  WSACleanup();

  return TRUE;
 }

LRESULT CALLBACK WSClientProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {

  switch(message)
   {
    case WM_WSAASYNC:
     {
      // what word?

      switch(WSAGETSELECTEVENT(lParam))
       {
        case FD_CONNECT:
         {
          // check for an error

          if (!WSAGETSELECTERROR(lParam))
            return FALSE;
          connection1.status = CSTAT_CONNECTED;
          return 0;
         } //FD_CONNECT

          break;

//        case FD_READ:

//         {


       }
     }

    case WM_PAINT:
      break;

    case WM_DESTROY: //terminate the program

      PostQuitMessage(0);
      break;

    default:
     //let windows process the rest

     return DefWindowProc(hWnd, message, wParam, lParam);
   }

  return 0;
 }


//end of netcom.cpp


EDIT: Forgot to mention...this is C++, Win32 API, and Allegro...so the keyboard input and screen output are Allegro stuff, and the message handling is obviously W32API. I'm pretty sure that code has no bugs (but hey, I may be wrong). Also, the "host" end is crashing on "FD_ACCEPT" (not FD_CONNECT). [edited by - TDragon on August 27, 2003 3:47:18 PM]
{[JohnE, Chief Architect and Senior Programmer, Twilight Dragon Media{[+++{GCC/MinGW}+++{Code::Blocks IDE}+++{wxWidgets Cross-Platform Native UI Framework}+++
Okay, I can understand that most people probably don''t want to dig through all that code.

But could someone at least point me at some code that will show me what I need to see?

I''ve already looked at all of gamedev.net''s resources, and the MSDN Library, but I couldn''t find anything in-depth enough.

I''m really stuck here...help me please!
{[JohnE, Chief Architect and Senior Programmer, Twilight Dragon Media{[+++{GCC/MinGW}+++{Code::Blocks IDE}+++{wxWidgets Cross-Platform Native UI Framework}+++
Advertisement
Peer-to-peer is no different than client-server at the socket layer, except both sides need to set the asynchronous mode on the socket.

Take any client/server sample code and just modify it to make the receiving socket non-blocking.
How simple you make it sound -- and yet that''s what I''ve unsuccessfully tried to do...
{[JohnE, Chief Architect and Senior Programmer, Twilight Dragon Media{[+++{GCC/MinGW}+++{Code::Blocks IDE}+++{wxWidgets Cross-Platform Native UI Framework}+++

This topic is closed to new replies.

Advertisement