gnugo-devel
[Top][All Lists]
Advanced

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

[gnugo-devel] TCP/IP connection built into GNU Go?


From: Paul Pogonyshev
Subject: [gnugo-devel] TCP/IP connection built into GNU Go?
Date: Fri, 8 Oct 2004 23:14:04 -0200
User-agent: KMail/1.4.3

I tried to build TCP/IP connection via sockets into GNU Go.
The patch below is not ready yet.  I'd like for someone to
look into it and maybe spot a problem I cannot track down.

The easiest way to test it is to run GNU Go like

        $ ./interface/gnugo --mode gtp --gtp-listen 127.0.0.1:5000

and then

        $ telnet 127.0.0.1 5000

to receieve a "remote" GNU Go terminal.  The problem is that
when you type `quit' and then try to make GNU Go listen on
the same port again, it won't work.  The system says the port
is still in use.  It gets released in about a minute (on my
machine.)  Clearly, something is not being closed, but I
don't understand what it is.  I doubt it could be a problem
in `telnet'.

Also, `--gtp-connect' is not tested since I couldn't come up
with an easy way to test.  The patch is completely non-
portable too.

Paul


Index: interface/gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/gtp.c,v
retrieving revision 1.19
diff -u -p -r1.19 gtp.c
--- interface/gtp.c     25 May 2004 03:13:47 -0000      1.19
+++ interface/gtp.c     8 Oct 2004 20:01:24 -0000
@@ -69,10 +69,17 @@ static gtp_transform_ptr vertex_transfor
  */
 static int current_id;
 
+/* The file all GTP output goes to.  This is made global for the user
+ * of this file may want to use functions other than gtp_printf() etc.
+ * Set by gtp_main_loop().
+ */
+FILE *gtp_output_file = NULL;
+
+
 /* Read filehandle gtp_input linewise and interpret as GTP commands. */
 void
-gtp_main_loop(struct gtp_command commands[], FILE *gtp_input,
-             FILE *gtp_dump_commands)
+gtp_main_loop(struct gtp_command commands[],
+             FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands)
 {
   char line[GTP_BUFSIZE];
   char command[GTP_BUFSIZE];
@@ -80,7 +87,9 @@ gtp_main_loop(struct gtp_command command
   int i;
   int n;
   int status = GTP_OK;
-  
+
+  gtp_output_file = gtp_output;
+
   while (status == GTP_OK) {
     /* Read a line from gtp_input. */
     if (!fgets(line, GTP_BUFSIZE, gtp_input))
@@ -180,25 +189,25 @@ gtp_mprintf(const char *fmt, ...)
       {
        /* rules of promotion => passed as int, not char */
        int c = va_arg(ap, int);
-       putc(c, stdout);
+       putc(c, gtp_output_file);
        break;
       }
       case 'd':
       {
        int d = va_arg(ap, int);
-       fprintf(stdout, "%d", d);
+       fprintf(gtp_output_file, "%d", d);
        break;
       }
       case 'f':
       {
        double f = va_arg(ap, double); /* passed as double, not float */
-       fprintf(stdout, "%f", f);
+       fprintf(gtp_output_file, "%f", f);
        break;
       }
       case 's':
       {
        char *s = va_arg(ap, char *);
-       fputs(s, stdout);
+       fputs(s, gtp_output_file);
        break;
       }
       case 'm':
@@ -212,20 +221,21 @@ gtp_mprintf(const char *fmt, ...)
       {
        int color = va_arg(ap, int);
        if (color == WHITE)
-         fputs("white", stdout);
+         fputs("white", gtp_output_file);
        else if (color == BLACK)
-         fputs("black", stdout);
+         fputs("black", gtp_output_file);
        else
-         fputs("empty", stdout);
+         fputs("empty", gtp_output_file);
        break;
       }
       default:
-       fprintf(stdout, "\n\nUnknown format character '%c'\n", *fmt);
+       /* FIXME: Should go to `stderr' instead? */
+       fprintf(gtp_output_file, "\n\nUnknown format character '%c'\n", *fmt);
        break;
       }
     }
     else
-      putc(*fmt, stdout);
+      putc(*fmt, gtp_output_file);
   }
   va_end(ap);
 }
@@ -237,7 +247,7 @@ gtp_printf(const char *format, ...)
 {
   va_list ap;
   va_start(ap, format);
-  vfprintf(stdout, format, ap);
+  vfprintf(gtp_output_file, format, ap);
   va_end(ap);
 }
 
@@ -278,7 +288,7 @@ gtp_success(const char *format, ...)
   va_list ap;
   gtp_start_response(GTP_SUCCESS);
   va_start(ap, format);
-  vfprintf(stdout, format, ap);
+  vfprintf(gtp_output_file, format, ap);
   va_end(ap);
   return gtp_finish_response();
 }
@@ -291,7 +301,7 @@ gtp_failure(const char *format, ...)
   va_list ap;
   gtp_start_response(GTP_FAILURE);
   va_start(ap, format);
-  vfprintf(stdout, format, ap);
+  vfprintf(gtp_output_file, format, ap);
   va_end(ap);
   return gtp_finish_response();
 }
Index: interface/gtp.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/gtp.h,v
retrieving revision 1.15
diff -u -p -r1.15 gtp.h
--- interface/gtp.h     24 Jan 2004 04:04:56 -0000      1.15
+++ interface/gtp.h     8 Oct 2004 20:01:24 -0000
@@ -65,8 +65,8 @@ struct gtp_command {
   gtp_fn_ptr function;
 };
 
-void gtp_main_loop(struct gtp_command commands[], FILE *gtp_input,
-                  FILE *gtp_dump_commands);
+void gtp_main_loop(struct gtp_command commands[],
+                  FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands);
 void gtp_internal_set_boardsize(int size);
 void gtp_set_vertex_transform_hooks(gtp_transform_ptr in,
                                    gtp_transform_ptr out);
@@ -83,6 +83,8 @@ int gtp_decode_move(char *s, int *color,
 void gtp_print_vertices(int n, int movei[], int movej[]);
 void gtp_print_vertex(int i, int j);
 
+extern FILE *gtp_output_file;
+
 /*
  * Local Variables:
  * tab-width: 8
Index: interface/interface.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/interface.h,v
retrieving revision 1.15
diff -u -p -r1.15 interface.h
--- interface/interface.h       24 Jan 2004 04:04:56 -0000      1.15
+++ interface/interface.h       8 Oct 2004 20:01:24 -0000
@@ -35,7 +35,7 @@ void play_ascii(SGFTree *tree, Gameinfo 
                char *filename, char *until);
 void play_ascii_emacs(SGFTree *tree, Gameinfo *gameinfo, 
                      char *filename, char *until);
-void play_gtp(FILE *gtp_input, FILE *gtp_dump_commands,
+void play_gtp(FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands,
              int gtp_initial_orientation);
 void play_gmp(Gameinfo *gameinfo, int simplified);
 void play_solo(Gameinfo *gameinfo, int benchmark);
Index: interface/main.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/main.c,v
retrieving revision 1.103
diff -u -p -r1.103 main.c
--- interface/main.c    8 Sep 2004 17:03:42 -0000       1.103
+++ interface/main.c    8 Oct 2004 20:01:28 -0000
@@ -34,6 +34,10 @@
 #include <io.h>
 #endif
 
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #if TIME_WITH_SYS_TIME
 # include <sys/time.h>
 # include <time.h>
@@ -74,6 +78,8 @@ enum {OPT_BOARDSIZE = 127,
       OPT_OUTFILE, 
       OPT_QUIET,
       OPT_GTP_INPUT,
+      OPT_GTP_CONNECT,
+      OPT_GTP_LISTEN,
       OPT_GTP_DUMP_COMMANDS,
       OPT_GTP_INITIAL_ORIENTATION,
       OPT_GTP_VERSION,
@@ -189,6 +195,8 @@ static struct gg_option const long_optio
   {"quiet",          no_argument,       0, OPT_QUIET},
   {"silent",         no_argument,       0, OPT_QUIET},
   {"gtp-input",      required_argument, 0, OPT_GTP_INPUT},
+  {"gtp-connect",    required_argument, 0, OPT_GTP_CONNECT},
+  {"gtp-listen",     required_argument, 0, OPT_GTP_LISTEN},
   {"gtp-dump-commands", required_argument, 0, OPT_GTP_DUMP_COMMANDS},
   {"orientation",    required_argument, 0, OPT_GTP_INITIAL_ORIENTATION},
   {"gtp-initial-orientation",
@@ -311,6 +319,8 @@ main(int argc, char *argv[])
   char *outflags = NULL;
   char *gtpfile = NULL;
   char *gtp_dump_commands_file = NULL;
+  int gtp_tcp_ip_mode = 0;
+  char *gtp_tcp_ip_address = NULL;
   
   char *printsgffile = NULL;
   
@@ -319,8 +329,6 @@ main(int argc, char *argv[])
   char debuginfluence_move[4] = "\0";
   
   int benchmark = 0;  /* benchmarking mode (-b) */
-  FILE *gtp_input_FILE;
-  FILE *gtp_dump_commands_FILE = NULL;
   FILE *output_check;
   int orientation = 0;
 
@@ -443,11 +451,25 @@ main(int argc, char *argv[])
       case OPT_QUIET:
        quiet = 1;
        break;
-       
+
       case OPT_GTP_INPUT:
-       gtpfile = gg_optarg;
+      case OPT_GTP_CONNECT:
+      case OPT_GTP_LISTEN:
+       if (gtp_tcp_ip_mode != 0 || gtpfile != NULL) {
+         fprintf(stderr, ("Options `--gtp-input', `--gtp-connect' and 
`--gtp-listen' "
+                          "are mutually-exclusive\n"));
+         exit(EXIT_FAILURE);
+       }
+
+       if (i == OPT_GTP_INPUT)
+         gtpfile = gg_optarg;
+       else {
+         gtp_tcp_ip_mode = i;
+         gtp_tcp_ip_address = gg_optarg;
+       }
+
        break;
-       
+
       case OPT_GTP_DUMP_COMMANDS:
        gtp_dump_commands_file = gg_optarg;
        break;
@@ -1340,29 +1362,104 @@ main(int argc, char *argv[])
     }
 #endif
 
-  case MODE_GTP:  
-    if (gtpfile != NULL) {
-      gtp_input_FILE = fopen(gtpfile, "r");
-      if (gtp_input_FILE == NULL) {
-       fprintf(stderr, "gnugo: Cannot open file %s\n", gtpfile);
-       return EXIT_FAILURE;
-      } 
-    }
-    else
-      gtp_input_FILE = stdin;
+  case MODE_GTP:
+    {
+      FILE *gtp_input_FILE = stdin;
+      FILE *gtp_output_FILE = stdout;
+      FILE *gtp_dump_commands_FILE = NULL;
+
+      if (gtpfile != NULL) {
+       gtp_input_FILE = fopen(gtpfile, "r");
+       if (gtp_input_FILE == NULL) {
+         fprintf(stderr, "gnugo: Cannot open file %s\n", gtpfile);
+         return EXIT_FAILURE;
+       }
+      }
 
-    if (gtp_dump_commands_file != NULL) {
-      gtp_dump_commands_FILE = fopen(gtp_dump_commands_file, "w");
-      if (gtp_dump_commands_FILE == NULL) {
-       fprintf(stderr, "gnugo: Cannot open file %s\n",
-               gtp_dump_commands_file);
-       return EXIT_FAILURE;
-      } 
+      if (gtp_tcp_ip_mode != 0) {
+       struct sockaddr_in address;
+       char *port_string = strchr(gtp_tcp_ip_address, ':');
+       int port = -1;
+       int main_socket;
+
+       if (port_string) {
+         *port_string++ = 0;
+
+         if (!inet_aton(gtp_tcp_ip_address, &address.sin_addr)) {
+           fprintf(stderr, "Cannot parse IP address\n");
+           exit(EXIT_FAILURE);
+         }
+
+         sscanf(port_string, "%u", &port);
+       }
+
+       if (port < 0 || 65535 < port) {
+         fprintf(stderr, "A valid TCP/IP port number expected\n");
+         exit(EXIT_FAILURE);
+       }
+
+       address.sin_family = AF_INET;
+       address.sin_port = htons(port);
+       /* address.sin_addr is set by inet_aton() above. */
+
+       main_socket = socket(PF_INET, SOCK_STREAM, 0);
+       if (main_socket == -1) {
+         fprintf(stderr, "Unable to open a socket to %s:%d\n",
+                 gtp_tcp_ip_address, port);
+         exit(EXIT_FAILURE);
+       }
+
+       if (gtp_tcp_ip_mode == OPT_GTP_CONNECT) {
+         if (connect(main_socket, (struct sockaddr *) &address,
+                     sizeof address) == -1) {
+           fprintf(stderr, "Failed to connect to %s:%d\n",
+                   gtp_tcp_ip_address, port);
+           close(main_socket);
+           exit(EXIT_FAILURE);
+         }
+       }
+       else {
+         /* gtp_tcp_ip_mode == OPT_GTP_LISTEN */
+
+         int connection_socket;
+         if (bind(main_socket, (struct sockaddr *) &address,
+                  sizeof address) == -1
+             || listen(main_socket, 0) == -1
+             || (connection_socket = accept(main_socket, NULL, NULL)) == -1) {
+           close(main_socket);
+           fprintf(stderr, "Failed to receive a connection at %s:%d\n",
+                   gtp_tcp_ip_address, port);
+           exit(EXIT_FAILURE);
+         }
+
+         close(main_socket);
+         main_socket = connection_socket;
+       }
+
+       gtp_input_FILE = fdopen(main_socket, "r");
+       gtp_output_FILE = fdopen(dup(main_socket), "w");
+      }
+
+      if (gtp_dump_commands_file != NULL) {
+       gtp_dump_commands_FILE = fopen(gtp_dump_commands_file, "w");
+       if (gtp_dump_commands_FILE == NULL) {
+         fprintf(stderr, "gnugo: Cannot open file %s\n",
+                 gtp_dump_commands_file);
+         return EXIT_FAILURE;
+       }
+      }
+
+      play_gtp(gtp_input_FILE, gtp_output_FILE, gtp_dump_commands_FILE,
+              orientation);
+
+      if (gtp_input_FILE != stdin)
+       fclose(gtp_input_FILE);
+      if (gtp_output_FILE != stdout)
+       fclose(gtp_output_FILE);
+      if (gtp_dump_commands_FILE)
+       fclose(gtp_dump_commands_FILE);
     }
 
-    play_gtp(gtp_input_FILE, gtp_dump_commands_FILE, orientation);
-    if (gtp_dump_commands_FILE)
-      fclose(gtp_dump_commands_FILE);
     break;
 
   case MODE_ASCII_EMACS:  
Index: interface/play_gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/play_gtp.c,v
retrieving revision 1.153
diff -u -p -r1.153 play_gtp.c
--- interface/play_gtp.c        19 Jul 2004 12:23:09 -0000      1.153
+++ interface/play_gtp.c        8 Oct 2004 20:01:33 -0000
@@ -337,12 +337,15 @@ static struct gtp_command commands[] = {
 
 /* Start playing using the Go Text Protocol. */
 void
-play_gtp(FILE *gtp_input, FILE *gtp_dump_commands, int gtp_initial_orientation)
+play_gtp(FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands,
+        int gtp_initial_orientation)
 {
-  /* Make sure stdout is unbuffered. (Line buffering is also okay but
-   * not necessary. Block buffering breaks GTP mode.)
+  /* Make sure `gtp_output' is unbuffered. (Line buffering is also
+   * okay but not necessary. Block buffering breaks GTP mode.)
+   *
+   * FIXME: Maybe should go to `gtp.c'?
    */
-  setbuf(stdout, NULL);
+  setbuf(gtp_output, NULL);
 
   /* Inform the GTP utility functions about the board size. */
   gtp_internal_set_boardsize(board_size);
@@ -355,7 +358,7 @@ play_gtp(FILE *gtp_input, FILE *gtp_dump
   /* Prepare pattern matcher and reading code. */
   reset_engine();
   clearstats();
-  gtp_main_loop(commands, gtp_input, gtp_dump_commands);
+  gtp_main_loop(commands, gtp_input, gtp_output, gtp_dump_commands);
   if (showstatistics)
     showstats();
 }
@@ -2633,7 +2636,7 @@ gtp_move_reasons(char *s)
     return gtp_failure("vertex must not be occupied");
 
   gtp_start_response(GTP_SUCCESS);
-  if (list_move_reasons(stdout, POS(i, j)) == 0)
+  if (list_move_reasons(gtp_output_file, POS(i, j)) == 0)
     gtp_printf("\n");
   gtp_printf("\n");
   return GTP_OK;
@@ -3562,7 +3565,7 @@ gtp_showboard(char *s)
   
   gtp_start_response(GTP_SUCCESS);
   gtp_printf("\n");
-  simple_showboard(stdout);
+  simple_showboard(gtp_output_file);
   return gtp_finish_response();
 }
 
@@ -4015,7 +4018,7 @@ gtp_dragon_data(char *s)
                && dragon[POS(m, n)].origin == POS(m, n))) {
          gtp_print_vertex(m, n);
          gtp_printf(":\n");
-         report_dragon(stdout, POS(m, n));
+         report_dragon(gtp_output_file, POS(m, n));
        }
   }
   gtp_printf("\n");
@@ -4281,7 +4284,7 @@ static int
 gtp_echo_err(char *s)
 {
   fprintf(stderr, "%s", s);
-  fflush(stdout);
+  fflush(gtp_output_file);
   fflush(stderr);
   return gtp_success("%s", s);
 }





reply via email to

[Prev in Thread] Current Thread [Next in Thread]