自社の製品でUDPを使ってRequest/Responseのデータを返す機器を採用することになったのだが、RequestのSourcePortにResponseを返す作りになっていたので、その時の実装をメモ。
こことここをベースにサンプルを作成。エラー処理も同様に割愛。
要は送受信のsocketを共用にして、bindする前にPortを指定しなければいい話。
サンプルコードは送信は1回だけ。★が今回書きたかったところ。
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main() { int sock; struct sockaddr_in recvaddr; fd_set fds, readfds; char buf[2048]; /* 受信ソケットを2つ作ります */ sock = socket(AF_INET, SOCK_DGRAM, 0); recvaddr.sin_family = AF_INET; recvaddr.sin_addr.s_addr = INADDR_ANY; // ★recvaddrにポートを指定しない bind(sock, (struct sockaddr *)&recvaddr, sizeof(recvaddr)); /* fd_setの初期化します */ FD_ZERO(&readfds); FD_SET(sock, &readfds); /* 送信処理 意味をはっきり分けるためブロック化 */ { struct sockaddr_in sendaddr; sendaddr.sin_family = AF_INET; /* 宛先ポート */ sendaddr.sin_port = htons(12345); /* 宛先IP */ sendaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // ★受信socketを使用して送信 sendto(sock, "HELLO", 5, 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr)); } /* 無限ループです このサンプルでは、この無限ループを抜けません */ while (1) { /* 読み込み用fd_setの初期化 */ /* selectが毎回内容を上書きしてしまうので、毎回初期化します */ memcpy(&fds, &readfds, sizeof(fd_set)); /* fdsに設定されたソケットが読み込み可能になるまで待ちます */ /* 一つ目の引数はファイルディスクリプタの最大値+1にします */ select(sock+1, &fds, NULL, NULL, NULL); /* sockに読み込み可能データがある場合 */ if (FD_ISSET(sock, &fds)) { /* sockからデータを受信して表示します */ memset(buf, 0, sizeof(buf)); recv(sock, buf, sizeof(buf), 0); printf("%s\n", buf); } } /* このサンプルでは、ここへは到達しません */ close(sock); return 0; }
個人的にRequest/Responseの形にするならTCPを使ったほうがいいと思うし、UDPでSourcePortに送るって仕組みがなんかしっくりこない。
UDPならそれぞれ決めたDestinationPortでやり取りするのが自然な気がする。
ともあれ久々に生のUDPを叩いた記念のメモでした。
Comments are closed here.