ゲストOSがLinuxのDockerコンテナの中から、ホストOSにデータを放り出す為に使ったUDPプログラム
0.追記(江端が、今、必要な情報をトップに)
### STLって面倒(strcpyでええやん)
// added by Ebata 2021/01/04
#include "simple_udp.h"
// Ebata added simple_udp 2021/01/05
simple_udp udp0("192.168.0.8",12345);
// added by Ebata 2021/01/04
std::cout << "Ebata:User: " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;
std::stringstream ss;
ss << "Ebata:User: " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;
// simple_udp udp0("192.168.0.8",12345);
udp0.udp_send("hello!\n");
//udp0.udp_send(ss); エラー
//udp0.udp_send(ss.c_str()); エラー
udp0.udp_send(ss.str());
1.背景
LinuxのDockerコンテナの中から、一方的に、ホストOSにデータを放り投げる為のUDPプログラムです。
1行程度のデータをコンテナの外に出したいだけなのに、なかなか、良い方法が見つからず、結局UDP socketを使ってしまいました(websocket用にサーバを作るのも大袈裟で面倒でした(3~4行程度で記述したかった))。
ちなみに、UDPは、相手がいようがいまいが、一方的に送信し、一方的に受信待ちができる、とても便利な通信方式です。当然、送達保証はありません。
2. 環境
私のホストOSのIPアドレスは、192.168.0.8、ポートは、12345 と適当に選びました。
普通DockerにはUDPのポートオープンの設定が必要だと思いますが、今回は、テストプログラムでたまたまpingとUDPが通ってしまったので、何もやっていません(運が良かっただけかもしれません)。
テスト用のUDPプログラムは、こちら「UDP送受信プログラム」を使いました(何でも残しておくものです)
3.ソースコード
まるまる、こちらをコピペさせて頂きました → 「メカトロ講座03 c++でudp通信をする方法」
一行でUDP送信ができるようにしたかったもので。
3.1 共通のヘッダファイル(simple_udp.h)
// g++ -std=c++11 としないと動かない(ことがある)
// simple_udp.h
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string> // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)
class simple_udp{
int sock;
struct sockaddr_in addr;
public:
simple_udp(std::string address, int port){
sock = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(address.c_str());
addr.sin_port = htons(port);
}
void udp_send(std::string word){
sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
}
void udp_bind(){
bind(sock, (const struct sockaddr *)&addr, sizeof(addr));
}
std::string udp_recv(){
#define BUFFER_MAX 400
char buf[BUFFER_MAX];
memset(buf, 0, sizeof(buf));
recv(sock, buf, sizeof(buf), 0);
return std::string(buf);
}
void udp_recv(char *buf, int size){
memset(buf, 0, size);
recv(sock, buf, size, 0);
}
~simple_udp(){
close(sock);
}
};
3.2 送信プログラム(udp_sendto.cpp)
// g++ udp_sendto.cpp -o udp_sendto で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
#include <stdio.h>
#include <string.h>
#include "simple_udp.h"
simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側
int main(int argc, char **argv){
udp0.udp_send("hello!");
return 0;
}
3.3 受信プログラム(udp_recvfrom.cpp)
// g++ udp_recvfrom.cpp -o udp_recvfrom で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
#include <stdio.h>
#include <string.h>
#include "simple_udp.h"
simple_udp udp0("0.0.0.0",12345);
int main(int argc, char **argv){
udp0.udp_bind();
while (1){
std::string rdata=udp0.udp_recv();
printf("recv:%s\n", rdata.c_str());
}
return 0;
}
4.Windows10でMinGWのgccを使ってる私の場合のケース
以下のように変更した
4.1 共通のヘッダファイル(simple_udp_win.h)
// simple_udp_win.h
/* Windows版 */
#include <stdio.h>
#include <sys/types.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <sys/socket.h>
//#include <netinet/in.h>
//#include <arpa/inet.h>
//#include <netdb.h>
#include <string> // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)
#include <unistd.h> // error: 'close' was not declared in this scope; did you mean 'fclose'?
class simple_udp{
int sock;
struct sockaddr_in addr;
// Windows専用おまじない(ここから)
WSADATA wsaData;
// Windows専用おまじない(ここまで)
public:
simple_udp(std::string address, int port){
// Windows専用おまじない(ここから)
WSAStartup(MAKEWORD(2,0), &wsaData);
// Windows専用おまじない(ここまで)
sock = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(address.c_str());
addr.sin_port = htons(port);
}
void udp_send(std::string word){
sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
}
void udp_bind(){
bind(sock, (const struct sockaddr *)&addr, sizeof(addr));
}
std::string udp_recv(){
#define BUFFER_MAX 400
char buf[BUFFER_MAX];
memset(buf, 0, sizeof(buf));
recv(sock, buf, sizeof(buf), 0);
return std::string(buf);
}
void udp_recv(char *buf, int size){
memset(buf, 0, size);
recv(sock, buf, size, 0);
}
~simple_udp(){
close(sock);
}
};
4.2 送信プログラム(udp_sendto_win.cpp)
// g++ udp_sendto_win.cpp -o udp_sendto_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// g++ -g udp_sendto_win.cpp -o udp_sendto_win -lwsock32 -lws2_32
#include <stdio.h>
#include <string.h>
//#include "simple_udp.h"
#include "simple_udp_win.h"
//simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側
//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側
simple_udp udp0("127.0.0.1",12345); // ホストOSのUDPの受信側
int main(int argc, char **argv){
udp0.udp_send("hello!");
return 0;
}
4.3 受信プログラム(udp_recvfrom_win.cpp)
// g++ udp_recvfrom_win.cpp -o udp_recvfrom_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// Windows の場合は、g++ -g udp_recvfrom_win.cpp -o udp_recvfrom_win -lwsock32 -lws2_32
#include <stdio.h>
#include <string.h>
#include "simple_udp.h"
//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側
simple_udp udp0("127.0.0.1",12345); int main(int argc, char **argv){ udp0.udp_bind(); while (1){ std::string rdata=udp0.udp_recv(); printf("recv:%s\n", rdata.c_str()); } return 0; }