socatで装置内通信のデータをのぞき見る
socatを使ってUNIXドメイン通信を中継し通信内容をのぞいてみる。
構成のイメージは下記。
-------------------------------------------------- ________ ________ ________ | | | | | | | Client |--->| socat |--->| Sever | |________| |________| |________| | ---> 標準出力に通信内容を表示。 --------------------------------------------------
プログラム
local.h
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define MSGID_HELLO_REQUEST (0x0101) #define MAX_USER (1) typedef enum { MSG_KIND_REQUEST = 0, MSG_KIND_MAX } msgkind_t; typedef struct { uint32_t id; uint16_t kind; uint16_t seqno; } msg_header_t;
client.c
#include "local.h" #define UNIX_SOCKET_FILEPATH "./usocket.client" int main(void) { int sfd = -1; struct sockaddr_un addr = { .sun_family = AF_UNIX, .sun_path = UNIX_SOCKET_FILEPATH, }; sfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sfd < 0) { perror("socket"); exit(EXIT_FAILURE); } int rc = connect(sfd, (struct sockaddr* )&addr, sizeof(addr)); if (rc < 0) { perror("connect"); close(sfd); exit(EXIT_FAILURE); } uint16_t seqno = 0; while(1) { msg_header_t req = { .id = MSGID_HELLO_REQUEST, .kind = MSG_KIND_REQUEST, .seqno = seqno, }; ssize_t ss = send(sfd, (void*)&req, sizeof(req), 0); if (ss < 0) { perror("send"); close(sfd); break; } if (ss == 0 || ss < 0) { printf("connection closed.\n"); unlink(UNIX_SOCKET_FILEPATH); break; } printf("sent request, wait 3 sec (-.-)zzZ\n"); sleep(3); seqno++; } return 0; }
server.c
#include "local.h" #define UNIX_SOCKET_FILEPATH "./usocket.server" int main(void) { int sfd = -1; struct sockaddr_un addr = { .sun_family = AF_UNIX, .sun_path = UNIX_SOCKET_FILEPATH, }; sfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sfd < 0) { perror("socket"); exit(EXIT_FAILURE); } unlink(UNIX_SOCKET_FILEPATH); int rc = bind(sfd, (struct sockaddr* )&addr, sizeof(addr)); if (rc < 0) { perror("bind"); close(sfd); exit(EXIT_FAILURE); } rc = listen(sfd, MAX_USER); if (rc < 0) { perror("listen"); close(sfd); exit(EXIT_FAILURE); } socklen_t addrlen; struct sockaddr_un caddr; int acsfd = accept(sfd, (struct sockaddr *)&caddr, &addrlen); if (acsfd < 0) { perror("accept"); close(sfd); exit(EXIT_FAILURE); } while(1) { uint16_t seqno = 0; msg_header_t req; ssize_t rs = recv(acsfd, (void*)&req, sizeof(req), 0); if (rs < 0) { perror("recv"); close(sfd); break; } printf("recv size=%d, id=%#x, kind=%#x, seqno=%d\n", rs, req.id, req.kind, req.seqno); if (rs == 0) { printf("connection closed.\n"); unlink(UNIX_SOCKET_FILEPATH); break; } } return 0; }
Ncursesでスクリーン制御
Ncurses(*1)を使ってスクリーン制御してみる。
(*1)
CUIでスクリーン、キー入力、カーソルなどの制御機能を提供するライブラリのこと。
make menuconfig とか実行すると出てくるアレを実現できる。
ソースコード
sample.c
#include <curses.h> #include <stdlib.h> #define ESCAPE 27 typedef enum { COLOR_TYPE_INVALID = 0, COLOR_TYPE_BACK_GROUND = 1 } color_type_t; void init_curses() { initscr(); start_color(); init_pair(COLOR_TYPE_BACK_GROUND, COLOR_RED, COLOR_BLACK); curs_set(0); noecho(); keypad(stdscr,TRUE); } int main() { init_curses(); bkgd(COLOR_PAIR(COLOR_TYPE_BACK_GROUND)); move(2,1); printw("Press ESC key, you will exit."); refresh(); int key; while(1) { int key = getch(); if (key == ESCAPE) { break; } refresh(); }; endwin(); return 0; }
マウスの座標を表示する
Javascriptでマウスの座標(左上端からのX,Y距離)を表示する。
ソースコード
mouse.html
<HTML> <HEAD> <TITLE> マウス座標 </TITLE> </HEAD> <BODY> マウス座標 <br> (X,Y) = <input type="text" style="width:50;" id="global_x">, <input type="text" style="width:50;" id="global_y"> <br> <SCRIPT type="text/javaSCRIPT"> (function (){ // スクロール位置取得 function GetScrollPosition(obj){ return{ x:obj.body.scrollLeft || obj.documentElement.scrollLeft, y:obj.body.scrollTop || obj.documentElement.scrollTop }; } // マウス追従 function TailingMouseMotion(e){ var scrlpos = GetScrollPosition(document); var position; // X座標計算 position = document.getElementById("global_x"); position.value = e.clientX + scrlpos.x; // Y座標計算 position = document.getElementById("global_y"); position.value = e.clientY + scrlpos.y; } // イベント処理 if(document.addEventListener){ document.addEventListener("mousemove" , TailingMouseMotion); }else if(document.attachEvent){ document.attachEvent("onmousemove" , TailingMouseMotion); } })(); </SCRIPT> </BODY> </HTML>
実行結果
次回は適当なgif画像を追従させるようにしたい。
Windows-Linux間をTCPで通信する
TCPを使ってWindowsとLinux間で通信してみる。
具体的にはWindows上にVirtualboxで仮想マシンを作成しHost-Only-Adapter経由で通信する。
構成
- サーバ : Linux CentOS 6, X86_64(64bit) on Virtualbox
- クライアント : Windows7 (32bit)
プログラム作成
クライアント側
実行バイナリはVisual Studio 2017 で作成。
TCP_Client.cpp
#include "stdafx.h" #include <stdio.h> #include <winsock2.h> #define PORT_NO_SERVER (12345) #define IP_ADDRESS_SERVER "192.168.100.254" int main(void) { WSADATA wsaData; struct sockaddr_in server; SOCKET sfd = -1; char buf[32] = { 0 }; int rc = 0; WSAStartup(MAKEWORD(2, 0), &wsaData); sfd = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_port = htons(PORT_NO_SERVER); server.sin_addr.S_un.S_addr = inet_addr(IP_ADDRESS_SERVER); rc = connect(sfd, (struct sockaddr *)&server, sizeof(server)); if (rc < 0) { printf("connect() failed(%d)\n", rc); exit(EXIT_FAILURE); } while (1) { memset(buf, 0, sizeof(buf)); int rs = recv(sfd, buf, sizeof(buf), 0); printf("recv %d byte, %s\n", rs, buf); } WSACleanup(); return 0; }
プロジェクトプロパティから入力にws2_32.libを追記してビルドする。
Windows側のIPアドレスを 192.168.100.0/24のネットワークに設定しておく。
C:> ipconfig Windows IP 構成 イーサネット アダプター VirtualBox Host-Only Network: 接続固有の DNS サフィックス . . . : IPv4 アドレス . . . . . . . . . . : 192.168.100.1 サブネット マスク . . . . . . . . : 255.255.255.0 デフォルト ゲートウェイ . . . . . :
サーバ側
tcp_server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #define MAX_CLIENTS (1) #define MSGBUF_SIZE (1024) int main(void) { int sfd = -1; struct sockaddr_in client; int socklen = sizeof(client); int ac_sfd = -1; int rc = 0; struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(12345), .sin_addr = { .s_addr = INADDR_ANY, }, }; /* ソケットの作成 */ sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd < 0) { perror("socket"); goto error_end; } rc = bind(sfd, (struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) { perror("bind"); goto close_sfd_end; } rc = listen(sfd, MAX_CLIENTS); if (rc < 0) { perror("listen"); goto close_sfd_end; } /* 要求受付 */ ac_sfd = accept(sfd, (struct sockaddr *)&client, &socklen); if (ac_sfd < 0) { perror("accept"); goto close_sfd_end; } while (1) { /* メッセージ送信 */ char msgbuf[MSGBUF_SIZE] = "HELLO WORLD!"; ssize_t ws = write(ac_sfd, msgbuf, strlen(msgbuf)); if (ws < 0) { perror("write"); goto close_all_end; } sleep(1); } close_all_end: close(ac_sfd); close_sfd_end: close(sfd); error_end: return 0; }
ソースコードのビルド
$ gcc tcp_server.c -o tcp_server $
こちらも通信インタフェースに同様のネットワークのIPアドレスを設定しておく。
$ ip -a addr 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 08:00:27:8c:e9:83 brd ff:ff:ff:ff:ff:ff inet 192.168.100.254/24 brd 192.168.100.255 scope global enp0s8 valid_lft forever preferred_lft forever
実行結果
確かにサーバが送信した文字列をクライアントで受け取れている。
キャプチャを見ても確かに3WAYハンドシェイクからデータ送信まで行っている。
(ただし、クライアント→サーバのキャプチャは見えてない。理由不明。)
Qt5で始めるGUIプログラミング
Qt5を使ってGUIプログラミングに挑戦してみる。
まずはウィンドウを出すだけ。
Linuxサーバでアプリケーションを作成&実行し、表示はWindowsで行うスタイル。
実行環境
ホスト側
- Windows7 32bit
- Xming 6.9.0.31
- Teraterm 4.9.2
サーバ側(実行側)
- CentOS 6, x86_64 (64bit)
- Qt 5.6.2
事前準備
ホスト側
下記アプリケーションをインストールする。
Xmingの起動、TeratermのSSH転送設定
インストール後、Xmingを起動しTeratermは下記設定を行う。
その後、一度Teratermを再起動して再度サーバにログインする。
サーバ側
Qt Downloads から実行環境に応じたバイナリをダウンロードする。今回はqt-opensource-linux-x64-5.6.2.runを選択。
$ chmod +x qt-opensource-linux-x64-5.6.2.run $ ./qt-opensource-linux-x64-5.6.2.run
すると以下の画面が表示されるので必要事項を入力してインストール実行。
インストール場所の変更
デフォルトだとhome配下にインストールされるのでパスを変更する。
(インストール時に指定可能だがrootユーザor sudo実行では画面転送が上手くいかない場合がある)
/usr/local/shareへ変更
$ sudo mv Qt5.6.2 /usr/local/share
ld.so.confにライブラリパスを追記
$ sudo vi /etc/ld.so.conf #/usr/local/share/Qt5.6.2/5.6/gcc_64/lib/ を追記 $ $ sudo ldconfig #追記したパスを反映 $
~/.bashrcにqmakeのパスを追記
$ vi ~/.bashrc # 下記行を追記 # export QT_ROOT="/usr/local/share/Qt5.6.2/5.6/" # export PATH="$QT_ROOT/gcc_64/bin:$PATH" $ $ source ~/.bashrc #追記したパスを反映 $
プログラム作成
事前準備が終ったのでプログラムを作っていく。
sample.cpp
#include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel *label = new QLabel("Hello Qt!"); label->show(); return app.exec(); }
Makefileを生成する
$ qmake -project $ qmake qt.pro
ここでmakeを実行するとコンパイルエラーが発生。
$ make g++ -c -pipe -O2 -std=gnu++0x -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -I. -I. -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include/QtGui -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include/QtCore -I. -I/usr/local/share/Qt5.6.2/5.6/gcc_64/mkspecs/linux-g++ -o sample.o sample.cpp sample.cpp:1:24: error: QApplication: No such file or directory sample.cpp:2:18: error: QLabel: No such file or directory sample.cpp: In function 'int main(int, char**)': sample.cpp:6: error: 'QApplication' was not declared in this scope sample.cpp:6: error: expected ';' before 'app' sample.cpp:7: error: 'QLabel' was not declared in this scope sample.cpp:7: error: 'label' was not declared in this scope sample.cpp:7: error: expected type-specifier before 'QLabel' sample.cpp:7: error: expected ';' before 'QLabel' sample.cpp:11: error: 'app' was not declared in this scope sample.cpp: At global scope: sample.cpp:4: warning: unused parameter 'argc' sample.cpp:4: warning: unused parameter 'argv'
こちらによるとqt.proに下記行を追加すれば良いらしい。
$ vi qt.pro TEMPLATE = app TARGET = qt INCLUDEPATH += . QT += widgets # この行を追記 # Input SOURCES += sample.cpp
再度ビルド実行
$ make /usr/local/share/Qt5.6.2/5.6/gcc_64/bin/qmake -o Makefile qt.pro g++ -c -pipe -O2 -std=gnu++0x -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I. -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include/QtWidgets -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include/QtGui -I/usr/local/share/Qt5.6.2/5.6/gcc_64/include/QtCore -I. -I/usr/local/share/Qt5.6.2/5.6/gcc_64/mkspecs/linux-g++ -o sample.o sample.cpp $
コンパイルが成功し実行するが謎のエラーが発生。
$ ./qt This application failed to start because it could not find or load the Qt platform plugin "xcb" in "". Reinstalling the application may fix this problem. Aborted
こちらによると環境変数 "QT_PLUGIN_PATH"を追加すれば良いらしい。
(stackoverflowさん、いつもありがとう)
$ vi ~/.bashrc # export QT_PLUGIN_PATH="$QT_ROOT/gcc_64/plugins" を追記 $
実行結果
$ ./qt
Windows上に下記画面が表示されれば成功。
シグナル受信をepollで監視
Linuxのsignalfdを使ってシグナルをepollで監視する。
signalfd.c
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/epoll.h> #include <sys/signalfd.h> #include <errno.h> #define MAX_EVENTS 10 #define RET_OK (0) #define RET_NG (-1) #define PERROR(X) \ {\ char __strerr[128] = {0};\ int errcode = errno;\ \ strerror_r(errcode, __strerr, sizeof(__strerr));\ printf(X " failed(%d:%s)\n", errcode, __strerr);\ } static int createSignalFd(int* sfd); static int waitSignalEvent(int sfd); int main(void) { int sfd = -1; int ret = RET_OK; do { ret = createSignalFd(&sfd); if (RET_OK != ret) { printf("createSignalFd() failed(%d)\n", ret); break; } ret = waitSignalEvent(sfd); if (RET_OK != ret) { printf("waitSignalEvent"); close(sfd); break; } close(sfd); } while(0); return 0; } static int createSignalFd(int* sfd) { int ret = RET_OK; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); do { if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { PERROR("sigprocmask()"); break; } *sfd = signalfd(-1, &mask, 0); if (*sfd < 0) { PERROR("signalfd()"); ret = RET_NG; break; } } while(0); return ret; } static int waitSignalEvent(int sfd) { int ret = RET_OK; int rc = 0; int epollfd = -1; int nfds = -1; struct epoll_event events[MAX_EVENTS]; struct epoll_event evt = { .events = EPOLLIN, .data = { .fd = sfd, }, }; epollfd = epoll_create(MAX_EVENTS); if (epollfd < 0) { PERROR("epoll_create()"); goto error_end; } rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &evt); if (rc != 0) { PERROR("epoll_ctl()"); close(epollfd); goto error_end; } for(;;) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds < 0) { PERROR("epoll_wait()"); close(epollfd); break; } int fd = 0; for (fd = 0; fd < MAX_EVENTS; fd++) { if (events[fd].data.fd == sfd) { struct signalfd_siginfo fdsi; ssize_t sz = read(sfd, &fdsi, sizeof(fdsi)); if (sz < 0) { PERROR("read()"); close(epollfd); break; } printf("ssi_signo = %d\n", fdsi.ssi_signo); } } } close(epollfd); error_end: return ret; }
実行結果
$ gcc -std=gnu99 signalfd.c -o signalfd $ ./signalfd ssi_signo = 1 ssi_signo = 2 ssi_signo = 3 Terminated #別端末から $ killall -SIGHUP signalfd $ killall -SIGINT signalfd $ killall -SIGQUIT signalfd $ killall -SIGTERM signalfd
TAILQでリスト管理
TAILQを使ってリストを実現する。
詳細はMan page of QUEUE参照。
tail.c
#include <stdio.h> #include <stdlib.h> #include <sys/queue.h> typedef struct memEntry { TAILQ_ENTRY(memEntry) entry; int32_t size; void* ptr; } memEntry_t; typedef struct { TAILQ_HEAD(tq_head, memEntry) head; int entry_count; } mngMemPool_t; static mngMemPool_t mngMem; void* allocateMemory(size_t size); void printAllMemory(void); void freeAllMemory(void); int main(void) { TAILQ_INIT(&mngMem.head); for (int i = 0; i < 10; i++) { void* ptr = allocateMemory(i * sizeof(int)); } printAllMemory(); freeAllMemory(); return 0; } void* allocateMemory(size_t size) { memEntry_t* ent; ent = malloc(sizeof(memEntry_t)); if (NULL == ent) { printf("out-of-memory"); exit(EXIT_FAILURE); } ent->ptr = malloc(size); if (NULL == ent->ptr) { free(ent); printf("out-of-memory"); exit(EXIT_FAILURE); } ent->size = size; TAILQ_INSERT_TAIL(&mngMem.head, ent, entry); return ent->ptr; } void printAllMemory(void) { memEntry_t* np = NULL; for (np = mngMem.head.tqh_first; np != NULL; np = np->entry.tqe_next) { printf("size=%d\n", np->size); } } void freeAllMemory(void) { while (mngMem.head.tqh_first != NULL) { memEntry_t* np = mngMem.head.tqh_first; TAILQ_REMOVE(&mngMem.head, np, entry); free(np->ptr); free(np); } }
実行結果
$ gcc -std=gnu99 tailq.c -o tailq $ ./tailq size=0 size=4 size=8 size=12 size=16 size=20 size=24 size=28 size=32 size=36