【问题标题】:SCTP: What should be the sctp_status.sstate value of an SCTP socket after succesful connect() call?SCTP:成功调用 connect() 后,SCTP 套接字的 sctp status.sstate 值应该是多少?
【发布时间】:2017-09-23 07:34:00
【问题描述】:

我正在尝试通过 SCTP 连接到远程对等点(除了通过套接字和 ping 连接到它之外,我没有目录访问权限)。假设我已成功连接,如果我尝试调用 getsocktopt(),我的 sctp_status.sstate 的值应该是多少?根据 sctp.h,我的是 SCTP_COOKIE_ECHOED(3)。这是正确的吗?不应该是 SCTP_ESTABLISHED 吗?

因为我尝试使用此代码向远程对等方发送消息:

ret = sctp_sendmsg (connSock, (void *) data, (size_t) strlen (data), (struct sockaddr *) &servaddr, sizeof (servaddr), 46, 0, 0, 0, 0);

它返回了我尝试发送的字节数。然后当我尝试捕捉是否有任何响应时:

ret = sctp_recvmsg (connSock, (void *) reply, sizeof (reply), NULL,
          NULL, NULL, &flags);

它返回 -1,错误号为 ECONNRESET(104)。我的代码或流程中可能出现的错误是什么?我错过了什么吗?

提前感谢您的回答。将很高兴地欣赏这一点。 :)

更新:下面是我连接到远程对等方的客户端代码。它实际上是一个节点插件供我使用,因为节点不完全支持 SCTP。使用 lksctp-tools 包来包含标题。

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAX_BUFFER 1024

int connSock = 0;

int connect(char host[], int port, char remote_host[], int remote_port, int timeout) {

  int ret, flags;
  fd_set rset, wset;
  struct sockaddr_in servaddr;
  struct sockaddr_in locaddr;
  struct sctp_initmsg initmsg;
  struct timeval tval;
  struct sctp_status status;
  socklen_t opt_len;

  errno = 0;

  connSock = socket (AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  flags = fcntl(connSock, F_GETFL, 0);
  fcntl(connSock, F_SETFL, flags | O_NONBLOCK);

  if (connSock == -1)
  {
      return (-1);
  }

  memset(&locaddr, 0, sizeof(locaddr));
  locaddr.sin_family = AF_INET;
  locaddr.sin_port = htons(port);
  locaddr.sin_addr.s_addr = inet_addr(host);

  ret = bind(connSock, (struct sockaddr *)&locaddr, sizeof(locaddr));

  if (ret == -1)
  {
      return (-1);
  }

  memset (&initmsg, 0, sizeof (initmsg));
  initmsg.sinit_num_ostreams = 5;
  initmsg.sinit_max_instreams = 5;
  initmsg.sinit_max_attempts = 10;
  ret = setsockopt(connSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));

  if (ret == -1)
  {
      return (-1);
  }

  memset (&servaddr, 0, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons (remote_port);
  servaddr.sin_addr.s_addr = inet_addr (remote_host);

  if((ret = connect (connSock, (struct sockaddr *) &servaddr, sizeof (servaddr))) < 0)
      if (errno != EINPROGRESS)
          return (-1);

  if (ret == 0) {
      fcntl(connSock, F_SETFL, flags);
      return 0;
  }

  FD_ZERO(&rset);
  FD_SET(connSock, &rset);
  wset = rset;
  tval.tv_sec = timeout;
  tval.tv_usec = 0;

  ret = select(connSock+1, &rset, &wset, NULL, timeout ? &tval : NULL);

  if (ret == 0) {
      close(connSock);
      errno = ETIMEDOUT;
      return(-1);
  }
  else if (ret < 0) {
      return(-1);
  }

  fcntl(connSock, F_SETFL, flags);

  opt_len = (socklen_t) sizeof(struct sctp_status);
  getsockopt(connSock, IPPROTO_SCTP, SCTP_STATUS, &status, &opt_len);

  printf ("assoc id  = %d\n", status.sstat_assoc_id);
  printf ("state     = %d\n", status.sstat_state);
  printf ("instrms   = %d\n", status.sstat_instrms);
  printf ("outstrms  = %d\n", status.sstat_outstrms);

  return 0;
}

int sendMessage(char remote_host[], int remote_port, char data[]) {

  int ret, flags;
  struct sockaddr_in servaddr;
  char reply[1024];

  errno = 0;

  memset (&servaddr, 0, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons (remote_port);
  servaddr.sin_addr.s_addr = inet_addr (remote_host);

  printf("\nSending %s (%li bytes)", data, strlen(data));

  ret = sctp_sendmsg (connSock, (void *) data, (size_t) strlen (data),
          (struct sockaddr *) &servaddr, sizeof (servaddr), 46, 0, 0, 0, 0);

  if (ret == -1)
  {
    printf("\nError sending errno(%d)", errno);
    return -1;
  }
  else {
    ret = sctp_recvmsg (connSock, (void *) reply, sizeof (reply), NULL,
          NULL, NULL, &flags);

    if (ret == -1)
    {
      printf("\nError receiving errno(%d)", errno);
      return -1;
    }
    else {
      printf("\nServer replied with %s", reply);
      return 0;
    }
  }
}

int getSocket() {

  return connSock;
}

我不知道在连接之前我是否需要先设置一些我错过的重要内容。我从不同的来源得到了 sn-p,所以它很乱。

另一个更新,这是执行时该代码的 tshark 日志:

3336.919408  local  -> remote SCTP 82 INIT
3337.006690  remote -> local  SCTP 810 INIT_ACK
3337.006727  local  -> remote SCTP 774 COOKIE_ECHO
3337.085390  remote -> local  SCTP 50 COOKIE_ACK
3337.086650  local  -> remote SCTP 94 DATA
3337.087277  remote -> local  SCTP 58 ABORT
3337.165266  remote -> local  SCTP 50 ABORT

这个here的详细tshark日志。

看起来远程发送了它的 COOKIE_ACK 块,但我的客户端未能将其状态设置为 ESTABLISHED(我仔细检查了 sstate 值 3 here)。

【问题讨论】:

    标签: sockets sctp


    【解决方案1】:

    如果关联设置过程已完成,则状态应为 SCTP_ESTABLISHED。 SCTP_COOKIE_ECHOED 表示关联尚未完全建立。这意味着发起方(在这种情况下是您的本地主机)已发送(一次或多次)COOKIE_ECHO 块,该块尚未被 COOKIE_ACK 从远程端确认。

    您可以在这种状态下发送消息(SCTP 将简单地对其进行缓冲,直到它获得 COOKIE_ACK 并稍后重新发送)。

    根据您提供的信息,很难说出了什么问题。在这个阶段,可能值得深入研究wireshark跟踪,看看远程端在你的COOKIE_ECHO上回复了什么。

    此外,如果您可以共享可能有助于确定根本原因的客户端/服务器端代码。

    更新#1: 还应注意,应用程序可以自行中止它们的关联(例如,如果该关联未在该服务器上配置)。如果您尝试连接到随机服务器(而不是您的特定服务器),这很有可能并且在您的情况下实际上是有意义的。在这种情况下,您这边的关联状态是 COOKIE_ECHOED 因为 COOKIE_ACK 尚未到达(只是竞争条件)。正如我之前所说,SCTP 很乐意接受您处于这种状态的数据,并只是缓冲它,直到它收到 COOKIE_ACK。远程端的 SCTP 会立即发送 COOKIE_ACK,甚至在应用程序在 accept() 中接收到执行控制之前。如果应用程序决定以不正常的方式终止关联,它将发送 ABORT(这是您在 wireshark 跟踪中的第一个 ABORT)。你方尚未收到此 ABORT 并发送 DATA 块。由于远程端认为这个关联已经终止,它不能处理 DATA 块,所以它把它当作意外(参见 RFC 4960 第 8.4 章)并发送另一个 t 位设置为 1 的 ABORT。 我想这就是你的情况。您只需查看wireshark跟踪即可轻松确认。

    【讨论】:

    • 您好,感谢您的回答。我明白了,这就是 sctp_sendmsg 返回这些字节的原因。在我清理它并可能编辑我的问题后,我可以分享我的这个(客户端)的代码。我尝试在我的机器上安装 Wireshark,但我认为 SSH 选项表单有问题,因为每当我尝试加载我的私钥时,文件输入文本框会变为红色,导致“开始”按钮变灰。解析文件路径时一定有一些错误,我还没有在这台机器(OSX)中找到 Wireshark 首选项的位置。我会回来寻求进展。谢谢。 :)
    • 更新了我的问题。谢谢。 :)
    • 发生了一些奇怪的事情。就远程方面而言,本地主机的状态无关紧要。如果远程端通过 COOKIE_ACK 确认了 COOKIE_ECHO,它必须将关联视为活动并因此处理传入的 DATA 块。请提供来自 wireshark 跟踪的模式详细信息。特别是来自 INIT/INIT_ACK/COOKIE_ECHO/COOKIE_ACK/DATA/ABORT 的 SCTP 详细信息,包括端口、验证标签、中止原因和来自 ABORT 块的 T 位值。顺便说一句,本地和远程端的操作系统是什么?
    • 嗨。这是我第一次使用 tshark,所以我想知道怎么做。至于操作系统,我只能说我的是 Debian Linux,因为对方正在处理远程端,所以我不能再给你有关这方面的信息了。为了完整起见,我在服务器 (Debian) 上安装了 lksctp-tools 和 libsctp-dev 来编译此代码并将其添加为节点插件。我会带着tshark踪迹回来。谢谢。
    • 更新了我的问题。 Here 是 tshark 的详细跟踪信息,因此您可以立即查看。谢谢。
    猜你喜欢
    • 2015-11-11
    • 2011-09-14
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 2015-03-18
    相关资源
    最近更新 更多