1 | #include <gio/gio.h> |
2 | #include <string.h> |
3 | #include <stdio.h> |
4 | |
5 | GMainLoop *loop; |
6 | |
7 | int cancel_timeout = 0; |
8 | int io_timeout = 0; |
9 | gboolean async = FALSE; |
10 | gboolean graceful = FALSE; |
11 | gboolean verbose = FALSE; |
12 | static GOptionEntry cmd_entries[] = { |
13 | {"cancel" , 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, |
14 | "Cancel any op after the specified amount of seconds" , NULL}, |
15 | {"async" , 'a', 0, G_OPTION_ARG_NONE, &async, |
16 | "Use async ops" , NULL}, |
17 | {"graceful-disconnect" , 'g', 0, G_OPTION_ARG_NONE, &graceful, |
18 | "Use graceful disconnect" , NULL}, |
19 | {"timeout" , 't', 0, G_OPTION_ARG_INT, &io_timeout, |
20 | "Time out socket I/O after the specified number of seconds" , NULL}, |
21 | {"verbose" , 'v', 0, G_OPTION_ARG_NONE, &verbose, |
22 | "Verbose debugging output" , NULL}, |
23 | {NULL} |
24 | }; |
25 | |
26 | static gpointer |
27 | cancel_thread (gpointer data) |
28 | { |
29 | GCancellable *cancellable = data; |
30 | |
31 | g_usleep (microseconds: 1000*1000*cancel_timeout); |
32 | g_print (format: "Cancelling\n" ); |
33 | g_cancellable_cancel (cancellable); |
34 | return NULL; |
35 | } |
36 | |
37 | static char * |
38 | socket_address_to_string (GSocketAddress *address) |
39 | { |
40 | GInetAddress *inet_address; |
41 | char *str, *res; |
42 | int port; |
43 | |
44 | inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); |
45 | str = g_inet_address_to_string (address: inet_address); |
46 | port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); |
47 | res = g_strdup_printf (format: "%s:%d" , str, port); |
48 | g_free (mem: str); |
49 | return res; |
50 | } |
51 | |
52 | static void |
53 | async_cb (GObject *source_object, |
54 | GAsyncResult *res, |
55 | gpointer user_data) |
56 | { |
57 | GAsyncResult **resp = user_data; |
58 | *resp = g_object_ref (res); |
59 | g_main_loop_quit (loop); |
60 | } |
61 | |
62 | static void |
63 | socket_client_event (GSocketClient *client, |
64 | GSocketClientEvent event, |
65 | GSocketConnectable *connectable, |
66 | GSocketConnection *connection) |
67 | { |
68 | static GEnumClass *event_class; |
69 | gint64 now_us; |
70 | |
71 | if (!event_class) |
72 | event_class = g_type_class_ref (type: G_TYPE_SOCKET_CLIENT_EVENT); |
73 | |
74 | now_us = g_get_real_time (); |
75 | g_print (format: "%" G_GINT64_FORMAT " GSocketClient => %s [%s]\n" , |
76 | now_us, |
77 | g_enum_get_value (enum_class: event_class, value: event)->value_nick, |
78 | connection ? G_OBJECT_TYPE_NAME (connection) : "" ); |
79 | } |
80 | |
81 | int |
82 | main (int argc, char *argv[]) |
83 | { |
84 | GOptionContext *context; |
85 | GSocketClient *client; |
86 | GSocketConnection *connection; |
87 | GSocketAddress *address; |
88 | GCancellable *cancellable; |
89 | GOutputStream *out; |
90 | GError *error = NULL; |
91 | char buffer[1000]; |
92 | |
93 | context = g_option_context_new (parameter_string: " <hostname>[:port] - send data to tcp host" ); |
94 | g_option_context_add_main_entries (context, entries: cmd_entries, NULL); |
95 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
96 | { |
97 | g_printerr (format: "%s: %s\n" , argv[0], error->message); |
98 | return 1; |
99 | } |
100 | |
101 | if (argc != 2) |
102 | { |
103 | g_printerr (format: "%s: %s\n" , argv[0], "Need to specify hostname" ); |
104 | return 1; |
105 | } |
106 | |
107 | if (async) |
108 | loop = g_main_loop_new (NULL, FALSE); |
109 | |
110 | if (cancel_timeout) |
111 | { |
112 | GThread *thread; |
113 | cancellable = g_cancellable_new (); |
114 | thread = g_thread_new (name: "cancel" , func: cancel_thread, data: cancellable); |
115 | g_thread_unref (thread); |
116 | } |
117 | else |
118 | { |
119 | cancellable = NULL; |
120 | } |
121 | |
122 | client = g_socket_client_new (); |
123 | if (io_timeout) |
124 | g_socket_client_set_timeout (client, timeout: io_timeout); |
125 | if (verbose) |
126 | g_signal_connect (client, "event" , G_CALLBACK (socket_client_event), NULL); |
127 | |
128 | if (async) |
129 | { |
130 | GAsyncResult *res; |
131 | g_socket_client_connect_to_host_async (client, host_and_port: argv[1], default_port: 7777, |
132 | cancellable, callback: async_cb, user_data: &res); |
133 | g_main_loop_run (loop); |
134 | connection = g_socket_client_connect_to_host_finish (client, result: res, error: &error); |
135 | g_object_unref (object: res); |
136 | } |
137 | else |
138 | { |
139 | connection = g_socket_client_connect_to_host (client, |
140 | host_and_port: argv[1], |
141 | default_port: 7777, |
142 | cancellable, error: &error); |
143 | } |
144 | if (connection == NULL) |
145 | { |
146 | g_printerr (format: "%s can't connect: %s\n" , argv[0], error->message); |
147 | return 1; |
148 | } |
149 | g_object_unref (object: client); |
150 | |
151 | address = g_socket_connection_get_remote_address (connection, error: &error); |
152 | if (!address) |
153 | { |
154 | g_printerr (format: "Error getting remote address: %s\n" , |
155 | error->message); |
156 | return 1; |
157 | } |
158 | g_print (format: "Connected to address: %s\n" , |
159 | socket_address_to_string (address)); |
160 | g_object_unref (object: address); |
161 | |
162 | if (graceful) |
163 | g_tcp_connection_set_graceful_disconnect (G_TCP_CONNECTION (connection), TRUE); |
164 | |
165 | out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); |
166 | |
167 | while (fgets(s: buffer, n: sizeof (buffer), stdin) != NULL) |
168 | { |
169 | /* FIXME if (async) */ |
170 | if (!g_output_stream_write_all (stream: out, buffer, count: strlen (s: buffer), |
171 | NULL, cancellable, error: &error)) |
172 | { |
173 | g_warning ("send error: %s" , error->message); |
174 | g_error_free (error); |
175 | error = NULL; |
176 | } |
177 | } |
178 | |
179 | g_print (format: "closing stream\n" ); |
180 | if (async) |
181 | { |
182 | GAsyncResult *res; |
183 | g_io_stream_close_async (G_IO_STREAM (connection), |
184 | io_priority: 0, cancellable, callback: async_cb, user_data: &res); |
185 | g_main_loop_run (loop); |
186 | if (!g_io_stream_close_finish (G_IO_STREAM (connection), |
187 | result: res, error: &error)) |
188 | { |
189 | g_object_unref (object: res); |
190 | g_warning ("close error: %s" , error->message); |
191 | return 1; |
192 | } |
193 | g_object_unref (object: res); |
194 | } |
195 | else |
196 | { |
197 | if (!g_io_stream_close (G_IO_STREAM (connection), cancellable, error: &error)) |
198 | { |
199 | g_warning ("close error: %s" , error->message); |
200 | return 1; |
201 | } |
202 | } |
203 | |
204 | g_object_unref (object: connection); |
205 | |
206 | return 0; |
207 | } |
208 | |