📜 ⬆️ ⬇️

How to make a flush bluetooth socket in C (Linux)?

Yesterday I was faced with the fact that the server program I wrote (Fedora 10, C), which communicated with my mobile client (Symbian 9.1, Mobile Python for Series 60), began to crash when sending a file.

_____________________________________________________
PS: It is a pity that no one responded to the council ... However, I already figured it out myself. It turns out that it is worth controlling the value returned by the write () function - actually less bytes can be sent than the program tried to send. Trying to send an array of bytes, it is worth checking how many bytes really went - and stubbornly send the unsent.
_____________________________________________________

Until yesterday, there were no failures. At the same time, the debug print of the server program shows that the file has been sent completely. Client application debug printing says that the file is not fully read. The client and server code did not change, I did not update the software on my computer and phone, I did not install the new one.
')
Other files are perfectly sent by the same programs from the server to the phone and back. The only problem is with the new version of the jar file being sent - from 20K it has grown to 38K. I watched the code of the file being sent to see if the transfer to the byte like FF is broken — no, in this place there is a quite ordinary sequence of bytes: ... 00 60 39 00 00 ... (39 is the last transmitted byte).

I suspect that the problem is in buffering the output to the socket: the server has not sent all the information, and the client is waiting for the entire amount of information to be transmitted. Although it is strange that with constant daily use of the server and the client, this error manifested itself for the first time. But I haven’t yet found a way to flush the socket (fflush (bt-> socketClient) gives a segmentation error - and, as I understand it, is not applicable to the socket created by the method described below).

Here is the code that creates the socket and tries to send a file through it:
  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");

The transfer of the file on which a failure occurs is interrupted either in one (80% of the time) location of the file, then in the second (in the remaining 20%).

Source: https://habr.com/ru/post/49729/


All Articles