Писал клиент-сереврную моногопоточную программу под Builder используя API функции send recv select accept. Суть программы — передача файлов по сети от клиента к серверу разбивая их на части определенного размера, также в пакет перед send вставляю в заголовок свою структуру данных, а далее собственно данные пакета. Все работает если: клиент и сервер запускаются на локалхосте (файлы передаются нормально), так же работает в паре машин c XP+98 сервер и клиент попеременно запускался на разных машинах файлы передаюся, НО пробовал тестить работу на паре машин XP+XP в ходе пересылки данных сервер после захода в send не возвращает управление в поток. Подскажите в чем может быть проблема
Здравствуйте, VVV, Вы писали:
// Главный юнит.
void __fastcall TForm1::GetFileClick(TObject *Sender)
{
TCPThread *mth=new TCPThread(false,FilStr->Text);
mth->FreeOnTerminate=true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
WSADATA wData;
WORD mVer;
int err;
mVer= MAKEWORD(2,2);
err=WSAStartup(mVer,&wData);
}
// Юнит потока клиента.
// FILE,TRANSMISSION,END_OF_TRANSMISSION - Дефайны
//s определен в хедере
__fastcall TCPThread::TCPThread(bool CreateSuspended,AnsiString FilName)
: TThread(CreateSuspended)
{
FileName=FilName;
}
//---------------------------------------------------------------------------
void __fastcall TCPThread::Execute()
{
int hFile;
PckHd PacketHead;
int tmp,iByteRecv,cnt=2048;
WORD port;
AnsiString FullFileName,FLen,Abuff,ShortFileName;
char *buff;
bool flag=false;
int iFileLength;
int iBytesRead,err;
fd_set rdfds;
TIMEVAL tm;
FD_ZERO(&rdfds);
tm.tv_sec=0;
tm.tv_usec=0;
unsigned long adlo;
BytesRec=0;
port=6666;
adlo=inet_addr("127.0.0.1");//Или любой адрес где стоит сервер
sockaddr_in adr;
adr.sin_addr.S_un.S_addr=adlo;
adr.sin_family=AF_INET;
adr.sin_port=htons(port);
ShortFileName=FileName;
int i=ShortFileName.Length();
while(flag!=true)
{
if(ShortFileName.c_str()[i]=='\\')
{
ShortFileName=ShortFileName.Delete(1,i+1);
flag=true;
}
else
i--;
};
flag=false;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
FD_SET(s,&rdfds);
tmp=connect(s,(sockaddr*)&adr,sizeof(sockaddr_in));
if(tmp==0)
{
PacketHead.CurrPacketSize=FileName.Length()+1;
PacketHead.Head=FILE;
buff=(char*)malloc(sizeof(PckHd)+PacketHead.CurrPacketSize);
memcpy(buff,&PacketHead,sizeof(PckHd));
memcpy(buff+sizeof(PckHd),FileName.c_str(),PacketHead.CurrPacketSize);
send(s,buff,sizeof(PckHd)+PacketHead.CurrPacketSize,0);
free(buff);
buff=(char*)malloc(sizeof(PckHd)+2048);
AnsiString Path;
Path="c:\\"+ShortFileName;//Любой путь куда копировать
hFile=FileCreate(Path);
while(flag!=true)
{
if(select(0,&rdfds,NULL,NULL,NULL)>0)//&tm
{
iByteRecv=recv(s,buff,sizeof(PckHd)+cnt,MSG_PEEK);
if(((PckHd*)buff)->Head==TRANSMISSION)
{
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
err=recv(s,buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize,0);
err=FileWrite(hFile,(char*)buff+sizeof(PckHd),((PckHd*)buff)->CurrPacketSize);
BytesRec+=err;
Synchronize(ChangeCap);
}
else
{
if(((PckHd*)buff)->Head==URL)
{
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
recv(s,buff,sizeof(buff),0);
err=FileWrite(hFile,(char*)buff+sizeof(PckHd),((PckHd*)buff)->CurrPacketSize);
BytesRec+=err;
Synchronize(ChangeCap);
};
if(((PckHd*)buff)->Head==END_OF_TRANSMISSION)
{
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
recv(s,buff,sizeof(buff),0);
err=FileWrite(hFile,(char*)buff+sizeof(PckHd),((PckHd*)buff)->CurrPacketSize);
BytesRec+=err;
Synchronize(ChangeCap);
FileClose(hFile);
flag=true;
};
};
};
};
};
closesocket(s);
}
//---------------------------------------------------------------------------
void __fastcall TCPThread::ChangeCap()
{
Form1->ByRec->Caption=IntToStr(BytesRec);
}
это то что в клиенте в главном и юните потока.
//Главный юнит сервера
void __fastcall TForm1::FormCreate(TObject *Sender)
{
AccTime->Enabled;
MsgHst->Clear();
WSADATA wData;
WORD mVer;
int err;
int tmp;
WORD port;
port=6666;
mVer= MAKEWORD(2,2);
sockaddr_in adr;
adr.sin_addr.S_un.S_addr=INADDR_ANY;
adr.sin_family=AF_INET;
adr.sin_port=htons(port);
err=WSAStartup(mVer,&wData);
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
tmp=bind(s,(sockaddr*)&adr,sizeof(sockaddr_in));
err=listen(s,1);
}
//-------------------------------
void __fastcall TForm1::AccTimeTimer(TObject *Sender)
{
SOCKET rSock;
fd_set rdfds;
TIMEVAL tm;
FD_ZERO(&rdfds);
FD_SET(s,&rdfds);
tm.tv_sec=0;
tm.tv_usec=0;
if(select(0,&rdfds,NULL,NULL,&tm)>0)
{
sockaddr nadr;
int dl=sizeof(sockaddr);
rSock=accept(s,(sockaddr*)&nadr,&dl);//?!sockaddr
if(rSock!=INVALID_SOCKET)
{
TCPThread *mth = new TCPThread(false,rSock);
mth->FreeOnTerminate = true;
}
};
}
//Юнит потока процесса sock определен в хедере
__fastcall TCPThread::TCPThread(bool CreateSuspended,SOCKET s)
: TThread(CreateSuspended)
{
sock=s;
}
//---------------------------------------------------------------------------
void __fastcall TCPThread::Execute()
{
bool flag=false;
PckHd PacketHead;
AnsiString tmpstr;
int UrlLen;
int iFileHandle,iByteRecv;
HANDLE hFile;
LPVOID hView,pStart;
int cnt,pOff=0;
DWORD dwSizeLow,dwSizeHigh;
int err;
int tmp;
WORD port;
unsigned long adlo;
port=6666;
fd_set rdfds;
TIMEVAL tm;
FD_ZERO(&rdfds);
FD_SET(sock,&rdfds);
tm.tv_sec=0;
tm.tv_usec=0;
UrlName="";FileName="";
char *buff;
cnt=2048;
buff=(char*)malloc(sizeof(PckHd)+cnt);
while(flag!=true)
{
if(select(0,&rdfds,NULL,NULL,&tm)>0)
{
iByteRecv=recv(sock,buff,sizeof(PckHd)+cnt,MSG_PEEK);
if(((PckHd*)buff)->Head==FILE)
{
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
err=recv(sock,buff,sizeof(buff),0);
FileName=(char*)buff+sizeof(PckHd);
Stat="FileNameRecived: "+FileName;
Synchronize(ChangeCap);
flag=true;
}
else
{
if(((PckHd*)buff)->Head==URL)
{
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
err=recv(sock,buff,sizeof(buff),0);
UrlName=(char*)buff+sizeof(PckHd);
flag=true;
};
};
};
};
free (buff);
if(FileName!="")
{
iFileHandle = FileOpen(FileName, fmOpenRead);
dwSizeLow = GetFileSize((HANDLE)iFileHandle, &dwSizeHigh);
hFile=CreateFileMapping((HANDLE)iFileHandle,NULL,PAGE_READONLY,dwSizeHigh,dwSizeLow,NULL);
hView=MapViewOfFile(hFile,FILE_MAP_READ,0,0,dwSizeLow);
Stat="FileMappingSucceed! FileSize: "+IntToStr(dwSizeLow);
Synchronize(ChangeCap);
pStart=hView;
buff=(char*)malloc(sizeof(PckHd)+cnt);
if(dwSizeLow>2048) cnt=2048;
else
{
PacketHead.Head=END_OF_TRANSMISSION;
PacketHead.CurrPacketSize=dwSizeLow;
realloc(buff,sizeof(PckHd)+dwSizeLow);
memcpy(buff,&PacketHead,sizeof(PckHd));
memcpy(buff+sizeof(PckHd),pStart,dwSizeLow);
err=send(sock,buff,sizeof(PckHd)+dwSizeLow,0);
BytesSend+=err;
Stat="BytesSending...";
Synchronize(ChangeCap);
flag=false;
};
while(flag!=false)
{
if(cnt+pOff<dwSizeLow)
{
PacketHead.Head=TRANSMISSION;
PacketHead.CurrPacketSize=cnt;
memcpy(buff,&PacketHead,sizeof(PckHd));
memcpy(buff+sizeof(PckHd),(char*)pStart+pOff,cnt);
err=send(sock,buff,sizeof(PckHd)+cnt,0);
BytesSend+=err;
Stat="BytesSending...";
Synchronize(ChangeCap);
pOff+=cnt;
}
else
{
cnt=dwSizeLow-pOff;
PacketHead.Head=END_OF_TRANSMISSION;
PacketHead.CurrPacketSize=cnt;
realloc(buff,sizeof(PckHd)+cnt);
memcpy(buff,&PacketHead,sizeof(PckHd));
memcpy(buff+sizeof(PckHd),(char*)pStart+pOff,cnt);
err=send(sock,buff,sizeof(PckHd)+cnt,0);
BytesSend+=err;
Stat="BytesSending...";
Synchronize(ChangeCap);
flag=false;
};
};
UnmapViewOfFile(hView);
Stat="Unmaping Success!!!";
Synchronize(ChangeCap);
};
}
//---------------------------------------------------------------------------
void __fastcall TCPThread::ChangeCap()
{
Form1->BySend->Caption=IntToStr(BytesSend);
Form1->Status->Caption=Stat;
}
Вот собственно и все. Мне кажется это не из-за bind т.к. на XP+98 все работает корректно. А в XP+XP (правда только в тесте участвовали 3 машины XP) может преслать либо некоторое кол-во байт а потом зайти в send и от туда не выйти, может вообще не переслать, а случалось и пересылал весь файл.
ЗЫ: Такое чуйство что в send`е идет блок. Компилятор C++ Builder 6
ЗЗЫ: ПЛЗ!!! =)) Не могли бы вы отправить мне на мыло ваш пример просто из спортивного интереса =) ynblpb@hotbox.ru или ynblpb@freemail.ru
можть и я вам когда-нибудь помогу
Здравствуйте, Аноним, Вы писали:
..
А>Вот собственно и все. Мне кажется это не из-за bind т.к. на XP+98 все работает корректно. А в XP+XP (правда только в тесте участвовали 3 машины XP) может преслать либо некоторое кол-во байт а потом зайти в send и от туда не выйти, может вообще не переслать, а случалось и пересылал весь файл.
А>ЗЫ: Такое чуйство что в send`е идет блок. Компилятор C++ Builder 6
А>ЗЗЫ: ПЛЗ!!! =)) Не могли бы вы отправить мне на мыло ваш пример просто из спортивного интереса =) ynblpb@hotbox.ru или ynblpb@freemail.ru
А>можть и я вам когда-нибудь помогу
А>
1. Думается, это может происходить из-за того, что recv может возвращать не все запрошенные данные, а по частям (также и send) — пример: послали строку "Вперёд, комсомол!", а получаем так: "Вперёд" ", " "комсо" "мол!", т.е. за четыре вызова recv. Т.е. нужен цикл, пока всё не считаем. Примеров не нашёл, есть код из проекта:
long
TSockHelper::Recv(SOCKET sock, void *Buf_, int iSize, int iTimeout)
{
TIMEVAL TheTime={1, 0};
int err;
int rc;
fd_set readfds;
fd_set exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
int iShift=0;
char *Buf=(char*)Buf_;
while(iSize > 0)
{
FD_SET(sock, &readfds);
FD_SET(sock, &exceptfds);
err=::select(0, &readfds, 0, &exceptfds, &TheTime);
if(err == SOCKET_ERROR || FD_ISSET(sock, &exceptfds))
return SOCKET_ERROR;
if(FD_ISSET(sock, &readfds))
{
rc=::recv(sock, Buf+iShift, iSize, 0);
if(rc == SOCKET_ERROR)
return SOCKET_ERROR;
iShift+=rc;
iSize-=rc;
}
else
if(err == 0)
{
if(iTimeout < 0 && --iTimeout == 0)
return SOCKET_ERROR;
}
}
return iShift;
}
//вызов
struct MyData{
...
};
MyData data;
TSockHelper::Recv(sock, &data, sizeof(data), 10); //10 seconds timeout
аналогично и для send написана Send.
2. Потенциальные ошибки:
из п.1 следует, что здесь в buff могло ещё не приняться sizeof(PckHd) байт, поэтому ((PckHd*)buff)->CurrPacketSize) вернёт неверное значение:
realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
recv(s,buff,sizeof(buff),0);
функция realloc переразмещает буфер,
The size argument gives the new size of the block, in bytes. The contents of the block are unchanged up to the shorter of the new and old sizes, although the new block can be in a different location. Because the new block can be in a new memory location, the pointer returned by realloc is not guaranteed to be the pointer passed through the memblock argument.
поэтому верное использование такое:
buff=realloc(buff,sizeof(PckHd)+((PckHd*)buff)->CurrPacketSize);
2.1 recv(s,buff,sizeof(buff),0); — как думаешь чему равно sizeof(buff)? правильно — всего 4 байта (32 бита) под Win32, поэтому здесь принимаешь максимум 4 байта. Думаю, ты хотел написать примерно следующее:
int rc=recv(s, buff+sizeof(PckHd), ((PckHd*)buff)->CurrPacketSize,0);
написал buff+sizeof(PckHd), потому что заголовок-то уже получен (хотя, может, не совсем понял протокола — не настаиваю

)
2.2. err=FileWrite(hFile,(char*)buff+sizeof(PckHd),((PckHd*)buff)->CurrPacketSize);
соответственно и в файл писать надо, то количество байт, которое вернул recv.
Код посмотрел не полностью (большой больно). Но, думаю, что и так чуть-чуть поможет

.