C++ – send data between two client sockets

cmacossockets

I have to make an app using C sockets on Mac-OS that sends data from one socket to other socket, like this.

  1. Server waits for connections
  2. Client connect to server(from 1). -> socket1
  3. Server connects to an external server and obtains an socket. -> socket2

From now on the server job is finish. The data exchange should be made only between the client socket (from 2) and socket obtained from 3.

Current implementation:
Server makes the connection and then reads data from one socket and sends to other.

Any ides how after step 3 to pipe line the two sockets socket1 and socket2.

Best Answer

Well your problem can be solved in two ways:

1) You need to code the part related to the connection formation between client and external server. But this puts an extra overload on the client, because it needs to make two connections, to both the servers (and I strongly feel the middle server in this case is useless).

2) Second way of solving it is passing the sockets between the servers: Client connects to the server, this middle server sends this socket to the external server. Now external server starts communication with the client. This can be done only if both the server processes run on the same machine. And the file-descriptors are usually passed using Unix Domain Sockets.

Here is the code which I have. You can use these two functions to either send or receive the file-descriptors. It works on my Linux machine. I don't know about Mac-OS.

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
/* this function passes 'fd_to_send' 
file descriptor via 
a unix domain socket 'sfd'...
*/
void pass_fd(int sfd, int fd_to_send)
{
    struct msghdr msg;

    /*allocate memory to 'msg_control' field in msghdr struct */
    char buf[CMSG_SPACE(sizeof(int))];
    /*the memory to be allocated should include data + header..
    this is calculated by the above macro...(it merely adds some
    no. of bytes and returs that number..*/

    struct cmsghdr *cmsg;

    struct iovec ve;    
    /*must send/receive atleast one byte...
    main purpose is to have some error 
    checking.. but this is completely 
    irrelevant in the current context..*/

    char *st ="I";
    /*jst let us allocate 1 byte for formality 
    and leave it that way...*/
    ve.iov_base = st;
    ve.iov_len =1;

    /*attach this memory to our main msghdr struct...*/
    msg.msg_iov = &ve;
    msg.msg_iovlen = 1;

    /*these are optional fields ..
    leave these fields with zeros..
    to prevent unnecessary SIGSEGVs..*/
    msg.msg_name = NULL;
    msg.msg_namelen = 0;


    /*here starts the main part..*/
    /*attach the 'buf' to msg_control..
    and fill in the size field correspondingly..
    */

    msg.msg_control = buf;
    msg.msg_controllen = sizeof(buf);

    /*actually msg_control field must 
    point to a struct of type 'cmsghdr'
    we just allocated the memory, yet we need to 
    set all the corresponding fields..
    It is done as follows:
    */
    cmsg = CMSG_FIRSTHDR(&msg);
    /* this macro returns the address in the buffer..
    from where the first header starts..
    */

    /*set all the fields appropriately..*/
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
    /*in the above field we need to store
    the size of header + data(in this case 4 bytes(int) for our fd..
    this is returned by the 'CMSG_LEN' macro..*/

    *(int*)CMSG_DATA(cmsg) = fd_to_send;
    /*after the above three fields we keep the actual data..
    the macro 'CMSG_DATA' returns pointer to this location
    and we set it to the file descriptor to be sent..
    */

    msg.msg_controllen = cmsg->cmsg_len;
    /*now that we have filled the 'cmsg' struct 
    we store the size of this struct..*/
    /*this one isn't required when you
    pass a single fd..
    but useful when u pass multiple fds.*/

    msg.msg_flags = 0;
    /*leave the flags field zeroed..*/

    if(sendmsg( sfd, &msg, 0)==-1){ perror("snd:\n"); exit(1); }
    /*send this over the UNIX deomain socoket..*/ 
    printf("sent fd:%d\n", fd_to_send);
    close(fd_to_send);
    /*close the fd which was sent..*/
}
/*returns the received fd over the unix domain socket 'sfd'..*/
int recv_fd(int sfd)
{
    struct msghdr msg;
    /*do all the unwanted things first...
    same as the send_fd function..*/
    struct iovec io;
    char ptr[1];
    io.iov_base = ptr;
    io.iov_len = 1;
    msg.msg_name = 0;
    msg.msg_namelen = 0;
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    /*-----------------------*/


    char buf[CMSG_SPACE(sizeof(int))];
    msg.msg_control = buf;
    msg.msg_controllen = sizeof(buf);
    /*reasoning is same..as above*/

    /*now here comes the main part..*/

    if(recvmsg( sfd, &msg, 0)==-1)
    {
        /*some shit has happened*/
        perror("recv\n");
        exit(1);
    }

    struct cmsghdr *cm;

    cm =  CMSG_FIRSTHDR(&msg);
    /*get the first message header..*/

    if(cm->cmsg_type != SCM_RIGHTS)
    {
        /*again some shit has happened..*/
        perror("unknown type..\n");
        exit(1);
    }

    /*if control has reached here.. this means
    we have got the correct message..and when you 
    extract the fd out of this message 
    this need not be same as the one which was sent..
    allocating a new fd is all done by the kernel
    and our job is jst to use it..*/
     printf("received fd:%d\n", *(int*)CMSG_DATA(cm));
     return *(int*)CMSG_DATA(cm);
}