2015-02-11 43 views
1

我最近正在基於usrsctp的項目工作。usrsctp數據收到回調參數值變成廢話

當創建一個新的SCTP套接字時,可以指定一個回調函數,當新數據可用時將會調用該函數,如下面的代碼所示。

創建一個新的SCTP套接字:

struct socket *s = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, 
            sctp_data_received_cb, NULL, 0, sctp); 

回調函數:

static int 
sctp_data_received_cb(struct socket *sock, union sctp_sockstore addr, void *data, 
        size_t len, struct sctp_rcvinfo recv_info, int flags, void *user_data) 
{ 
    struct sctp_transport *sctp = (struct sctp_transport *)user_data; 
    if (sctp == NULL || len == 0) 
    return -1; 

    fprintf(stdout, "Data of length %u received on stream %u with SSN %u, TSN %u, PPID %u\n", 
      (uint32_t)len, 
      recv_info.rcv_sid, 
      recv_info.rcv_ssn, 
      recv_info.rcv_tsn, 
      ntohl(recv_info.rcv_ppid)); 

    if (flags & MSG_NOTIFICATION) 
    handle_notification_message(sctp, (union sctp_notification *)data, len); 
    else 
    handle_rtcdc_message(sctp, data, len, ntohl(recv_info.rcv_ppid), recv_info.rcv_sid); 

    free(data); 
    return 0; 
} 

這個回調函數將被正常調用,但是它的參數值都只是無稽之談。上面的代碼的輸出是等

Data of length 675381504 received on stream 31504 with SSN 34835, TSN 32651, PPID 8470824

應該一直喜歡

Data of length 18 received on stream 0 with SSN 0, TSN 4117987333, PPID 50

我讀usrsctp的源代碼,並發現其中的回調被調用:

if (control->spec_flags & M_NOTIFICATION) { 
    flags |= MSG_NOTIFICATION; 
} 
inp->recv_callback(so, addr, buffer, control->length, rcv, flags, inp->ulp_info); 
SCTP_TCB_LOCK(stcb); 

將其更改爲以下代碼並重新編譯庫

if (control->spec_flags & M_NOTIFICATION) { 
    flags |= MSG_NOTIFICATION; 
} 
fprintf(stdout, "[LIB] Data of length %u received on stream %u with SSN %u, TSN %u, PPID %u\n", 
     control->length, 
     rcv.rcv_sid, 
     rcv.rcv_ssn, 
     rcv.rcv_tsn, 
     ntohl(rcv.rcv_ppid)); 
inp->recv_callback(so, addr, buffer, control->length, rcv, flags, inp->ulp_info); 
SCTP_TCB_LOCK(stcb); 

我能得到預期的輸出:

[LIB] Data of length 18 received on stream 0 with SSN 0, TSN 4117987333, PPID 50

爲什麼參數值成爲回調函數廢話?

我發現了一個類似的問題here,但無法理解它的答案。我很肯定這是同一個問題。

[UPDATE1]

usrsctp_socket的usrsctp.h原型:

struct socket * 
usrsctp_socket(int domain, int type, int protocol, 
      int (*receive_cb)(struct socket *sock, union sctp_sockstore addr, void *data, 
          size_t datalen, struct sctp_rcvinfo, int flags, void *ulp_info), 
      int (*send_cb)(struct socket *sock, uint32_t sb_free), 
      uint32_t sb_threshold, 
      void *ulp_info); 

[UPDATE2]

我敢肯定,需要在the old similar thread提示沒有額外的技巧,因爲我在official examples中看到沒有奇怪的鑄件,他們只是運作良好。

例如echo_server.c:

static int 
receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, 
      size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) 
{ 
    char namebuf[INET6_ADDRSTRLEN]; 
    const char *name; 
    uint16_t port; 

    if (data) { 
    if (flags & MSG_NOTIFICATION) { 
     printf("Notification of length %d received.\n", (int)datalen); 
    } else { 
     switch (addr.sa.sa_family) { 
#ifdef INET 
     case AF_INET: 
     name = inet_ntop(AF_INET, &addr.sin.sin_addr, namebuf, INET_ADDRSTRLEN); 
     port = ntohs(addr.sin.sin_port); 
     break; 
#endif 
#ifdef INET6 
     case AF_INET6: 
     name = inet_ntop(AF_INET6, &addr.sin6.sin6_addr, namebuf, INET6_ADDRSTRLEN), 
     port = ntohs(addr.sin6.sin6_port); 
     break; 
#endif 
     case AF_CONN: 
#ifdef _WIN32 
     _snprintf(namebuf, INET6_ADDRSTRLEN, "%p", addr.sconn.sconn_addr); 
#else 
     snprintf(namebuf, INET6_ADDRSTRLEN, "%p", addr.sconn.sconn_addr); 
#endif 
     name = namebuf; 
     port = ntohs(addr.sconn.sconn_port); 
     break; 
     default: 
     name = NULL; 
     port = 0; 
     break; 
     } 
     printf("Msg of length %d received from %s:%u on stream %d with SSN %u and TSN %u, PPID %d, context %u.\n", 
      (int)datalen, 
      name, 
      port, 
      rcv.rcv_sid, 
      rcv.rcv_ssn, 
      rcv.rcv_tsn, 
      ntohl(rcv.rcv_ppid), 
      rcv.rcv_context); 
     if (flags & MSG_EOR) { 
     struct sctp_sndinfo snd_info; 

     snd_info.snd_sid = rcv.rcv_sid; 
     snd_info.snd_flags = 0; 
     if (rcv.rcv_flags & SCTP_UNORDERED) { 
      snd_info.snd_flags |= SCTP_UNORDERED; 
     } 
     snd_info.snd_ppid = rcv.rcv_ppid; 
     snd_info.snd_context = 0; 
     snd_info.snd_assoc_id = rcv.rcv_assoc_id; 
     if (usrsctp_sendv(sock, data, datalen, NULL, 0, &snd_info, sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) { 
      perror("sctp_sendv"); 
     } 
     } 
    } 
    free(data); 
    } 
    return (1); 
} 
+0

我不這麼認爲,我已經將usrsctp_socket的原型添加到了問題中。 – xhs 2015-02-11 13:17:05

+0

這是合理的,我會嘗試。但在官方的[例子](https://sctp-refimpl.googlecode.com/svn/trunk/KERN/usrsctp/programs/)中,我沒有看到這些鑄造的東西,他們只是工作得很好。 – xhs 2015-02-11 13:37:03

+0

嗨,@ joachim-pileborg,我再次更新了我的問題。我仍然堅持認爲它與'addr'參數無關。 – xhs 2015-02-11 14:11:11

回答

2

OK,我想通了,爲什麼我自己。這很愚蠢,但我會在這裏發佈解決方案,以防有人需要它。

聯合sctp_sockstore(回調函數的第二個參數的類型)的定義如下所示。

union sctp_sockstore { 
#if defined(INET) 
    struct sockaddr_in sin; 
#endif 
#if defined(INET6) 
    struct sockaddr_in6 sin6; 
#endif 
    struct sockaddr_conn sconn; 
    struct sockaddr sa; 
}; 

INET和INET6在usrsctp庫,但不是在我的代碼中定義的,因爲我手工製作的Makefile文件和省略它們。由於工會的規模不同,參數發生了偏移(如16位),因此變得無稽之談。

當您編譯自己的代碼時,定義INET和INET6(特別是INET6)可以解決問題。