Вчера столкнулась с тем, что написанная мною серверная программа (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%).
