Windows errata or my fault?



I've been tyring various little programs to get a feel for glib on windows. One of them is attached. The idea is to connect to the echo port on a system, send it hello world from time to time, and get those bytes back.

I have one of those "it works on linux but not here" situations. Under linux (debian 3.1 I think, recent glib from testing) the output looks something like:

raj tardy:~$ ./gplay
Starting loop...
writing 'Hello World' to the channel
Read:
 Hello World
 from the source
Callback called 1th time with 0x804c730
Read:
 Hello World
 from the source
Callback called 2th time with 0x804c730
Read:
 Hello World
 from the source
Callback called 3th time with 0x804c730
Read:
 Hello World
 from the source
Callback called 4th time with 0x804c730
Read:
 Hello World
 from the source
Callback called 5th time with 0x804c730
Read:
 Hello World
 from the source
Callback called 6th time with 0x804c730
Time to quit


Under Windows XP with glib 2.10 it stops after this:

Starting loop...
writing 'Hello World' to the channel
Read:
 Hello World
 from the source
Callback called 1th time with 00354620
Read:
 Hello World
 from the source

so the first hello world went-out to the remote, we got the reply, the timer event happened the first time and wrote hello world again, and it seems we got it back from the remote, but the next timer event didn't seem to happen.

rick jones
#include <glib.h>
#include <sys/types.h>

#ifndef G_OS_WIN32
#include <netdb.h>
#include <sys/socket.h>
#define INVALID_SOCKET -1
#define SOCKET int
#define __cdecl 
#else
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define close(x) closesocket(x)
#endif


/*
  establish_control()

set-up the control connection between netperf and the netserver so we
can actually run some tests. if we cannot establish the control
connection, that may or may not be a good thing, so we will let the
caller decide what to do.

to assist with pesky end-to-end-unfriendly things like firewalls, we
allow the caller to specify both the remote hostname and port, and the
local addressing info.  i believe that in theory it is possible to
have an IPv4 endpoint and an IPv6 endpoint communicate with one
another, but for the time being, we are only going to take-in one
requested address family parameter. this means that the only way
(iirc) that we might get a mixed-mode connection would be if the
address family is specified as AF_UNSPEC, and getaddrinfo() returns
different families for the local and server names.

the "names" can also be IP addresses in ASCII string form.

raj 2003-02-27 */

static SOCKET
establish_control_internal(char *hostname,
			   char *port,
			   int   remfam,
			   char *localhost,
			   char *localport,
			   int   locfam)
{
  int not_connected;
  SOCKET control_sock;
  int count;
  int error;

  struct addrinfo   hints;
  struct addrinfo  *local_res;
  struct addrinfo  *remote_res;
  struct addrinfo  *local_res_temp;
  struct addrinfo  *remote_res_temp;

  /* first, we do the remote */
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = remfam;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  hints.ai_flags = 0|AI_CANONNAME;
  count = 0;
  do {
    error = getaddrinfo((char *)hostname,
                        (char *)port,
                        &hints,
                        &remote_res);
    count += 1;
    if (error == EAI_AGAIN) {
      g_usleep(1000000);
    }
  } while ((error == EAI_AGAIN) && (count <= 5));

  if (error) {
    return(INVALID_SOCKET);
  }

  /* now we do the local */
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = locfam;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  hints.ai_flags = AI_PASSIVE|AI_CANONNAME;
  count = 0;
  do {
    count += 1;
    error = getaddrinfo((char *)localhost,
                           (char *)localport,
                           &hints,
                           &local_res);
    if (error == EAI_AGAIN) {
      g_usleep(1000000);
    }
  } while ((error == EAI_AGAIN) && (count <= 5));

  if (error) {
    return(INVALID_SOCKET);
  }

  not_connected = 1;
  local_res_temp = local_res;
  remote_res_temp = remote_res;
  /* we want to loop through all the possibilities. looping on the
     local addresses will be handled within the while loop.  I suppose
     these is some more "C-expert" way to code this, but it has not
     lept to mind just yet :)  raj 2003-02024 */

  while (remote_res_temp != NULL) {

    /* I am guessing that we should use the address family of the
       local endpoint, and we will not worry about mixed family types
       - presumeably the stack or other transition mechanisms will be
       able to deal with that for us. famous last words :)  raj 2003-02-26 */
    control_sock = socket(local_res_temp->ai_family,
                          SOCK_STREAM,
                          0);
    if (control_sock == INVALID_SOCKET) {
      /* at some point we'll need a more generic "display error"
         message for when/if we use GUIs and the like. unlike a bind
         or connect failure, failure to allocate a socket is
         "immediately fatal" and so we return to the caller. raj 2003-02-24 */
      return(INVALID_SOCKET);
    }

    /* if we are going to control the local enpoint addressing, we
       need to call bind. of course, we should probably be setting one
       of the SO_REUSEmumble socket options? raj 2005-02-04 */
    if (bind(control_sock,
	     local_res_temp->ai_addr,
	     local_res_temp->ai_addrlen) == 0) {

      if (connect(control_sock,
		  remote_res_temp->ai_addr,
		  remote_res_temp->ai_addrlen) == 0) {
	/* we have successfully connected to the remote netserver */
	not_connected = 0;
	/* this should get us out of the while loop */
	break;
      } else {
	/* the connect call failed */
      }
    }
    else {
      /* the bind failed */
    }

    if ((local_res_temp = local_res_temp->ai_next) == NULL) {
      /* wrap the local and move to the next server, don't forget to
         close the current control socket. raj 2003-02-24 */
      local_res_temp = local_res;
      /* the outer while conditions will deal with the case when we
         get to the end of all the possible remote addresses. */
      remote_res_temp = remote_res_temp->ai_next;
      /* it is simplest here to just close the control sock. since
         this is not a performance critical section of code, we
         don't worry about overheads for socket allocation or
         close. raj 2003-02-24 */
    }
    close(control_sock);
  }

  /* we no longer need the addrinfo stuff */
  freeaddrinfo(local_res);
  freeaddrinfo(remote_res);

  /* so, we are either connected or not */
  if (not_connected) {
    return(INVALID_SOCKET);
  }
  /* at this point, we are connected.  we probably want some sort of
     version check with the remote at some point. raj 2003-02-24 */
  return(control_sock);
}

GIOChannel *my_channel;
gchar *hello="Hello World\n";

gboolean  read_something(GIOChannel *source, 
			 GIOCondition condition, 
			 gpointer data)
{
  gchar buf[1024];
  GIOStatus status;
  gsize bytes_read;
  GError *error=NULL;

  while ((status = 
	  g_io_channel_read_chars(source,buf,1023,&bytes_read,&error)) ==
	 G_IO_STATUS_NORMAL) {
    buf[bytes_read] = '\0';
    g_print("Read:\n %s from the source\n",buf);
  }

  if (error) {
    g_warning("g_io_channel_read_chars %s %d %s\n",
	      g_quark_to_string(error->domain),
	      error->code,
	      error->message);
    g_clear_error(&error);
  }
  return(TRUE);
}

static void callback(gpointer data)
{
  gsize bytes_written;
  GError *error=NULL;
  GIOStatus status;

  static int count=0;
  g_print("Callback called %dth time with %p\n",++count,data);
  status = g_io_channel_write_chars(my_channel,
				    hello,
				    strlen(hello),
				    &bytes_written,
				    &error);
  g_print("after write in call back status is %d\n",status);
  if (error) {
    g_warning("g_io_write_channel_chars %s %d %s\n",
	      g_quark_to_string(error->domain),
	      error->code,
	      error->message);
    g_clear_error(&error);
  }
  if (count > 5) {
    g_print("Time to quit\n");
    g_main_loop_quit(data);
  }
}

int __cdecl main(int argc, char *argv[])
{
  SOCKET control;
  guint watch_id;
  GIOStatus status;
  GError *error = NULL;
  gsize bytes_written;

  GMainLoop *loop;

#ifdef G_OS_WIN32
  WSADATA	wsa_data ;

  /* Initialize the winsock lib ( version 2.2 ) */
  if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){
    g_print("WSAStartup() failed : %d\n", GetLastError()) ;
    return 1 ;
  }
#endif
  
  control = establish_control_internal("tardy.cup.hp.com",
				       "echo",
				       AF_UNSPEC,
				       "0.0.0.0",
				       "0",
				       AF_UNSPEC);

  loop = g_main_loop_new(NULL, FALSE);
  g_print("Starting loop...\n");

#ifndef G_OS_WIN32
  my_channel = g_io_channel_unix_new(control);
#else
  my_channel = g_io_channel_win32_new_socket(control);
#endif
  status = g_io_channel_set_flags(my_channel,G_IO_FLAG_NONBLOCK,&error);
  if (error) {
    g_warning("g_io_channel_set_flags %s %d %s\n",
	      g_quark_to_string(error->domain),
	      error->code,
	      error->message);
    g_clear_error(&error);
  }

  status = g_io_channel_set_encoding(my_channel,NULL,&error);
  if (error) {
    g_warning("g_io_channel_set_encoding %s %d %s\n",
	      g_quark_to_string(error->domain),
	      error->code,
	      error->message);
    g_clear_error(&error);
  }

  g_io_channel_set_buffered(my_channel,FALSE);

  /* loop is passed just to have something passed */
  watch_id = g_io_add_watch(my_channel, G_IO_IN, read_something, loop);

  status = g_io_channel_write_chars(my_channel,
				    hello,
				    strlen(hello),
				    &bytes_written,
				    &error);
  if (error) {
    g_warning("g_io_write_channel_chars %s %d %s\n",
	      g_quark_to_string(error->domain),
	      error->code,
	      error->message);
    g_clear_error(&error);
  }

  g_print("writing 'Hello World' to the channel\n");

  g_timeout_add(1000, (GSourceFunc)callback, loop);

  g_main_loop_run(loop);
  
  return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]