LinuxのプログラムをWindowsで動くように移植するとき、スレッドやミューテックなどは同等のAPIがWindowsにあるため、どうにかなるのですが、Unixドメインソケットの部分だけはなかなか同等のAPIがありません。
Cygwinを使わないで、Windowsで1:nのUnixドメインソケットを使いたい場合、よいライブラリがないので、Googleのコードを参考に変更して作ってみました。
#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
}