2014年12月9日火曜日

WindowsでUnixドメインソケットを使う

LinuxのプログラムをWindowsで動くように移植するとき、スレッドやミューテックなどは同等のAPIがWindowsにあるため、どうにかなるのですが、Unixドメインソケットの部分だけはなかなか同等のAPIがありません。

そこでみんなどうしているのかいろいろ調べてみました。

まずGoogle。
LocalBiChannnel.cppというのがAndroidのソースコードの中にあります。
たぶんローカルバイナリーチャンネルの略なのでしょう。
このファイルは1:1でローカルソケットを使うときはLinux、MacOSX、WindowsすべてのOSで動くので大変便利。Androidでは大体これを使っています。このやり方とても頭よいです。さすが。


それ以外の人はCygwinでUnixドメインソケットが使えるのでCygwinのローカルを使っているようです。

Cygwinを使わないで、Windowsで1:nのUnixドメインソケットを使いたい場合、よいライブラリがないので、Googleのコードを参考に変更して作ってみました。


---------------------------------------
localsocket_.c
---------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "localsocket_.h"

#if defined(_WIN32)
# include <windows.h>
#endif

#if defined(unix) || defined(__APPLE__)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#endif



#ifndef SUN_LEN
/*
 * Our current set of ARM header files don't define this.
 */
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
                      + strlen ((ptr)->sun_path))
#endif


static const unsigned long kInvalidHandle = (unsigned long) -1;
static const int kPipeSize = 4096;

/*
 * Destructor.  If we're the server side, we may need to clean up after
 * ourselves.
 */

void localsocket_close(localsocket_t* ls)
{
#ifdef _WIN32
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle));
        CloseHandle((HANDLE)(ls->mHandle));
}
    if (ls->mHandle2 != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle2));
        CloseHandle((HANDLE)(ls->mHandle2));
}
free(ls);
#endif
#if defined(unix) || defined(__APPLE__)
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle)
        close((int) (ls->mHandle));
    if (ls->mIsListener) {
        (void) unlink(ls->mFileName);
    }
free(ls);
#endif
}

void makeFilename(char* fileName, const char* name)
{
#ifdef _WIN32
static const char* kBasePath = "\\\\.\\pipe\\android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);

#endif
#if defined(unix) || defined(__APPLE__)
    static const char* kBasePath = "/tmp/android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);
#endif
}

static localsocket_t* alloc()
{
localsocket_t* ret;
ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;
memset(ret,0,sizeof(localsocket_t));
ret->mHandle=kInvalidHandle;
ret->mHandle2=kInvalidHandle;
ret->mPort=1;

return ret;
}

localsocket_t* localsocket_create(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

makeFilename(ret->mFileName,name);
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    struct stat sb;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    cc = stat(ret->mFileName, &sb);
    if (cc < 0) {
        if (errno != ENOENT) {
free(ret);
return NULL;
        }
    } else {
        /* don't touch it if it's not a socket */
        if (!(S_ISSOCK(sb.st_mode))) {
free(ret);
return NULL;
   }

       /* remove the cruft */
        if (unlink(ret->mFileName) < 0) {
free(ret);
return NULL;
        }
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* bind the socket; this creates the file on disk */
    strcpy(addr.sun_path, ret->mFileName);    // max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

    ret->mHandle = (unsigned long) sock;
    ret->mIsListener = 1;

return ret;

#endif
}

localsocket_t* localsocket_attach(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
HANDLE handle;
DWORD bytesRead;
int port;
char buf[16];
int ok;

if(name==NULL || strlen(name)>100)return NULL;
ret=alloc();
if(ret==NULL)return ret;

//
// open common pipe to fix port num
//

makeFilename(ret->mFileName,name);
do{
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// No calling CreateNamedPipe() API.
free(ret);
return NULL;
}
// If other thread called CreateFile() API, Windows returned INVALID_HANDLE_VALUE.
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
}while(handle == INVALID_HANDLE_VALUE);

buf[0]=0;
bytesRead=0;
ReadFile(handle, buf, 1, &bytesRead,NULL);
//printf("read=%d\n",bytesRead);
port=buf[0]&255;

CloseHandle(handle);
sprintf(buf,"-%d",port);

//
// open port 
//

strcat(ret->mFileName,buf);
//printf("port=%d   name=%s\n",port,ret->mFileName);
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// 
free(ret);
return NULL;
}
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
    if (handle == INVALID_HANDLE_VALUE) {
free(ret);
        return NULL;
    }
ret->mHandle=(unsigned long)handle;
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* connect to socket; fails if file doesn't exist */
    strcpy(addr.sun_path, ret->mFileName); 
// max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

ret->mHandle=sock;
    ret->mIsListener = 0;

    return ret;
#endif
}

localsocket_t* localsocket_listen(localsocket_t* ls)
{
return localsocket_listen_timeout(ls,0);
}

localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int timeout)
{
#ifdef _WIN32
localsocket_t* ret;
    HANDLE hPipe;
    BOOL connected;
OVERLAPPED ol ={0};
char port[16];
DWORD bytesWritten;

if(ls==NULL)return NULL;

ret=alloc();
if(ret==NULL)return ret;

//
// open common port
//
if(ls->mHandle==kInvalidHandle){
hPipe = CreateNamedPipe(
                    ls->mFileName,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
| (timeout?FILE_FLAG_OVERLAPPED:0)
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle=(unsigned long)hPipe;
}
//
// open first port
//
if(ls->mHandle2==kInvalidHandle){
int pp;
pp=ls->mPort & 255;
strcpy(ls->mFileName2,ls->mFileName);
sprintf(port,"-%d",pp);
strcat(ls->mFileName2,port);

//printf("port=%d  name=%s  name=%s\n",pp,ls->mFileName,ls->mFileName2);

hPipe = CreateNamedPipe(
                    ls->mFileName2,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle2=(unsigned long)hPipe;
//printf("handle=%p\n",hPipe);
}

//
// connect
//
hPipe=(HANDLE)(ls->mHandle);

if(timeout){
connected=0;
ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
connected=ConnectNamedPipe(hPipe, &ol);
if(connected==0){
switch(GetLastError()){
case ERROR_PIPE_CONNECTED:
connected=TRUE;
break;
case ERROR_IO_PENDING:
if(WaitForSingleObject(ol.hEvent,timeout)==WAIT_OBJECT_0){
DWORD ig;
connected=GetOverlappedResult(hPipe,&ol,&ig,FALSE);
}else{
CancelIo(hPipe);
}
break;

}
}
CloseHandle(ol.hEvent);
}else{
   connected = ConnectNamedPipe(hPipe, NULL) ?
   TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
}
    if (connected==FALSE) {
free(ret);
return NULL;
}
//printf("connect step2\n");

// write port no.
port[0]=ls->mPort;
ls->mPort++;
WriteFile((HANDLE) (ls->mHandle), port, 1, &bytesWritten, NULL);
FlushFileBuffers((HANDLE) (ls->mHandle));
DisconnectNamedPipe(hPipe);

ConnectNamedPipe((HANDLE) (ls->mHandle2), NULL);

ret->mHandle=(unsigned long)(ls->mHandle2);
ls->mHandle2=(unsigned long)kInvalidHandle;

return ret;


#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    struct sockaddr_un from;
    socklen_t fromlen;

    int sock, lsock;
    int cc;
fd_set readfds;
struct timeval tv;
int n;

if(ls==NULL)return NULL;

ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;

lsock = (int) (ls->mHandle);
if(ls->mIsListened==0){
   cc = listen(lsock, 5);
if (cc < 0) {
free(ret);
return NULL;
}
ls->mIsListened=1;
}


if(timeout){
FD_ZERO(&readfds);
FD_SET(lsock, &readfds);

memset(&tv,0,sizeof(tv));
tv.tv_sec=timeout/1000;
tv.tv_usec=(timeout%1000)*1000;

n = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
if(n==0){
free(ret);
return NULL;
}
}
    fromlen = sizeof(from);     // not SUN_LEN()
    sock = accept(lsock, (struct sockaddr*) &from, &fromlen);
    if (sock < 0) {
free(ret);
return NULL;
    }

ret->mHandle=sock;
ret->mIsListener=0;
    return ret;
#endif
}


int localsocket_read(localsocket_t* ls,void* buf, int count)
{
#ifdef _WIN32
    DWORD totalBytesAvail = count;
    DWORD bytesRead;

if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

if (!ReadFile((HANDLE) (ls->mHandle), buf, totalBytesAvail, &bytesRead,
            NULL))
    {
        DWORD err = GetLastError();
        if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
            return 0;
        return -1;
    }
    return (int) bytesRead;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = read((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;
#endif
}
int localsocket_write(localsocket_t* ls,const void* buf, int count)
{
#ifdef _WIN32
    DWORD bytesWritten;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

    if (!WriteFile((HANDLE) (ls->mHandle), buf, count, &bytesWritten, NULL)) {
        return -1;
    }
    return (int) bytesWritten;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = write((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;

#endif
}
int localsocket_readReady(localsocket_t* ls)
{
#ifdef _WIN32
    DWORD totalBytesAvail;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;


    if (!PeekNamedPipe((HANDLE) (ls->mHandle), NULL, 0, NULL,
            &totalBytesAvail, NULL))
    {
        return 1;
    }

    return (totalBytesAvail != 0);
#endif
#if defined(unix) || defined(__APPLE__)
    errno = 0;
    fd_set readfds;
    struct timeval tv = { 0, 0 };
    int cc;

if(ls==NULL)return -1;

FD_ZERO(&readfds);
    FD_SET((int)(ls->mHandle), &readfds);

    cc = select((int)(ls->mHandle)+1, &readfds, NULL, NULL, &tv);
    if (cc < 0) {
        return 1;
    } else if (cc == 0) {
        /* timed out, nothing available */
        return 0;
    } else if (cc == 1) {
        /* our fd is ready */
        return 1;
    } else {
        return 1;
    }
#endif
}


---------------------------------------
localsocket_.h
---------------------------------------
#ifndef __LOCALSOCKET_H
#define __LOCALSOCKET_H

typedef struct st_local_socket{
    char       mFileName[128];
    char       mFileName2[128];
    int        mIsListener;
    unsigned long mHandle;
    unsigned long mHandle2;
int        mIsListened;
unsigned char mPort;
} localsocket_t;


#ifdef __cplusplus
extern "C" {
#endif

localsocket_t* localsocket_create(const char* name);
localsocket_t* localsocket_listen(localsocket_t* ls);
localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int to);
localsocket_t* localsocket_attach(const char* name);

void localsocket_close(localsocket_t* ls);

int localsocket_read(localsocket_t* ls,void* buf, int count);
int localsocket_write(localsocket_t* ls,const void* buf, int count);
int localsocket_readReady(localsocket_t* ls);


#ifdef __cplusplus
}
#endif

#endif 


0 件のコメント:

コメントを投稿