| #include <gio/gio.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| GMainLoop *loop; | |
| int cancel_timeout = 0; | |
| int io_timeout = 0; | |
| gboolean async = FALSE; | |
| gboolean graceful = FALSE; | |
| gboolean verbose = FALSE; | |
| static GOptionEntry cmd_entries[] = { | |
| {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, | |
| "Cancel any op after the specified amount of seconds", NULL}, | |
| {"async", 'a', 0, G_OPTION_ARG_NONE, &async, | |
| "Use async ops", NULL}, | |
| {"graceful-disconnect", 'g', 0, G_OPTION_ARG_NONE, &graceful, | |
| "Use graceful disconnect", NULL}, | |
| {"timeout", 't', 0, G_OPTION_ARG_INT, &io_timeout, | |
| "Time out socket I/O after the specified number of seconds", NULL}, | |
| {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, | |
| "Verbose debugging output", NULL}, | |
| G_OPTION_ENTRY_NULL | |
| }; | |
| static gpointer | |
| cancel_thread (gpointer data) | |
| { | |
| GCancellable *cancellable = data; | |
| g_usleep (1000*1000*cancel_timeout); | |
| g_print ("Cancelling\n"); | |
| g_cancellable_cancel (cancellable); | |
| return NULL; | |
| } | |
| static char * | |
| socket_address_to_string (GSocketAddress *address) | |
| { | |
| GInetAddress *inet_address; | |
| char *str, *res; | |
| int port; | |
| inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); | |
| str = g_inet_address_to_string (inet_address); | |
| port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); | |
| res = g_strdup_printf ("%s:%d", str, port); | |
| g_free (str); | |
| return res; | |
| } | |
| static void | |
| async_cb (GObject *source_object, | |
| GAsyncResult *res, | |
| gpointer user_data) | |
| { | |
| GAsyncResult **resp = user_data; | |
| *resp = g_object_ref (res); | |
| g_main_loop_quit (loop); | |
| } | |
| static void | |
| socket_client_event (GSocketClient *client, | |
| GSocketClientEvent event, | |
| GSocketConnectable *connectable, | |
| GSocketConnection *connection) | |
| { | |
| static GEnumClass *event_class; | |
| gint64 now_us; | |
| if (!event_class) | |
| event_class = g_type_class_ref (G_TYPE_SOCKET_CLIENT_EVENT); | |
| now_us = g_get_real_time (); | |
| g_print ("%" G_GINT64_FORMAT " GSocketClient => %s [%s]\n", | |
| now_us, | |
| g_enum_get_value (event_class, event)->value_nick, | |
| connection ? G_OBJECT_TYPE_NAME (connection) : ""); | |
| } | |
| int | |
| main (int argc, char *argv[]) | |
| { | |
| GOptionContext *context; | |
| GSocketClient *client; | |
| GSocketConnection *connection; | |
| GSocketAddress *address; | |
| GCancellable *cancellable; | |
| GOutputStream *out; | |
| GError *error = NULL; | |
| char buffer[1000]; | |
| context = g_option_context_new (" <hostname>[:port] - send data to tcp host"); | |
| g_option_context_add_main_entries (context, cmd_entries, NULL); | |
| if (!g_option_context_parse (context, &argc, &argv, &error)) | |
| { | |
| g_printerr ("%s: %s\n", argv[0], error->message); | |
| return 1; | |
| } | |
| if (argc != 2) | |
| { | |
| g_printerr ("%s: %s\n", argv[0], "Need to specify hostname"); | |
| return 1; | |
| } | |
| if (async) | |
| loop = g_main_loop_new (NULL, FALSE); | |
| if (cancel_timeout) | |
| { | |
| GThread *thread; | |
| cancellable = g_cancellable_new (); | |
| thread = g_thread_new ("cancel", cancel_thread, cancellable); | |
| g_thread_unref (thread); | |
| } | |
| else | |
| { | |
| cancellable = NULL; | |
| } | |
| client = g_socket_client_new (); | |
| if (io_timeout) | |
| g_socket_client_set_timeout (client, io_timeout); | |
| if (verbose) | |
| g_signal_connect (client, "event", G_CALLBACK (socket_client_event), NULL); | |
| if (async) | |
| { | |
| GAsyncResult *res; | |
| g_socket_client_connect_to_host_async (client, argv[1], 7777, | |
| cancellable, async_cb, &res); | |
| g_main_loop_run (loop); | |
| connection = g_socket_client_connect_to_host_finish (client, res, &error); | |
| g_object_unref (res); | |
| } | |
| else | |
| { | |
| connection = g_socket_client_connect_to_host (client, | |
| argv[1], | |
| 7777, | |
| cancellable, &error); | |
| } | |
| if (connection == NULL) | |
| { | |
| g_printerr ("%s can't connect: %s\n", argv[0], error->message); | |
| return 1; | |
| } | |
| g_object_unref (client); | |
| address = g_socket_connection_get_remote_address (connection, &error); | |
| if (!address) | |
| { | |
| g_printerr ("Error getting remote address: %s\n", | |
| error->message); | |
| return 1; | |
| } | |
| g_print ("Connected to address: %s\n", | |
| socket_address_to_string (address)); | |
| g_object_unref (address); | |
| if (graceful) | |
| g_tcp_connection_set_graceful_disconnect (G_TCP_CONNECTION (connection), TRUE); | |
| out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); | |
| while (fgets(buffer, sizeof (buffer), stdin) != NULL) | |
| { | |
| /* FIXME if (async) */ | |
| if (!g_output_stream_write_all (out, buffer, strlen (buffer), | |
| NULL, cancellable, &error)) | |
| { | |
| g_warning ("send error: %s", error->message); | |
| g_error_free (error); | |
| error = NULL; | |
| } | |
| } | |
| g_print ("closing stream\n"); | |
| if (async) | |
| { | |
| GAsyncResult *res; | |
| g_io_stream_close_async (G_IO_STREAM (connection), | |
| 0, cancellable, async_cb, &res); | |
| g_main_loop_run (loop); | |
| if (!g_io_stream_close_finish (G_IO_STREAM (connection), | |
| res, &error)) | |
| { | |
| g_object_unref (res); | |
| g_warning ("close error: %s", error->message); | |
| return 1; | |
| } | |
| g_object_unref (res); | |
| } | |
| else | |
| { | |
| if (!g_io_stream_close (G_IO_STREAM (connection), cancellable, &error)) | |
| { | |
| g_warning ("close error: %s", error->message); | |
| return 1; | |
| } | |
| } | |
| g_object_unref (connection); | |
| return 0; | |
| } |