7

SSDP プロトコルを実装しようとしていますが、それがどのように機能するのか正確にはわかりません。SSDP は udp 経由でデータを送信します。これは明らかです。コントローラがネットワークに接続すると、マルチキャスト アドレス 239.255.255.250:1900 に送信できる MSEARCH メッセージでデバイスを検索できます。すべてのデバイスは、このアドレスをリッスンして応答する必要があります。しかし、彼らがどのように反応するかはわかりません。彼らがユニキャストで応答することをwiresharkで見ていますが、応答を受信するポートを特定する方法がわかりません。

編集 - - - - - - - -

スパイク ファジング フレームワークを使用して ssdp ファザーを作成しようとしています。私が言ったように、正しいデータを送信できますが、応答を受信できません。簡単な説明とともに、いくつかのスパイク コードを貼り付けてみます。送信するデータを表す Spike 構造体があります (実際のデータ、サイズ、プロトコル情報を格納します...)。より明確にするために、いくつかの変数を削除しました。

struct spike {

  /*total size of all data*/
  unsigned long datasize;
  unsigned char *databuf;
  unsigned char *endbuf;
  int fd; /*for holding socket or file information*/
  int proto; /*1 for tcp, 2 for udp*/
  struct sockaddr_in *destsockaddr;
};

今、私はudp経由でデータを送信しており、次の関数でいくつかの応答を受け取りたいです

spike_connect_udp(target,port);
spike_send();
s_read_packet(); 

関数の実装:

int 
spike_connect_udp(char * host, int port)
{
  int fd;
  /*ahh, having udpstuff.c makes this stuff easy*/
  fd=udpconnect(host,port);
  if (fd==-1)
    {
      fprintf(stderr,"Couldn't udp connect to target\n");
      return (0);
    }
  current_spike->fd=fd;
  current_spike->proto=2; /*UDP*/
  return 1;
}

int
udpconnect(const char * host,  const unsigned short port )
{
  int sfd = -1;
  struct sockaddr_in addr;
  /* Translate hostname from DNS or IP-address form */

  memset(&addr, 0, sizeof(addr));
  if (!getHostAddress(host, &addr))
    {
      hdebug("can't resolve host or address.\n");
      return -1;
    }
  addr.sin_family = AF_INET;
  addr.sin_port = ntohs(port);

  if ((sfd = socket(AF_INET,  SOCK_DGRAM, 0)) < 0)
    {
      hdebug("Could not create socket!\n");
      return -1;
    }

  /* Now connect! */

  if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
      close(sfd);
      return -1;
    }

  return sfd;

}

int
spike_send()
{
  int retval;

  switch (current_spike->proto) 
    {
    case 1: /*TCP*/
    //deleted, doesnt matter, i am sending via udp
    case 2: /*UDP*/
      //udp_write_data is function from framework
      retval=udp_write_data(current_spike->fd, current_spike->destsockaddr, s_get_size(), s_get_databuf());
      break;

    }

  fflush(0);

  return retval;
}

これは正常に機能し、udp 経由でデータを送信します。ここで、開いているソケット current_spike->fd を介していくつかの応答を受け取りたいと考えています。関数 s_read_packet void

s_read_packet()
{
  unsigned char buffer[5000];
  int i;
  int size;
  s_fd_wait();
  printf("Reading packet\n");
  memset(buffer,0x00,sizeof(buffer));
  /what alarm and fcntl does?
  alarm(1);
  fcntl(current_spike->fd, F_SETFL, O_NONBLOCK);
  //this read return error -1 and sets errno to 11 service temporarily unavailable
  size=read(current_spike->fd,buffer,1500);
  fcntl(current_spike->fd, F_SETFL, 0);
  alarm(0);

  for (i=0; i<size; i++)
    {
      if (isprint(buffer[i]))
    printf("%c",buffer[i]);
      else
    printf("[%2.2x]",buffer[i]);
    }

  printf("\nDone with read\n");
}

int
s_fd_wait()
{
  /*this function does a select to wait for
    input on the fd, and if there
    is, returns 1, else 0 */
  int fd;
  fd_set rfds;
  struct timeval tv;
  int retval;

  fd=current_spike->fd;

  /* Watch server_fd (fd 0) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(fd, &rfds);
  /* Wait up to zero seconds  .  will this wait forever? not on linux.*/

  /* from man page: timeout is an upper bound on the amount of time
       elapsed before select returns. It may be zero, causing select
       to return immediately.  If timeout is NULL (no timeout), select
       can block indefinitely. */

  /*wait 2 seconds only*/
  tv.tv_sec = TIMEINSECONDS;
  tv.tv_usec = TIMEINUSECONDS;
  //printf("Before select %d:%d\n",TIMEINSECONDS,TIMEINUSECONDS);
  retval = select(fd+1, &rfds, NULL, NULL, &tv);
  /* Don't rely on the value of tv now! */
  //printf("After select retval=%d.\n",retval);
  switch (retval)
   {
     case 0:
       /*Timeout - no packet or keypress*/
        return(0);
        break;
     case -1:
      /* ignore interrupted system calls */
       if (errno != EINTR)
         {
           /*some kind of weird select error. Die. */
           exit(-1);
         }
      /*otherwise we got interrupted, so just return false*/
       return (0);
       break;
     default:
         {
           if (FD_ISSET(fd,&rfds))
             return (1);
           else
            return (0);
         }
   }
} 

しかし、関数 s_read_packet はデータを生成しません...

4

2 に答える 2

11

SSDP を実装するには、アプリケーションが指定されたマルチキャスト アドレスにNOTIFYandM-SEARCHヘッダーを送信できる必要があり、これらのメッセージも受信できる必要があります。そのためには、専用の UDP ソケットを作成する必要があります。

このようなソケットを初期化する方法の例を次に示します。

// Structs needed
struct in_addr localInterface;
struct sockaddr_in groupSock;
struct sockaddr_in localSock;
struct ip_mreq group;   

// Create the Socket
int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

// Enable SO_REUSEADDR to allow multiple instances of this application to receive copies of the multicast datagrams.
int reuse = 1;

setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));

// Initialize the group sockaddr structure with a group address of 239.255.255.250 and port 1900.
memset((char *) &groupSock, 0, sizeof(groupSock));

groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("239.255.255.250");
groupSock.sin_port = htons(1900);

// Disable loopback so you do not receive your own datagrams.
char loopch = 0;

setsockopt(udpSocket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch));

// Set local interface for outbound multicast datagrams. The IP address specified must be associated with a local, multicast capable interface.
localInterface.s_addr = inet_addr("192.168.0.1");

setsockopt(udpSocket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface));

// Bind to the proper port number with the IP address specified as INADDR_ANY.
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(1900);
localSock.sin_addr.s_addr = INADDR_ANY;

bind(udpSocket, (struct sockaddr*)&localSock, sizeof(localSock));

// Join the multicast group on the local interface. Note that this IP_ADD_MEMBERSHIP option must be called for each local interface over which the multicast datagrams are to be received.
group.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
group.imr_interface.s_addr = inet_addr("192.168.0.1");

setsockopt(udpSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group));

これで、このソケットを使用してデータをマルチキャスト グループに送信できます。

sendto(udpSocket, message, message_length, 0, (struct sockaddr*)&groupSock, sizeof(groupSock));

メッセージを受信するには:

struct sockaddr_in si_other;
socklen_t slen = sizeof(si_other);
char buffer[1024];

recvfrom(udpSocket, buffer, 1024, 0, (struct sockaddr *) &si_other, &slen);

特定のリクエスト (上記のように受信) に応答するには、次のようにします。

sendto(udpSocket, message, message_length, 0, (struct sockaddr*)&si_other, sizeof(si_other));

あとは、必要なメッセージを作成して送信し、受信したデータを処理するだけです。M-SEARCH(前述のように) マルチキャスト グループに要求を送信すると、各デバイスから次のような応答が返されるとします。

HTTP/1.1 200 OK
SERVER: Linux/2.6.15.2 UPnP/1.0 Mediaserver/1.0
CACHE-CONTROL: max-age=1200
LOCATION: http://192.168.0.223:5001/description.xml 
ST: urn:schemas-upnp-org:device:MediaServer:4
USN: uuid:550e8400-e29b-11d4-a716-446655440000::urn:schemas-upnp-org:device:MediaServer:4
Content-Length: 0
EXT:
于 2014-12-19T13:00:10.307 に答える
6

質問は、TCP/UDP 通信の一般原則に関するものであり、SSDP の詳細に関するものではありません。UDP ネットワーク クライアントとしてのコントローラーが特定のリモート アドレス (マルチキャストかユニキャストかは関係ありません) へのソケットを開く場合、ローカル アドレスは、OS によって割り当てられた適用可能なローカル ネットワーク アダプター アドレスとポート番号です。ランダムに見えますが、OS は慎重に割り当てて、同じネットワーク アダプターを使用するすべてのアプリケーションの一意性を管理します。Wireshark では、次のように表示されます。

IP, Src: 192.168.1.40 Dst: 239.255.255.250
UDP, Src Port: 42578 Dst Port: 1900

192.168.1.40コントローラーの (発信) ネットワーク アドレスはどこにありますか。デバイスは に応答する必要があり192.168.1.40:42578ます。UDP/IP スタックの実装により、そのタプルが提供されます。

UPnP デバイス アーキテクチャ ドキュメントを読むことをお勧めします。第 1 章は SSDP に関するすべてであり、まさにあなたが求めている部分である、発見、広告、および検索に関するものです。

コード追加後の編集:

「サーバー」コードは表示されませんbind()。送信と受信に同じ記述子を使用しようとしています。さらに、接続されread()たリソースの汎用 POSIX 関数である which を使用しています(記述子が永続的である場合)。一般に、TCP クライアントの例を取り上げて、プロトコルを変更しただけのように見えます。UDP では機能しません。UDP パケットを受信するには、お客様側でサーバーをセットアップする必要があります。UDP は、TCP と同様に、あるパケットが他のパケットへの「応答」であるかどうかを知りません。UDP はコネクションレスですが、それはすでに知っているはずです。

一般的なUDPの原則を読んで、UDP(ランダムで見栄えの良い開始点)に非常に単純なエコーサーバーを実装してから、SSDPマルチキャストを積み上げることをお勧めします。

于 2012-11-14T22:13:03.437 に答える