1
0

More than 3 years have passed since last update.

SwiftでCの構造体をキャストしてUNIXドメインソケットを使う方法

Posted at

SwiftでもCのシステムコールを呼ぶことができるのでうまく使えば、UNIXドメインソケットを使ったSwiftとCのライブラリ間のスレッド通信を行うことができる。

Cでの操作方法

C言語でUNIXドメインソケットを使う場合にはsockaddr_un構造体をsockaddrにキャストしてbindやconnectに渡す必要がある。

int sock = socket(AF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un addr;

strcpy(addr.sun_path, "/tmp/hogehoge");

addr.sun_family = AF_UNIX;

bind(sock, (struct sockaddr *)&addr, sizeof(addr));

Swiftでの操作方法

Swiftで同様にキャストを行なう必要があるが、let sockAddrUn = sockAddr as sockaddr_unのようなキャストはできない。
Swiftの場合は、

sockaddr_un構造体 → UnsafeRawPointer化 → assumingMemoryBoundを使ってsockaddr構造体に変換

のようなステップを踏む必要がある。

具体的には、次のように書く

let sock = socket(AF_UNIX, SOCK_STREAM, 0)

let sockPath = "/tmp/hogehoge"

var sockAddrUn = sockaddr_un()
sockAddrUn.sun_family = sa_family_t(AF_UNIX)
sockPath.withCString { path in
    Darwin.memcpy(&(sockAddrUn.sun_path), path, Int(strlen(path)))
}

let rawPtr = UnsafeRawPointer(&sockAddrUn)
let sockAddrPtr = rawPtr.assumingMemoryBound(to: sockaddr.self)

Darwin.bind(sock, sockAddrPtr, socklen_t(MemoryLayout<sockaddr_un>.stride))

こんな書き方は絶対ダメ

因みに最初に構造体のキャスト方法がわからず変数を二つ用意してmemcpyをするという荒技を行なっていた。

let sock = socket(AF_UNIX, SOCK_STREAM, 0)

let sockPath = "/tmp/hogehoge"

var sockAddrUn = sockaddr_un()
sockAddrUn.sun_family = sa_family_t(AF_UNIX)
sockPath.withCString { path in
    Darwin.memcpy(&(sockAddrUn.sun_path), path, Int(strlen(path)))
}

var sockAddr = sockaddr()
Darwin.memcpy(&sockAddr, &sockAddrUn, MemoryLayout<sockaddr_un>.stride)

Darwin.bind(sock, &sockAddr, socklen_t(MemoryLayout<sockaddr_un>.stride))

sockaddr_unの分だけコピーしてbindにsockaddr_unの長さを渡すのでビルドは通るし、動きはするがReleaseビルドで最適化設定がされるとsockaddrからはみ出た分のデータが消えてしまい、bindがEAFNOSUPPORTエラーを返すようになる。

A構造体 → UnsafeRawPointer → assumingMemoryBoundでB構造体 でキャストするようにしましょう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0