pythonでインターフェース作ったり、Arduinoのコントロールしながら、
C++での画像処理結果(前に何があるか、とか)をpythonに送りたくなったのでソケット通信でやって見ました。
pythonだとopencvで特徴量記述とかが出来ないし(そもそも遅いし)、C++でインターフェースとかシリアル通信なんかやってられないので、双方の弱点をカバーする方法としてプロセス間通信が最も横着な方法であると判断。
参考文献:
1、Pythonメモ 様の記事 ttp://yoshi-python.blogspot.jp/2009/10/blog-post_19.html
2、Geekなぺーじ 様の記事 ttp://www.geekpage.jp/programming/linux-network/tcp-1.php
3、信州大学HP 様 ttp://intuniv.cs.shinshu-u.ac.jp/Lecture/NetProg/chapter3/struct.html
4、pythonのリファレンス
■ 今回はpythonをサーバとしてC++側と双方向通信させました。 ちなみにサーバとクライアントの違いについてはよくわかってません。接続を待つ方がサーバぐらいの認識でいいのだろうか。
以下、参考文献のページのコードをほぼ引用した自分用のメモ
python側:
import socket
def server(host, port):
s = socket.socket() ←ソケットのインスタンスを作る
s.bind((host, port)) ←バインド(IPとポートを設定する)
s.listen(1) ←接続待ちに必要。引数は接続できるノードの数。
print "waiting"
conn, addr = s.accept() # ←クライアントの接続を待つ。connはつながってきたソケット
addrはノードのアドレス(使い所は不明)
while True: ←ずっとループ
data = conn.recv(1024) # クライアントからのデータを受信する(ここでは1024byte)
↑ここでのdataの形式がよくわからないが多分char(C++側ではconst charで送ってる)
print "server: receive '%s'" % data
datalist=str(data) ←とりあえずデータをstring化
datasp=datalist.split(",") ←データをカンマで区切る。数字をカンマ区切りで送ることを考えている。
print datasp
object.set_value(float(datasp[0]))
↑任意の変数に受信データの値を入力。これでArduinoでもなんでも動かせる(pythonと繋がってれば)
conn.send("sendfrompython") # クライアントにデータを送信する。ここでpythonからの命令(右に曲がるから右に注目しろ、とか)をC++に送ることが出来る。
conn.close() ←クライアントのソケットを閉じる。
s.close() ←こっちのソケットを閉じる。
閉じてもすぐに切断されるわけではないらしい。連続で同じポート使ったりできないことがあるので時間開けるかポート番号変えるのが吉。いまのところpython-C++ではこの事象は起こってないがpythonのプログラム間(同ソース別スレッド)でこの事象を確認。
class Server(threading.Thread):######### C++との通信スレッド
def run(self):
server(HOST, PORT) ←上記関数。HOST(127.0.0.1とか)とPORT(12345とか)はグローバル変数か何かにしとく
C++側:参考資料2からほぼ引用、難しそうな構造体については参考文献3を参照
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
↑必要なヘッダの皆さん。特に意識せずに使えたので最初から入ってると思われる。
void* thread(void* pParam) //pythonへの情報送信用スレッド
{
////ソケット通信による通信
int sockmode=1; ////1ならソケット通信ON ←通信をONOFFできて便利
if(sockmode==1){
struct sockaddr_in server; ←アドレスなんかを入れておく構造体インスタンスを定義
int sock; ←ソケットをとりあえず定義
char buf[64]; ←バッファを定義[]内はバイト数。多すぎず少なすぎずで。(少ないと即死)
int n; ←データ読み込み用
/* ソケットの作成 */
sock = socket(AF_INET, SOCK_STREAM, 0);
↑AF_INETはIPv4であること、SOCK_STREAMはTCPであることを表している。多分どの言語でも共通の定数。ここらを変えるとIPv6にしたりUDPにもできる。サーバと共通じゃないとだめ。
/* 接続先指定用構造体の準備 */↓python側の設定を書く。つまり宛先の設定
server.sin_family = AF_INET;
server.sin_port = htons(12347); ←ポート番号。htons()はバイトオーダを変換する関数
(詳細不明。接続のための何らかの規格化だろう)
server.sin_addr.s_addr = inet_addr("127.0.0.1");←このIPは自分自身(PC)を示す。変えると別の機器(PC他)にももちろんつながる。はず。関数は文字列をちゃんとアドレスの形式にする関数
/* サーバに接続 */↓詳細は「C++ connect socket」でぐぐってね
connect(sock, (struct sockaddr *)&server, sizeof(server));
while(1){ /* サーバからデータを受信。 送信も */
memset(buf, 0, sizeof(buf)); ←バッファのリセット
stringstream sts; ←とっても便利なstringstream(以下ss)オブジェクトを定義
string str="0,0,end"; ←送信文字列の初期化。文字は適当に
最後の「,end」はとても大事。python側でsplitするときとか特に
↓ 別のところで「進んでOK」とか「止まれ」とかのフラグ出すようにしてる。それに応じて送信値変更。明らかに改善の余地あり。
if(forwardflag ==1){str="100,100,end";}
if(stopflag==1){str="0,0,end";}
cout << "send:" <<str <<endl;
char sdata[64]; ←送信用バッファの定義
sts << str; ←事前に作った文字列をssへ
sts >> sdata; ←ssから送信用charへ自動変換&代入。このツールまじ便利
const char* sdataconst= sdata; ←charをconst charへ送信関数の引数がconst〜なので(このへんの変換でちょっと詰まった。)
write(sock, sdataconst, strlen(sdataconst));←データをあっちに送信。第3引数は送信データの文字数にする。strlenは全角文字入ると正確じゃなくなるので注意
cout<<"senddata"<<sdataconst <<endl;
int n = read(sock, buf, sizeof(buf)); ←pythonからbufへ読み込み。nは読み込めたかどうかとかを確認するための数字(読み込め無かったら-1とか)。読み込みデータじゃないので注意
cout <<"resdata" <<buf << endl;
sleep(1);←1秒に1回通信。もっと少なくてもいいけどスリープ入れないと多分ろくなことにならない
sts.clear(); ←ssの中身をクリア
}
/* socketの終了 */
close(sock); ←ソケット閉じる
}
return 0;
以上。
これでとりあえず通信ができることを確認。強制終了前提のコードなのであくまで参考で。
↓左がC++で右がpython(eclipseのコンソール)。なんとか通信している。
初心者です。
返信削除c++で、ソケット通信を用いて、pythonに数字を送りたいです。どんなプログラムを書けばよいでしょうか?教えていただけると幸いです。