chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

SwiftでiOSアプリを作る上でGUIよりUDP通信が課題

シンプルなiOSアプリを試作して申請してみたいと思っています。あまりにへなちょこアプリだと門前払いだろうと思い、EchonetLite機器をスキャンするアプリを作ろうと思っています。最初はGUIをSwiftUIで作るかなーと思っていたけど、Swiftによる実装ではUDP通信の方が課題と分かってきた。ライブラリで実現できない場合、Cで書く必要があるらしい。そこでSwiftUIの勉強に着手する前に、再度CでUDP通信をどう実装するかを再確認する。

C言語によるUDPマルチキャスト通信サンプルはいろんな人が実装例を出してくれているので比較的簡単にサンプルが作れる
(参考にした先人の記事は最後につけます)

以下はEchonetLite機器が投げるUDPによるマルチキャストを受信するサンプルコード。修正点は、PORTを3610に変更、マルチキャスト用アドレスを224.0.23.0に変更。前回Pythonで作成したUDPマルチキャストによるINF_REQに対する機器からの応答が受け取れています。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUF_SIZE 128
#define PORT 3610
#define MULTICAST_GROUP "224.0.23.0"
#define LOCAL_ADDR "192.168.10.157"

int main()
{
 int sock,len;
 struct sockaddr_in addr;
 struct ip_mreq mreq;

 unsigned char buf[BUF_SIZE];

 sock = socket(AF_INET, SOCK_DGRAM, 0);

 addr.sin_family = AF_INET;
 addr.sin_port = htons(PORT);
 addr.sin_addr.s_addr = INADDR_ANY;

 bind(sock, (struct sockaddr *)&addr, sizeof(addr));

 /* must be executed setsockopt after bind*/
 memset(&mreq, 0, sizeof(mreq));
 mreq.imr_interface.s_addr = inet_addr(LOCAL_ADDR);
 mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);

 if (setsockopt(sock,
	IPPROTO_IP,
	IP_ADD_MEMBERSHIP,
	(char *)&mreq, sizeof(mreq)) != 0) {
	perror("setsockopt");
	return 1;
 }

 memset(buf, 0, sizeof(buf));

 while(1){

  len =  recv(sock, buf, sizeof(buf), 0);

    printf("---------------\n");
    for(int i=0; i< len; i++){
        printf("[%02X]", buf[i]);
    }
    printf("\n");
    printf("---------------\n");


 }

 close(sock);
 return 0;

}

/*
output sample
[10][81][00][02][05][FF][01][0E][F0][01][63][01][D6][00]
[10][81][00][02][0E][F0][01][05][FF][01][73][01][D6][07][02][05][FF][01][01][30][09]
[10][81][00][74][0E][F0][01][0E][F0][01][62][02][D6][00][8A][00]
*/

以下はUDPマルチキャストを投げるサンプル。試作中のため電文はまだEchonetLiteになっていません。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define BUF_SIZE 128
#define PORT 3610
#define MULTICAST_GROUP "224.0.23.0"
#define LOCAL_ADDR "192.168.10.157"


int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in addr;
  struct sockaddr_in src_addr;
  in_addr_t ip_addr;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP);

  ip_addr = inet_addr(LOCAL_ADDR);
  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ip_addr, sizeof(ip_addr)) != 0) {
    perror("setsockopt");
    return 1;
  }

  if(sendto(sock, "TEST", 4, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1){
    perror("sendto");
    return 1;
  }
  close(sock);
  return 0;
}

送信はまだEchonetLite電文になっていないが、、だいたいできたので、次はSwiftでUDPマルチキャストが実装できるのかを調べて作ってみる。スマフォでEchonetLite機器を探せたらそれなりに面白いと思うのだが。。もしEchonetLite対応機器が見つかると、対象の機器に向かって電文を送ることで機器制御ができる。

■ご参考URL
UDPマルチキャストについて参考にした記事。。記事提供ありがとうございます。おかげ様で試作コードが一発で動きました。
マルチキャストパケットの送受信方法 - Qiita
UDPでマルチキャストを使う(マルチキャスト受信):Geekなぺーじ

SwiftによるUDPマルチキャスト受信ライブラリ
GitHub - matzpersson/swift-udp: Swift UDP Broadcast receiver. Great for notifying Swift IOS app when server data changes.


[iOS 12]Network FrameworkでUDPソケット通信 - Qiita
NetworkFrameworkを使うらしいです

Network framework UDP broadcast | Apple Developer Forums
NetworkFrameworkではUDPマルチキャストが動かないといった記事も。。
Network.framework enhancement: sup… | Apple Developer Forums
2018年にはサポートされたという記事も。。