i guess you want to forward the phrelay protocol
have fun…
- disable signal SIGPIPE (or handle it) to prevent program termination when you try to use send() on an closed socket (in a special case the select reports you can send() but in the meantime between select() and send() the socket gets closed on system level, the error condition after such a send() is errno==EBADF, you can just call close() on such socket)
signal(SIGPIPE,SIG_IGN);
- use socket type SOCK_STREAM
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
- switch your sockets into non-blocking mode (just call it on all sockets created by socket() and accept())
fcntl_opts = fcntl(socket_fd, F_GETFL, 0);
fcntl_opts |= O_NONBLOCK;
fcntl(socket_fd, F_SETFL, fcntl_opts);
- use SO_REUSEADDR for listen socket
int opt_value = 1;
setsockopt(socket_fd_listen, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizoef(opt_value));
- use select() to wait for sockets until they are ready for reading or writing
- do not add a socket in fds_send until you have a buffer ready to be sent
- the listen sockets can be included in the select’s fds_recv (of course you do not call recv()/send() on those but only accept())
- the sockets which are connecting (waiting for other endpoint or timeouting) can be included in the select’s fds_send
- i use select() without timeout, to leave from select() i use user programmable system timer
- width should be the highest socket_fd value which is set in the fds_recv and fds_send structures
for (;;)
{
FD_ZERO(&fds_recv);
FD_ZERO(&fds_send);
FD_SET(socket_fd_srv, &fds_recv);
FD_SET(socket_fd_cli, &fds_recv);
if (is_listening(socket_fd_listen))
FD_SET(socket_fd_listen, &fds_recv);
if (is_connecting(socket_fd_connect))
FD_SET(socket_fd_connect, &fds_send);
if (has_data_to_be_sent(socket_fd_srv))
FD_SET(socket_fd_srv, &fds_send);
if (has_data_to_be_sent(socket_fd_cli))
FD_SET(socket_fd_cli, &fds_send);
select(width+1,&fds_recv,&fds_send,NULL,NULL);
if (FD_ISSET(socket_fd_srv, &fds_recv))
{
srv_recv_buffer_received_size = recv(socket_fd_srv, srv_recv_buffer, srv_recv_buffer_size);
handle_received_data(socket_fd_srv); // if you are doing the forwarder then mark the data to be forwarded to another socket
}
if (has_data_to_be_sent(socket_fd_srv) && FD_ISSET(socket_fd_srv, &fds_send))
{
srv_recv_buffer_sent_size = send(socket_fd_srv, srv_send_buffer, srv_send_buffer_to_be_sent_size); // this call can in a special case fail in errno==EWOULDBLOCK, in that case you can just fall back to select and wait for another event
handle_sent_data(socket_fd_srv); // clean-up i guess
}
// ... same for all the other listening, connecting and connected sockets ...
}
-
use non-blocking connect - socket in non-blocking mode will fail connect() call in errno=EINPROGRESS, just use this socket in select’s fds_send (see above) to wait until the socket is writable which means the connect succeeded. there is problem in connect timeout which can be quite long in some cases - personaly i use user programmable system timer to timeout connect() - just call close() when you think you have waited long enough
-
use binary buffers to read/write (recv/send) from/to a socket
char *buffer;
int buffer_size;
recv_ret = recv(socket_fd_srv, buffer, buffer_size);
send_ret = send(socket_fd_cli, buffer, recv_ret);
- enable TCP_NODELAY to get faster response times
- disable TCP_NODELAY (should be default when socket is created) to have lower cpu load and better overall throughput
- consider using TCP_NODELAY to mimic frame sizes which you received and then sending out
int opt_value = 1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &opt_value, sizeof(opt_value));
- the above code is just an example, in real code you have to check each and all calls for failures