Вчера столкнулась с тем, что написанная мною серверная программа (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) дает ошибку сегментирования — и, как я понимаю, для сокета, созданного описанным ниже способом, неприменим).
Вот код, который создает сокет и пытается отправить чеез него файл:
Передача файла, на котором случается сбой, обрывается то на одном (в 80% случаев) месте файла, то на втором (в оставшихся 20%).
_____________________________________________________
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%).