2010年12月17日 星期五

Unix Domain Socket

由Wiki上對"Unix domain socket"的解釋, 很清楚的知道它也是IPC的一種方式, 因為他所用的API與internet socket幾乎相同, 所以對於寫過socket程式的人, 應該很好上手.

這裡提供一個小範例, 將unix socket api再包一層, 讓開發程式時能更容易的達成IPC目的.

my_unix_socket.h
#ifndef __MY_UNIX_SOCKET_H__
#define __MY_UNIX_SOCKET_H__

int create_listener(char* name, void* funp(char*));
int send_to_listener(char* app_name, void* buf, int buf_size);

#endif /* __MY_UNIX_SOCKET_H__ */

my_unix_socket.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>

#define MAX_BUF_SIZE  (1024)

static void first_handler(int fd, char* buf)
{
  int connected_fd;
  struct sockaddr_un from;
  int lenfrom = sizeof(from);
 
  bzero(&from, sizeof(from));
 
  if ((connected_fd = accept(fd, (struct sockaddr *)&from, &lenfrom)) < 0 )
  {
    printf("accept failed\n");
    return;
  }

  if (recv(connected_fd, buf, MAX_BUF_SIZE, 0) < 0)
  {
    printf("recv failed\n");
    return;
  }

  close(connected_fd);

  return;
}

int create_listener(char* name, void* funp(char*))
{
  int fd, ret=-1;
  struct sockaddr_un saddr;
  struct sockaddr_un from;
  char buf[MAX_BUF_SIZE];

  /* open a socket */
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  {
    printf("socket failed\n");
    goto fun_exit;
  }
 
  /* set the addr */
  unlink(name);
  bzero(&saddr, sizeof(saddr));
  saddr.sun_family = AF_UNIX;
  strcpy(saddr.sun_path, name);

  /* bind socket with addr */
  if (bind(fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
  {
    printf("bind failed\n");
    goto fun_exit;
  }
 
  /* listen fd */
  if (listen(fd, 10) < 0)
  {
    printf("listen failed\n");
    goto fun_exit;
  }

  fd_set rset;
  FD_ZERO(&rset);
  FD_SET(fd, &rset);

  while (1)
  {
    select(fd+1, &rset, NULL, NULL, NULL);
    if (FD_ISSET(fd, &rset))
    {
      first_handler(fd, buf);
      funp(buf);
    }
  }
  ret = 0;

fun_exit:
  if (fd>0) close(fd);
  return ret;
}

int send_to_listener(char* app_name, void* buf, int buf_size)
{
  int fd, ret=-1;
  struct sockaddr_un saddr;

  if (buf_size > MAX_BUF_SIZE)
  {
    printf("The max message size is %d\n", MAX_BUF_SIZE);
    goto fun_exit;
  }

  /* open a socket */
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  {
    printf("socket failed\n");
    goto fun_exit;
  }
 
  /* set the saddr */
  bzero(&saddr, sizeof(saddr));
  saddr.sun_family = AF_UNIX;
  strcpy(saddr.sun_path, app_name);

  /* connect to server */
  if (connect(fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
  {
    printf("connect failed\n");
    goto fun_exit;
  }

  /* send data */
  if (send(fd, buf, buf_size, 0) < 0)
  {
    printf("send failed\n");
    goto fun_exit;
  }
  ret = 0;

fun_exit:
  if (fd>0) close(fd);
  return ret;
}

兩隻簡單的應用程式:

server.c
#include <stdio.h>

void my_recv(char* buf)
{
  printf("%s,%s: %s\n", __FILE__, __FUNCTION__, buf);
}

int main()
{
  create_listener("my_server", my_recv);
}

client.c
#include <stdio.h>
#include "my_unix_socket.h"

int main()
{
  char buf[] = "hello unix socket";
  send_to_listener("my_server", buf, sizeof(buf));
}


參考資料:
[1] Unix Sockets
[2] Unix domain sockets vs. internet sockets

沒有留言:

張貼留言