From ed3f143edde18f4fc3781293c50e25ca843320db Mon Sep 17 00:00:00 2001 From: Martin Peach Date: Wed, 4 Mar 2009 22:33:05 +0000 Subject: Changed send routine to send one byte at a time and output number of bytes sent, so it won't block if the other end disappears. Also settable buffer size. Help path updated. svn path=/trunk/externals/mrpeach/; revision=10835 --- net/tcpclient-help.pd | 52 ++++++++----- net/tcpclient.c | 200 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 161 insertions(+), 91 deletions(-) diff --git a/net/tcpclient-help.pd b/net/tcpclient-help.pd index 53c8523..360bb04 100644 --- a/net/tcpclient-help.pd +++ b/net/tcpclient-help.pd @@ -1,15 +1,15 @@ -#N canvas 162 3 1096 608 12; +#N canvas 349 7 1096 608 12; #X msg -112 56 disconnect; -#X obj 160 370 unpack 0 0 0 0; -#X floatatom 160 393 3 0 0 0 - - -; -#X floatatom 191 393 3 0 0 0 - - -; -#X floatatom 223 393 3 0 0 0 - - -; -#X floatatom 255 393 3 0 0 0 - - -; -#X text 120 392 from; +#X obj 186 406 unpack 0 0 0 0; +#X floatatom 186 429 3 0 0 0 - - -; +#X floatatom 217 429 3 0 0 0 - - -; +#X floatatom 249 429 3 0 0 0 - - -; +#X floatatom 281 429 3 0 0 0 - - -; +#X text 146 428 from; #X msg -175 -7 connect 132.205.142.12 80; -#X obj 130 318 tcpclient; -#X obj 190 344 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291 --1 0 1; +#X obj 166 354 tcpclient; +#X obj 206 380 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291 +-1 1 1; #X msg -88 80 dump \$1; #X obj -140 65 tgl 15 0 empty empty empty 0 -6 0 8 -4034 -257985 -1 1 1; @@ -19,7 +19,7 @@ #X msg -200 -32 connect www.concordia.ca 80; #X text -17 79 print received messages to main window in hexdump format ; -#X text 201 317 tcpclient opens a tcp socket to send and receive bytes +#X text 237 353 tcpclient opens a tcp socket to send and receive bytes on; #X text -217 305 See also:; #X obj -212 329 netclient; @@ -27,8 +27,8 @@ on; #X obj -212 352 tcpreceive; #X text -214 374 can receive messages from tcpclient; #X text -136 328 is what tcpclient is based on; -#X text 181 444 Received messages are output as a list of bytes; -#X text 202 476 Attempting to print long messages output can hang pd! +#X text 217 480 Received messages are output as a list of bytes; +#X text 238 509 Attempting to print long messages output can hang pd! ; #X text 77 176 get any received data (not useful unless you need it faster than once per 20ms); @@ -50,14 +50,23 @@ be transmitted or received.; #X msg 99 251 71 69 84 32 104 116 116 112 58 47 47 47 105 110 100 101 120 46 104 116 109 108 13 10; #X text 529 257 'send' prefix is optional; -#X obj 130 445 spigot; -#X obj 169 422 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 --1 1 1; -#X obj 130 474 print >>>; -#X text -213 475 2009/02/24 Martin Peach; +#X obj 166 481 spigot; +#X obj 205 458 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 +-1 0 1; +#X obj 166 510 print >>>; #X msg -136 16 send 71 69 84 32 104 116 116 112 58 47 47 47 105 110 100 101 120 46 112 104 112 13 10; #X text 272 24 GET http:///index.phpCRLF; +#X floatatom 341 400 9 0 0 0 - - -; +#X text 197 291 set send-buffer size; +#X obj 341 374 route sent buf; +#X floatatom 388 424 9 0 0 0 - - -; +#X text 458 423 Size of the send buffer; +#X text 415 399 Number of bytes sent (may still be in buffer); +#X msg 147 315 buf; +#X text 177 314 get send-buffer size; +#X text -213 475 2009/03/04 Martin Peach; +#X msg 123 291 buf 10; #X connect 0 0 8 0; #X connect 1 0 2 0; #X connect 1 1 3 0; @@ -67,6 +76,7 @@ be transmitted or received.; #X connect 8 0 40 0; #X connect 8 1 1 0; #X connect 8 2 9 0; +#X connect 8 3 47 0; #X connect 10 0 8 0; #X connect 11 0 10 0; #X connect 12 0 8 0; @@ -81,4 +91,8 @@ be transmitted or received.; #X connect 38 0 8 0; #X connect 40 0 42 0; #X connect 41 0 40 1; -#X connect 44 0 8 0; +#X connect 43 0 8 0; +#X connect 47 0 45 0; +#X connect 47 1 48 0; +#X connect 51 0 8 0; +#X connect 54 0 8 0; diff --git a/net/tcpclient.c b/net/tcpclient.c index d183e90..727d181 100644 --- a/net/tcpclient.c +++ b/net/tcpclient.c @@ -63,9 +63,11 @@ typedef struct _tcpclient t_clock *x_poll; t_outlet *x_msgout; t_outlet *x_addrout; - t_outlet *x_outconnect; + t_outlet *x_connectout; + t_outlet *x_statusout; int x_dump; // 1 = hexdump received bytes int x_fd; // the socket + int x_fdbuf; // the socket's buffer size char *x_hostname; // address we want to connect to as text int x_connectstate; // 0 = not connected, 1 = connected int x_port; // port we're connected to @@ -85,6 +87,10 @@ static void *tcpclient_child_connect(void *w); static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno); static void tcpclient_disconnect(t_tcpclient *x); static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv); +int tcpclient_send_byte(t_tcpclient *x, char byte); +static int tcpclient_get_socket_send_buf_size(t_tcpclient *x); +static int tcpclient_set_socket_send_buf_size(t_tcpclient *x, int size); +static void tcpclient_buf_size(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv); static void tcpclient_rcv(t_tcpclient *x); static void tcpclient_poll(t_tcpclient *x); static void *tcpclient_new(t_floatarg udpflag); @@ -126,7 +132,7 @@ static void tcp_client_hexdump(unsigned char *buf, long len) static void tcpclient_tick(t_tcpclient *x) { - outlet_float(x->x_outconnect, 1); + outlet_float(x->x_connectout, 1); } static void *tcpclient_child_connect(void *w) @@ -202,7 +208,7 @@ static void tcpclient_disconnect(t_tcpclient *x) sys_closesocket(x->x_fd); x->x_fd = -1; x->x_connectstate = 0; - outlet_float(x->x_outconnect, 0); + outlet_float(x->x_connectout, 0); post("%s: disconnected", objName); } else post("%s: not connected", objName); @@ -210,27 +216,29 @@ static void tcpclient_disconnect(t_tcpclient *x) static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) { -#define BYTE_BUF_LEN 65536 // arbitrary maximum similar to max IP packet size - static char byte_buf[BYTE_BUF_LEN]; - int i, j, d; - unsigned char c; - float f, e; - char *bp; - int length, sent; - int result; - static double lastwarntime; - static double pleasewarn; - double timebefore; - double timeafter; - int late; - char fpath[FILENAME_MAX]; - FILE *fptr; +//#define BYTE_BUF_LEN 65536 // arbitrary maximum similar to max IP packet size +// static char byte_buf[BYTE_BUF_LEN]; + int i, j, d; + unsigned char c; + float f, e; + char *bp; + int length; + size_t sent; + int result; + char fpath[FILENAME_MAX]; + FILE *fptr; + t_atom output_atom; #ifdef DEBUG post("s: %s", s->s_name); post("argc: %d", argc); #endif + if (x->x_fd < 0) + { + error("%s: not connected", objName); + return; + } for (i = j = 0; i < argc; ++i) { if (argv[i].a_type == A_FLOAT) @@ -238,9 +246,6 @@ static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) f = argv[i].a_w.w_float; d = (int)f; e = f - d; -#ifdef DEBUG - post("%s: argv[%d]: float:%f int:%d delta:%f", objName, i, f, d, e); -#endif if (e != 0) { error("%s_send: item %d (%f) is not an integer", objName, i, f); @@ -252,18 +257,13 @@ static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) return; } c = (unsigned char)d; -#ifdef DEBUG - post("%s_send: argv[%d]: %d", objName, i, c); -#endif - byte_buf[j++] = c; + if (0 == tcpclient_send_byte(x, c)) break; + ++j; } else if (argv[i].a_type == A_SYMBOL) { atom_string(&argv[i], fpath, FILENAME_MAX); -#ifdef DEBUG - post ("%s_send fname: %s", objName, fpath); -#endif fptr = fopen(fpath, "rb"); if (fptr == NULL) { @@ -271,66 +271,120 @@ static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) return; } rewind(fptr); -#ifdef DEBUG - post("%s_send: d is %d", objName, d); -#endif while ((d = fgetc(fptr)) != EOF) { - byte_buf[j++] = (char)(d & 0x0FF); -#ifdef DEBUG - post("%s_send: byte_buf[%d] = %d", objName, j-1, byte_buf[j-1]); -#endif - if (j >= BYTE_BUF_LEN) - { - post ("%s_send: file too long, truncating at %lu", objName, BYTE_BUF_LEN); - break; - } + c = (char)(d & 0x0FF); + if (0 == tcpclient_send_byte(x, c)) break; + ++j; } fclose(fptr); fptr = NULL; post("%s_send: read \"%s\" length %d byte%s", objName, fpath, j, ((d==1)?"":"s")); } else - { + { error("%s_send: item %d is not a float or a file name", objName, i); return; } } + sent = j; + SETFLOAT(&output_atom, sent); + outlet_anything( x->x_statusout, gensym("sent"), 1, &output_atom); +} - length = j; - if ((x->x_fd >= 0) && (length > 0)) +int tcpclient_send_byte(t_tcpclient *x, char byte) +{ + int result = 0; + fd_set wfds; + struct timeval timeout; + + FD_ZERO(&wfds); + FD_SET(x->x_fd, &wfds); + timeout.tv_sec = 0; /* give it no time to clear buffer */ + timeout.tv_usec = 0; + result = select(x->x_fd+1, NULL, &wfds, NULL, &timeout); + if (result == -1) { - for (bp = byte_buf, sent = 0; sent < length;) + post("%s_send_byte: select returned error %d", objName, errno); + return 0; + } + if (FD_ISSET(x->x_fd, &wfds)) + { + result = send(x->x_fd, &byte, 1, 0); + if (result <= 0) { - timebefore = sys_getrealtime(); - result = send(x->x_fd, byte_buf, length-sent, 0); - timeafter = sys_getrealtime(); - late = (timeafter - timebefore > 0.005); - if (late || pleasewarn) - { - if (timeafter > lastwarntime + 2) - { - post("%s_send blocked %d msec", objName, - (int)(1000 * ((timeafter - timebefore) + pleasewarn))); - pleasewarn = 0; - lastwarntime = timeafter; - } - else if (late) pleasewarn += timeafter - timebefore; - } - if (result <= 0) - { - sys_sockerror("tcpclient_send"); - tcpclient_disconnect(x); - break; - } - else - { - sent += result; - bp += result; - } + sys_sockerror("tcpclient: send"); + post("%s_send_byte: could not send data ", objName); + return 0; + } + } + return result; +} + +/* Return the send buffer size of socket, also output it on status outlet */ +static int tcpclient_get_socket_send_buf_size(t_tcpclient *x) +{ + int optVal = 0; + int optLen = sizeof(int); + t_atom output_atom; +#ifdef MSW + if (getsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == SOCKET_ERROR) + post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, WSAGetLastError()); +#else + if (getsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == -1) + post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, errno); +#endif + SETFLOAT(&output_atom, optVal); + outlet_anything( x->x_statusout, gensym("buf"), 1, &output_atom); + return optVal; +} + +/* Set the send buffer size of socket, returns actual size */ +static int tcpclient_set_socket_send_buf_size(t_tcpclient *x, int size) +{ + int optVal = size; + int optLen = sizeof(int); +#ifdef MSW + if (setsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == SOCKET_ERROR) + { + post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, WSAGetLastError()); +#else + if (setsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == -1) + { + post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, errno); +#endif + return 0; + } + else return (tcpclient_get_socket_send_buf_size(x)); +} + +/* Get/set the send buffer size of client socket */ +static void tcpclient_buf_size(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) +{ + int client = -1; + float buf_size = 0; + t_atom output_atom[3]; + + if(x->x_connectstate == 0) + { + post("%s_buf_size: no clients connected", objName); + return; + } + /* get size of buffer (first element in list) */ + if (argc > 0) + { + if (argv[0].a_type != A_FLOAT) + { + post("%s_buf_size: specify buffer size with a float", objName); + return; } + buf_size = atom_getfloatarg(0, argc, argv); + x->x_fdbuf = tcpclient_set_socket_send_buf_size(x, (int)buf_size); + post("%s_buf_size: set to %d", objName, x->x_fdbuf); + return; } - else error("%s: not connected", objName); + x->x_fdbuf = tcpclient_get_socket_send_buf_size(x); + return; } static void tcpclient_rcv(t_tcpclient *x) @@ -418,7 +472,8 @@ static void *tcpclient_new(t_floatarg udpflag) t_tcpclient *x = (t_tcpclient *)pd_new(tcpclient_class); x->x_msgout = outlet_new(&x->x_obj, &s_anything); /* received data */ x->x_addrout = outlet_new(&x->x_obj, &s_list); - x->x_outconnect = outlet_new(&x->x_obj, &s_float); /* connection state */ + x->x_connectout = outlet_new(&x->x_obj, &s_float); /* connection state */ + x->x_statusout = outlet_new(&x->x_obj, &s_anything);/* last outlet for everything else */ x->x_clock = clock_new(x, (t_method)tcpclient_tick); x->x_poll = clock_new(x, (t_method)tcpclient_poll); x->x_fd = -1; @@ -459,6 +514,7 @@ void tcpclient_setup(void) , A_SYMBOL, A_FLOAT, 0); class_addmethod(tcpclient_class, (t_method)tcpclient_disconnect, gensym("disconnect"), 0); class_addmethod(tcpclient_class, (t_method)tcpclient_send, gensym("send"), A_GIMME, 0); + class_addmethod(tcpclient_class, (t_method)tcpclient_buf_size, gensym("buf"), A_GIMME, 0); class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("receive"), 0); class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("rcv"), 0); class_addmethod(tcpclient_class, (t_method)tcpclient_dump, gensym("dump"), A_FLOAT, 0); -- cgit v1.2.1