socket programming?

I am a novice socket programmer, and my question is,
after a client and server has made connected in stream mode, how can both receive and send data independent of each other.

I guess what I want is like a chat session, where client can receive when server is typing and server can do the same when client is typing.

sample code would be good.

Thanks

I think the obvious way would be using two threads.

is using select() to monitor the socket to see if data is ready more popular?

Please define popular. I think you mean have select monitor the socket and the keyboard. I’m sure that will work fine, but for the application you defined, one in which the outgoing has nothing to do with the incoming data, I’d think two threads would be a lot easier. Doesn’t even have to be threads for that matter, could be two separate processes.

I think select is better. It is simpler, and more reliable (there is no possibility for race conditions).

ok, now my next question is, how can I just forward the data received from one socket to another socket? I don’t know the type of data or it’s size (ie char, int,float, struct etc…) I just don’t care because all I need to do is do some port forwarding. Do I have to use RAW socket in this case? What data type should I make the buffer when calling recv() and send()?

Thanks

recv() and send() don’t care what type of data you send, although the compiler might.
They only care how many bytes.

i guess you want to forward the phrelay protocol
have fun… :slight_smile:

  • 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

Jinma,

Here is a nice link that shows some simple sample code for a server application using TCP sockets and select. It should do everything you want if your looking for a port forwarding example. Since you don’t want to forward back to the original sender, you’ll have to add code not to send back to yourself (this sample sends it to everyone).

beej.us/guide/bgnet/output/html/ … tml#select

The rest of this site is worthwhile exploring because it explains everything you need to know for a novice sockets programmer.

Mezek’s example is quite correct but probably a bit hard to follow if your not experienced. But most definitely, you need to ignore the SIGPIPE signal.

Tim