views:

371

answers:

3

I am writing an API which includes IPC functions which send data to another process which may be local or on another host. I'd really like the send function to be as simple as:

int mySendFunc(myDataThing_t* thing, int sd);

without the caller having to know -- in the immediate context of the mySendFunc() call -- whether sd leads to a local or remote process. It seems to me that if I could so something like:

switch (socketFamily(sd)) {
case AF_UNIX:
case AF_LOCAL:
   // Send without byteswapping
   break;
default:
   // Use htons() and htonl() on multi-byte values
   break;
}

It has been suggested that I might implement socketFamily() as:

unsigned short socketFamily(int sd)
{
   struct sockaddr sa;
   size_t len;
   getsockname(sd, &sa, &len);   
   return sa.sa_family;
}

But I'm a little concerned about the efficiency of getsockname() and wonder if I can afford to do it every time I send.

+3  A: 

See getsockname(2). You then inspect the struct sockaddr for the family.

EDIT: As a side note, its sometimes useful to query info as well, in this case info libc sockets

EDIT:

You really can't know without looking it up every time. It can't be simply cached, as the socket number can be reused by closing and reopening it. I just looked into the glibc code and it seems getsockname is simply a syscall, which could be nasty performance-wise.

But my suggestion is to use some sort of object-oriented concepts. Make the user pass a pointer to a struct you had previously returned to him, i.e. have him register/open sockets with your API. Then you can cache whatever you want about that socket.

Eduard - Gabriel Munteanu
+1  A: 

If you control the client and server code I have a different suggestion, which I've used successfully in the past.

Have the first four bytes of your message be a known integer value. The receiver can then inspect the first four bytes to see if it matches the known value. If it matches, then no byte swapping is needed.

This saves you from having to do byte swapping when both machines have the same endianness.

17 of 26
Unless I misunderstand you, that works for receiving but not for sending.
Chris Nelson
With this technique, byte swapping is only done on the receiving side. The sender never swaps - it just sets that 4 byte value before the rest of the data. By looking at the 4 bytes on the receiving end, the receiver knows whether or not they need to swap.
17 of 26
The beauty of this is that as long as the sender and receiver endianness match there is no byte swapping done, even if they are not in network byte order.
17 of 26
Yeah, though it wouldn't work if someone decides to use this unilaterally, e.g. making a HTTP retriever.
Eduard - Gabriel Munteanu
Right, the caveat here is that you need to be in control of both sides of the connection. Or at least be able to dictate the terms to the other side :). The original poster's situation was unclear to me.
17 of 26
+2  A: 

Why not always send in network byte order?

fizzer
Because fairly often I'll be doing local IPC and don't want to do hton*() then ntoh*() on every multi-byte value.
Chris Nelson
Why not? You'll need this code anyway in the network case. Is there any evidence that the byte swap is a bottleneck in the context of IPC?
fizzer
@fizzer, tell that to Xorg guys. Yes, it matters.
Eduard - Gabriel Munteanu
Whenever you're doing network communication you want to minimize data copying as much as humanly possible if you want your application to scale well.
17 of 26
Though there is a point here. I'm unsure what should be done in case we assume no collaboration between endpoints: do local sockets generally expect data to be in network byte order?
Eduard - Gabriel Munteanu
I believe that is the case, yes.
17 of 26