自社の製品で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.