TheDarkKnight
TheDarkKnight

Reputation: 27621

How to use a local domain socket in Objective-C

Objective-C makes it very easy to use network sockets with streams doing something like this:

// Setup comms with the server, assumed to be running on the local host
NSHost* host = [NSHost hostWithAddress:@"127.0.0.1"];

NSInputStream *iStream;
NSOutputStream *oStream;

[NSStream getStreamsToHost:host port:_PORT inputStream: &iStream outputStream: &oStream];

However, is it possible to create and or connect to a local domain socket this way, or does Objective-C provide other classes for this?

If I can still use NSStream and getStreamsToHost, how would I specify the file and what would I put for the port number?

My research on this, so far, shows many examples working with TCP/IP or UDP, but not local domain sockets.

Upvotes: 3

Views: 2195

Answers (1)

al45tair
al45tair

Reputation: 4433

You can’t use -getStreamsToHost:port:inputStream:outputStream: for a UNIX domain socket, no. You can, however, create your own NSInputStream and NSOutputStream instances; the easiest way is to take advantage of toll-free bridging between CF(Read|Write)Stream and NS(Input|Output)Stream; for instance:

struct sockaddr_un sun;

sun.sun_family = AF_UNIX;
strcpy (sun.sun_path, "/path/to/my/socket");
sun.sun_len = SUN_LEN(&sun);

// Server side (naive)
int server_sock = socket (SOCK_UNIX, SOCK_STREAM, 0);
int ret = bind (server_sock, (struct sockaddr *)&sun, sun.sun_len);

listen (server_sock, 1);               // In practice you'd specify more than 1

s = accept (server_sock, NULL, NULL);  // In practice you want to keep calling this

// Client side
int s = socket (SOCK_UNIX, SOCK_STREAM, 0);

int ret = connect (s, (struct sockaddr *)&sun, sun.sun_len);

CFReadStreamRef readStream;
CFWriteStreamRef writeStream;

CFStreamCreatePairWithSocket (kCFAllocatorDefault, s, &readStream, &writeStream);

Then to get an NSInputStream and NSOutputStream you can just do

NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputSteram *)outputStream;

Obviously in practice you may want to wrap all of the above in a function or method in your own code. Also, take care with sockaddr_un; the sun_path member was probably supposed to have 1024 characters, but in the headers it seems to only have 104 (this is a long-standing problem and apparently goes way back to BSD4.4; some systems have other character counts here too). This is nowhere near PATH_MAX, so in practice you may wish to write something more like

struct sockaddr_un *new_unix_addr (const char *path) {
  size_t len = strlen (path);
  size_t bytes = sizeof (struct sockaddr_un) + len + 1
                 - sizeof (((struct sockaddr_un *)0)->sun_path);
  struct sockaddr_un *pun = (struct sockaddr_un *)malloc (bytes);

  pun->sun_family = AF_UNIX;
  pun->sun_len = bytes;
  memcpy (pun->sun_path, path, len + 1);
  return pun;
}

remembering later to free() it.

Upvotes: 2

Related Questions