为什么socketcan bind()调用从jni调用时总是返回0?

cclgggtu  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(477)

我正在尝试创建一个java Package 的can总线c实现,它将在android环境中使用。我在linux内核中使用了socketcan来创建一个socket并将其绑定到can接口。
开发环境没有物理can总线,因此我通过 sudo ip link add dev vcan0 type vcan 以及 sudo ip link set up vcan0 .
在这个环境中运行本机c代码可以正常工作,当接口存在时套接字绑定,当接口不存在时返回错误。但是,当通过jni运行相同的本机c代码时 bind(...) 不管接口的状态如何,调用总是返回0,尽管任何后续的 write(...) 调用按预期失败。
是不是有什么我忽略了,这意味着这是事实?
jni代码如下(直接从我的c实现中提取,必要时使用额外的类型转换):

JNIEXPORT jboolean JNICALL Java_SocketCAN_nativeOpen
  (JNIEnv * env, jobject jobj, jint bus_id)
{
  if ((int) bus_id < MAX_NUMBER_OF_CAN_BUSES)
  {
    int s;

    if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) 
    {
      printf("Error while opening socket\n");
      return JNI_FALSE;
    }

    struct sockaddr_can addr;
    struct ifreq ifr;

    strcpy(ifr.ifr_name, "vcan0");
    ioctl(s, SIOCGIFINDEX, &ifr);

    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
    {
      printf("Error in socket bind\n");
      return JNI_FALSE;
    }

    // Set the socketId in the Java class.
    jclass   jcls        = (*env)->FindClass(env, "SocketCAN");
    jfieldID socket_id   = (*env)->GetFieldID(env, jcls, "socket", "I");
    jint     j_socket_id = (*env)->GetIntField(env, jobj, socket_id);
    j_socket_id = s;
    (*env)->SetIntField(env, jobj, socket_id, j_socket_id);

    return JNI_TRUE;
  }

  return JNI_FALSE;
}

非常感谢您的帮助,谢谢!
编辑:如果有人似乎遇到了这个奇怪的问题,想要一个解决方法(尽管这可能是正确的方法,我忽略了它),请检查 ioctl(...) 函数调用。在运行c和jni时,如果未设置“vcan0”,则返回-1。
对于@124312341234123411234123和@andrewhenle的建议,我修改后的更新代码如下:

JNIEXPORT jint JNICALL Java_SocketCAN_nativeOpen
  (JNIEnv * env, jobject jobj, jint bus_id)
{
  if ((int) bus_id < MAX_NUMBER_OF_CAN_BUSES)
  {
    int s;

    if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) 
    {
      printf("Error while opening socket\n");
      return -1;
    }

    struct sockaddr_can addr;
    struct ifreq ifr;

    strcpy(ifr.ifr_name, "vcan0");
    if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
    {
      printf("Error in ioctl\n");
      return -1;
    }

    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
    {
      printf("Error in socket bind\n");
      return -1;
    }

    return (jint) s;
  }

  return -1;
}
ajsxfq5m

ajsxfq5m1#

这个 bind() 调用中需要适当的接口索引 can_ifindex . 要获得此值,可以使用 ioctl() 与…通话 SIOCGIFINDEX ,就像你一样。然而,当 ioctl() 调用失败,则 ifreq 结构不一定具有正确的索引,它可能仍然具有来自之前占用相同内存区域的最后一个对象的“随机”值。因为忽略了 ioctl() ,你打电话来了 bind() 具有“随机”接口索引。这也意味着绑定可能会失败,也可能不会失败,这取决于值,因为在大多数情况下使用未初始化的值是ub。要避免此错误,请检查 ioctl() 并相应地处理错误。
这个“随机”值对于纯c版本和jni版本似乎是不同的。避免这种随机差异的一种可能性是将每个新的自动对象直接设置为一个值。在您的情况下,您可以将所有内容设置为0: struct ifreq ifr={0}; ,相同 addr . 这个额外的步骤可以获得更一致的行为。

相关问题