| #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; |
| } |