Как стать автором
Обновить

Как сделать flush bluetooth-сокета в C (Linux)?

Время на прочтение5 мин
Количество просмотров922
Вчера столкнулась с тем, что написанная мною серверная программа (Fedora 10, C), общающаяся с моим же мобильным клиентом (Symbian 9.1, Mobile Python for Series 60) стала давать сбой при пересылке файла.

_____________________________________________________
P.S.: Жаль, что никто не откликнулся советом… Впрочем, уже сама разобралась. Оказывается, стоит контролировать значение, возвращаемое функцией write() — реально может отправляться меньше байт, чем пыталась переслать программа. Пытаясь отправить массив байт, стоит проверять, сколько байт реально отправилось — и упорно досылать неотправленное.
_____________________________________________________

До вчерашнего дня сбоев не было. При этом отладочная печать серверной программы показывает, что файл отправлен полностью. Отладочная печать клиентского приложения говорит, что файл считан не полностью. Код клиента и сервера не менялся, софт на компе и телефоне я не обновляла, новый не устанавливала.

Другие файлы прекрасно отправляются этими же программами с сервера на телефон и обратно. Проблема лишь с новой версией отправляемого jar-файла — с 20К он вырос до 38К. Смотрела код пересылаемого файла, чтобы понять, не обрывается ли передача на байте вроде FF — нет, в этом месте вполне рядовая последовательность байтов: ...00 60 39 00 00… (39 — последний переданный байт).

Подозреваю, что проблема — в буферизации вывода в сокет: сервер отправил не всю информацию, а клиент ждет, когда весь объем информации будет передан. Хотя странно, что при постоянном ежедневном использовании сервера и клиента эта ошибка проявилась первый раз. Но способа сделать flush сокету я пока не нашла (fflush(bt->socketClient) дает ошибку сегментирования — и, как я понимаю, для сокета, созданного описанным ниже способом, неприменим).

Вот код, который создает сокет и пытается отправить чеез него файл:
struct BT_SDESC {
  unsigned char channel;
  unsigned int uuid;
  char *serviceName;
  char *serviceDescription;
  char *serviceProvider;
  void *session;
  int hciID;
};

// Bluetooth server
struct BT_SRV {
  struct BT_SDESC *sd;
  int socketClient;
  int socketServer;
};

...

struct BT_SDESC *btBuildSDesc(int channel) {
  struct BT_SDESC *sd;
  sd=(struct BT_SDESC *)malloc(sizeof(struct BT_SDESC));
  if (!sd) {
    return NULL;
  }
  memset(sd,0,sizeof(struct BT_SDESC));
  sd->channel = channel;
  sd->uuid = 0xAFFF; /* TODO: check what is this value */
  if( !(sd->serviceName = strdup("TeleComp: W-Shell"))
    || !(sd->serviceDescription = strdup("User Shell for Wearable Computer"))
    || !(sd->serviceProvider = strdup("kiborgov.net"))
    ) {
    btDestroySDesc(sd);
    return NULL;
  }
  return sd;
}

int btSDescribe(struct BT_SDESC *sd) {
  uint32_t serviceUuidInt[]={0,0,0,sd->uuid};
  uuid_t serviceUuid, rootUuid, l2capUuid, rfcommUuid;
  sdp_record_t *record=sdp_record_alloc();
  sdp_session_t *session=NULL;
  sdp_list_t *rootList=NULL, *svClassID=NULL, *profilesList=NULL, *l2capList=NULL, *protocolList=NULL, *rfcommList=NULL, *accessProtocolList=NULL;
  sdp_profile_desc_t profile;
  sdp_data_t *channel=NULL;
  int ret=-2;

  /* Set general service ID */
  sdp_uuid128_create(&serviceUuid, &serviceUuidInt);
  sdp_set_service_id(record, serviceUuid);

  /* Public service record */
  sdp_uuid16_create(&rootUuid, PUBLIC_BROWSE_GROUP);
  rootList = sdp_list_append(0, &rootUuid);
  sdp_set_browse_groups(record, rootList);

  /* Set port attributes */
  sdp_uuid16_create(&rootUuid, SERIAL_PORT_SVCLASS_ID);
  svClassID = sdp_list_append(0, &rootUuid);
  sdp_set_service_classes(record, svClassID);

  sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
  profile.version = 0x100;
  profilesList = sdp_list_append(0, &profile);
  sdp_set_profile_descs(record, profilesList);

  /* Set l2cap info */
  sdp_uuid16_create(&l2capUuid, L2CAP_UUID);
  l2capList = sdp_list_append(0, &l2capUuid);
  protocolList = sdp_list_append(0, l2capList);

  /* Set rfcomm info */
  sdp_uuid16_create(&rfcommUuid, RFCOMM_UUID);
  channel = sdp_data_alloc(SDP_UINT8, &(sd->channel));
  rfcommList = sdp_list_append(0, &rfcommUuid);
  sdp_list_append(rfcommList, channel);
  sdp_list_append(protocolList, rfcommList);

  /* Attach protocol information to service record */
  accessProtocolList = sdp_list_append(0, protocolList);
  sdp_set_access_protos(record, accessProtocolList);

  /* Set name, provider, description */
  sdp_set_info_attr(record, sd->serviceName, sd->serviceProvider, sd->serviceDescription);

  /* Connect local SDP  server, register service record, then disconnect */
  if ((session=sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY))) {
    ret=sdp_record_register(session, record, 0);
  }

  /* Free allocated resources */
  sdp_data_free(channel);
  sdp_list_free(l2capList, 0);
  sdp_list_free(rfcommList, 0);
  sdp_list_free(rootList, 0);
  sdp_list_free(accessProtocolList, 0);
  sdp_list_free(svClassID, 0);
  sdp_list_free(profilesList, 0);

  sd->session = (void *)session;

  return ret;
}

int btBuildSocket(unsigned int channel, struct BT_SDESC *sd) {
  struct sockaddr_rc localAddr;
  int s=-1, res=-1;

  if ((sd->hciID=btCheckDevice())==-1) {
    return -1;
  }
  if (hci_open_dev(sd->hciID)<0) {
    return -1;
  }
  hci_close_dev(sd->hciID);
  memset(&localAddr, 0, sizeof(struct sockaddr_rc));
  if ((s=socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM))==-1) {
    return -1;
  }

  /* TODO: replace code below with code uses SDP */
  localAddr.rc_family = AF_BLUETOOTH;
  localAddr.rc_bdaddr = *BDADDR_ANY;
  localAddr.rc_channel = (uint8_t) channel;
  if ( (res=bind(s, (struct sockaddr *) &localAddr, sizeof(localAddr)))==-1 ) {
    close(s);
    return -1;
  }

  return s;
}

struct BT_SRV *btStartServer(int channel) {
  struct BT_SRV *bt=malloc(sizeof(struct BT_SRV));
  if (!bt) {
    btErr=BT_ERR_START_SERVER;
    btKillServer(bt);
    return NULL;
  }
  memset(bt, 0, sizeof(struct BT_SRV));
  bt->socketServer=bt->socketClient=-1;
  btErr=BT_OK;

  if (btCheckDevice()<0) {
    btErr=BT_ERR_NO_DEVICE;
    btKillServer(bt);
    return NULL;
  }

  if ( !(bt->sd=btBuildSDesc(channel)) ) {
    btErr=BT_ERR_SDESC_CREATION;
    btKillServer(bt);
    return NULL;
  }

  if (btSDescribe(bt->sd)==-1) {
    btErr=BT_ERR_SDESC_REGISTRATION;
    btKillServer(bt);
    return NULL;
  }

  if ((bt->socketServer=btBuildSocket(channel, bt->sd))==-1) {
    btErr=BT_ERR_BUILD_SOCKET;
    btKillServer(bt);
    return NULL;
  }

  if (listen(bt->socketServer,10)) {
    btErr=BT_ERR_SOCKET_LISTENING;
    btKillServer(bt);
    return NULL;
  }
  return bt;
}

...

struct BT_SRV *bt=NULL;

int sendFileToClient(char *fname) {
  static char buf[1024];
  int h;
  long int bufsize=1024;
  long int sent=0;
  long int loaded;
  struct stat stbuf;
logPrint("Send file \"%s\" to client.",fname);
  if (stat(fname,&stbuf)==-1) {
logPrint("ERROR: cannot retreive file information.");
    return -1;
  }
  if ((h=open(fname,O_RDWR))<1) {
logPrint("ERROR: cannot open file.");
    return -2;
  }
  sprintf(buf,"%010ld",stbuf.st_size);
logPrint("Sending %ld bytes of data...",stbuf.st_size);
  write(bt->socketClient, buf, 10);
  while (sent<stbuf.st_size) {
    loaded=(stbuf.st_size>bufsize)?bufsize:stbuf.st_size;
    loaded=read(h,&buf,loaded);
    if (loaded<1) {
      break;
    }
    sent+=loaded;
    write(bt->socketClient, buf, loaded);
logPrint("sent: %ld",sent);
  }
  close(h);
logPrint("File is sent. %ld bytes transmitted.",sent);
  return sent;
}

...

  bt=btStartServer(cfgBtChannel);
  ...
  bt->socketClient=accept(bt->socketServer,NULL,NULL);
  fcntl(bt->socketClient,F_SETFL,O_NONBLOCK);
  sendFileToClient("./projects/cloud/bin/Cloud.jar");

Передача файла, на котором случается сбой, обрывается то на одном (в 80% случаев) месте файла, то на втором (в оставшихся 20%).
Теги:
Хабы:
Всего голосов 5: ↑5 и ↓0+5
Комментарии3

Публикации