Can someone please help on what I can do to make this simple TCP socket transfer code fast?
I have tried changing the buffer size and non blocking / blocking sockets. Nothing helps. It is much slower than a ssh tunnel. I only have 2 TCP sockets to deal with so I assume select() is not the issue. TRANSFER_BUFF_SIZE of 1000 seems to work the best. Have no idea why.
Thanks in advance
/* Sends data to the specified non blocking tcp socket. data_size is in bytes.
* Returns true if successful */
bool send_non_block_tcp_socket_data (int socket_id, void *data, int data_size)
{
int i, n;
fd_set write_fds, err_fds;
timeval tv_delay;
char *ptr;
if (socket_id == -1) return false;
ptr = (char*) data;
tv_delay.tv_sec = 10;
tv_delay.tv_usec = 0;
i = 0;
while (i < n)
{
FD_ZERO (&write_fds); //clear set
FD_SET (socket_id, &write_fds); //add socket_id to set
FD_ZERO (&err_fds); //clear set
FD_SET (socket_id, &err_fds); //add socket_id to set
while (select (socket_id + 1, NULL, &write_fds, &err_fds, &tv_delay) == 0)
{
tv_delay.tv_sec = 10;
tv_delay.tv_usec = 0;
}
if (FD_ISSET (socket_id, &err_fds) != 0) return false;
if (FD_ISSET (socket_id, &write_fds) != 0)
{
n = send (socket_id, &ptr[i], data_size - i, 0);
if (n == -1) return false;
i += n;
}
}
return true;
}
/* Sets the tcp socket as non blocking */
bool set_tcp_socket_non_blocking (int socket_id)
{
unsigned int op;
op = fcntl (socket_id, F_GETFL);
//sbit (&op, O_NONBLOCK);
op = O_NONBLOCK;
fcntl (socket_id, F_SETFL, op);
return true;
}
/* Reads tty output into the specified buffer.
* Returns number of bytes read or -1 on error*/
int get_non_block_tcp_socket_data (int socket_id, char *buffer, int buffer_size)
{
int n;
if (socket_id == -1) return -1;
if ((n = recv (socket_id, buffer, buffer_size, 0)) > 0) return n;
if (errno == EAGAIN) return 0; //no error, just no data
return -1;
}
/* Waits for the specified tcp sockets to have data to be read.
* Returns socket that has data ready or -1 on error */
int wait_read_tcp_sockets (int socket_id, int socket_id2)
{
int n, id;
fd_set read_fds, err_fds;
fd_set sid_set;
timeval tv_delay;
if (socket_id == -1) return -1;
if (socket_id2 == -1) return -1;
id = socket_id;
if (id < socket_id2) id = socket_id2;
while (true)
{
FD_ZERO (&read_fds);
FD_SET (socket_id, &read_fds);
FD_SET (socket_id2, &read_fds);
FD_ZERO (&err_fds);
FD_SET (socket_id, &err_fds);
FD_SET (socket_id2, &err_fds);
tv_delay.tv_sec = 10;
tv_delay.tv_usec = 0;
n = select (id + 1, &read_fds, NULL, &err_fds, &tv_delay);
if (n != 0) break;
}
if (n > 0)
{
if (FD_ISSET (socket_id, &err_fds) != 0) return -1;
if (FD_ISSET (socket_id2, &err_fds) != 0) return -1;
if (FD_ISSET (socket_id, &read_fds) != 0) return socket_id;
if (FD_ISSET (socket_id2, &read_fds) != 0) return socket_id2;
}
if (n < 0)
{
fprintf (stderr, "wait_read_tcp_sockets() select error\n");
}
return -1;
}
/* Transfers data between two TCP sockets
*
* Return true if successful */
bool transfer_data_tcp_sockets (int socket_id, int socket_id2)
{
int n, si, read_sid, write_sid;
char buffer[TRANSFER_BUFF_SIZE+4];
bool read;
set_tcp_socket_non_blocking (socket_id);
set_tcp_socket_non_blocking (socket_id2);
while (true)
{
read = false;
n = get_non_block_tcp_socket_data (socket_id2, buffer, TRANSFER_BUFF_SIZE);
if (n < 0) return false;
if (n > 0)
{
read = true;
if (send_non_block_tcp_socket_data (socket_id, buffer, n) == -1) return false;
}
//--------
n = get_non_block_tcp_socket_data (socket_id, buffer, TRANSFER_BUFF_SIZE);
if (n < 0) return false;
if (n > 0)
{
read = true;
if (send_non_block_tcp_socket_data (socket_id2, buffer, n) == -1) return false;
}
if (!read)
{
while (true)
{
read_sid = wait_read_tcp_sockets (socket_id, socket_id2);
if (read_sid == -1)
{
fprintf (stderr, "wait_read_tcp_sockets() error\n");
return false;
}
else if (read_sid == socket_id) write_sid = socket_id2;
else if (read_sid == socket_id2) write_sid = socket_id;
else
{
fprintf (stderr, "wait_read_tcp_sockets() returned invalid socket %d\n",
read_sid);
continue;
}
while (true)
{
n = get_non_block_tcp_socket_data (read_sid, buffer, TRANSFER_BUFF_SIZE);
if (n < 0) return false;
if (n > 0)
{
read = true;
if (send_non_block_tcp_socket_data (write_sid, buffer, n) == -1) return false;
}
break;
}
}
}
}
return true;
}