通信時のソースアドレスを取得する
※Linuxです。
なんて言えばよいのかわからなかったのですが、ip route get xxx.xxx.xxx.xxx
と打ったときに出てくる
ソース(送信元)アドレスをC/C++内で取得したかった。すぐに思いついた方法は、
popen
を使ってip route get
を実行し、標準出力を取得- 一時的にソケットを作って送信元IPアドレスを取得
です。以下のようなサンプルコードを書きました。
どちらも同じ出力が得られますが、手元の環境で試したところ、
1000回実行するのにかかる時間がpopen
を使った方法では約1秒、
ソケットを使った方法では約0.005秒だった。(コードの改善点はあるかもしれませんが。)
他にもっと良い方法があれば教えてください!
ソケットに結び付けられたアドレスを取得する
※Linuxです。
netstat
したときのLocal AddressとForeign AddressにあたるものをC/C++で取得したかった。
Local Addressはgetsockname()
、Foreign Addressはgetpeername()
を使えば取得できるらしい。
C++でサンプルを書くと以下のような感じ。
gist6c0628fe0ad9790fe7a1fbc5cba9b13c
vagrant@vagrant-ubuntu-trusty-64:~$ ./a.out 192.168.0.2 2551 Local Address: 192.168.0.1:46402 Foreign Address: 192.168.0.2:2551
ちゃんと取得できてますね!(↑のサンプルコードを使うときは対応するアドレスをlistenしてください)
ZeroMQのheartbeatを試してみた
ZeroMQのversion 4.2からheartbeat機能が追加されました。 元々コネクションの切断は検知してzmq_socket_monitor(3)で通知してくれたのですが、 TCPエラーを吐かずにコネクションが死んだり、Out of Memory等でプロセスが止まったりした場合は検知できないので heartbeat機能が追加されたようです。(参考:ZeroMQのRFC)
heartbeatに関するソケットのオプションは以下の3つです。
ZMQ_HEARTBEAT_IVL
ZMQ_HEARTBEAT_TIMEOUT
ZMQ_HEARTBEAT_TTL
ZMQ_HEARTBEAT_IVL
ミリ秒の間隔でpingを送り、ZMQ_HEARTBEAT_TIMEOUT
ミリ秒経っても
pongが返ってこなかったらコネクションが死んだとみなすという感じです。また、pongを返す側では
ZMQ_HEARTBEAT_TTL
ミリ秒経っても次のpingが来なかったらコネクションが死んだとみなします。
ZMQ_HEARTBEAT_IVL
を設定することで初めてheartbeatが動作します。
試しに以下のような設定をしてみます。基本的にどちらか片方(connect側もしくはbind側)のソケットにだけ設定しておけばOKだと思います。
int interval = 100, timeout = 1000, ttl = 1000; zmq_setsockopt(sock, ZMQ_HEARTBEAT_IVL, &interval, sizeof(int)); zmq_setsockopt(sock, ZMQ_HEARTBEAT_TIMEOUT, &timeout, sizeof(int)); zmq_setsockopt(sock, ZMQ_HEARTBEAT_TTL, &ttl, sizeof(int)); zmq_setsockopt(sock, ZMQ_HANDSHAKE_IVL, &timeout, sizeof(int));
ZMQ_HEARTBEAT_TTL
はZMQ_HEARTBEAT_IVL
よりも十分大きい値にしておく必要があります。
そうしないと接続が頻繁に切れてしまいます。ZMQ_HEARTBEAT_TIMEOUT
も短すぎると
偽陽性が増えてしまうので注意です。
また、ZMQ_HANDSHAKE_IVL
を設定しています。ZeroMQではソケットが接続した後に
ソケットの設定情報を交換する(これをhandshakeと呼ぶ)そうなのですが、止まっているソケットに再接続した際に
ZMQ_HEARTBEAT_TIMEOUT
よりもZMQ_HANDSHAKE_IVL
が優先されるため、両方共同じ値にしておきます。
ZMQ_HANDSHAKE_IVL
のデフォルト値は30秒なので、これを設定しないと
ZMQ_HEARTBEAT_TIMEOUT
をどれだけ短くしても再接続時に30秒待ってしまいます。
実際にこんな感じのコードを書いて動かします。
bind/connectが完了した時点で、connect側のプロセスにSIGTSTPを送るとbind側のzmq_socket_monitor(3)
から
ZMQ_EVENT_DISCONNECTED
が通知されます。逆に、bind側のプロセスにSIGTSTPを送ると、connect側の
zmq_socket_monitor(3)
からZMQ_EVENT_DISCONNECTED
が通知された後に、再接続を試みます。
bind側のlistenが残っているからか、再接続は成功しますが、handshakeが失敗して再びZMQ_EVENT_DISCONNECTED
が
通知されるというような挙動になります。
また、heartbeatを有効にしても前回の調査同様、unbind/disconnect時には 相手側のソケットには何も通知されません。disconnectしても再接続しようとするし、unbind/disconnectとは何なのだろう…。 とりあえずソケットを切断するときは必ずcloseした方が良さそうです。
※ZeroMQ v4.2.1を使用
ZeroMQのzmq_socket_monitor(3)の挙動を調べた
ZeroMQのzmq_socket_monitor(3)
の挙動を調べてみました。
bindするソケットとconnectするソケットを用意し、以下の順に処理して
zmq_socket_monitor(3)
でどのようなイベントが通知されるか確認しました。
- create (bind/connect socket)
- bind
- connect
- disconnect
- close (connect socket)
- create (connect socket)
- connect
- unbind
- close (bind socket)
上記の処理を1秒間隔で行い、イベントの出力を確認します。 使用したソースコードはこちらに載せました。
bind側の出力
vagrant@vagrant-ubuntu-trusty-64:~$ ./a.out bind tcp://0.0.0.0:2551 [0.000] [main] Create... [1.000] [main] Bind... [1.010] [event] number: 8, value: 12, address: tcp://0.0.0.0:2551 [2.011] [event] number: 32, value: 13, address: tcp://0.0.0.0:2551 [4.014] [event] number: 512, value: 13, address: tcp://0.0.0.0:2551 [6.019] [event] number: 32, value: 13, address: tcp://0.0.0.0:2551 [7.001] [main] Unbind... [7.009] [event] number: 128, value: 12, address: tcp://0.0.0.0:2551 [8.003] [main] Close... [8.004] [event] number: 1024, value: 0, address: [9.004] [main] Finish...
connect側の出力
vagrant@vagrant-ubuntu-trusty-64:~$ ./a.out connect tcp://192.168.0.1:2551 [0.000] [main] Create... [2.000] [main] Connect... [2.003] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [2.004] [event] number: 1, value: 12, address: tcp://192.168.0.1:2551 [3.002] [main] Disconnect... [4.005] [main] Close... [4.006] [event] number: 1024, value: 0, address: [5.012] [main] Create... [6.014] [main] Connect... [6.015] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [6.016] [event] number: 1, value: 11, address: tcp://192.168.0.1:2551 [8.013] [event] number: 512, value: 11, address: tcp://192.168.0.1:2551 [8.014] [event] number: 4, value: 183, address: tcp://192.168.0.1:2551 [8.196] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.197] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.197] [event] number: 4, value: 125, address: tcp://192.168.0.1:2551 [8.316] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.317] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.317] [event] number: 4, value: 141, address: tcp://192.168.0.1:2551 [8.466] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.466] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.467] [event] number: 4, value: 186, address: tcp://192.168.0.1:2551 [8.652] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.653] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.654] [event] number: 4, value: 197, address: tcp://192.168.0.1:2551 [8.848] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.849] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.849] [event] number: 4, value: 110, address: tcp://192.168.0.1:2551 [8.957] [event] number: 2, value: 115, address: tcp://192.168.0.1:2551 [8.958] [event] number: 128, value: 11, address: tcp://192.168.0.1:2551 [8.959] [event] number: 4, value: 138, address: tcp://192.168.0.1:2551 [9.015] [main] Close... [9.015] [main] Finish...
一番左側の列は経過時間を表しています。 numberはZeroMQで定義されているイベント番号で、以下のようになっています。
/* Socket transport events (tcp and ipc only) */ #define ZMQ_EVENT_CONNECTED 1 #define ZMQ_EVENT_CONNECT_DELAYED 2 #define ZMQ_EVENT_CONNECT_RETRIED 4 #define ZMQ_EVENT_LISTENING 8 #define ZMQ_EVENT_BIND_FAILED 16 #define ZMQ_EVENT_ACCEPTED 32 #define ZMQ_EVENT_ACCEPT_FAILED 64 #define ZMQ_EVENT_CLOSED 128 #define ZMQ_EVENT_CLOSE_FAILED 256 #define ZMQ_EVENT_DISCONNECTED 512 #define ZMQ_EVENT_MONITOR_STOPPED 1024
この結果からわかることは、
- bindした直後にbind側で
ZMQ_EVENT_LISTENING
が通知される - connectした直後にbind側で
ZMQ_EVENT_ACCEPTED
、connect側でZMQ_EVENT_CONNECT_DELAYED
、ZMQ_EVENT_CONNECTED
が通知される - disconnectしてもbind側、connect側共に何も通知されない
- unbindするとbind側には
ZMQ_EVENT_CLOSED
が通知されるが、connect側には何も通知されない - connect側をcloseするとbind側に
ZMQ_EVENT_DISCONNECTED
が通知される - bind側をcloseするとconnect側に
ZMQ_EVENT_DISCONNECTED
が通知され、connect側は再接続を試みる - connect側が再接続を試みている間は、
ZMQ_EVENT_CLOSED
、ZMQ_EVENT_CONNECT_RETRIED
、ZMQ_EVENT_CONNECT_DELAYED
が通知され続ける - ソケットをcloseすると、
ZMQ_EVENT_MONITOR_STOPPED
が通知される
ちなみに、プロセスを強制的に落とした場合はcloseしたときと同じ挙動になり、
ZMQ_EVENT_DISCONNECTED
が通知されます。
今回は通知するイベントをZMQ_EVENT_ALL
にしましたが、
実際に使うときには適切なイベントを選択して通知した方が良いかと思います。
※ZeroMQ v4.2.1を使用