CAsyncSocket&Receive
От: zaxs  
Дата: 10.06.04 12:15
Оценка:
Посылаю сообщение серверу и не получаю ответа. Смотрел поиском — такая тема уже поднималась когда кто-то пытался получить ответ от сервера на логин и ему дали кучу примеров. У меня похожая проблема:
соединяюсь с почтовым сервером и скачиваю файл ~300 кб. С помощью такого куска кода:
while (BufLen>0)
{
Sleep(100);
BufLen=ServerSocket.Receive(lpBuf,65536,0);
// write to the file
if (BufLen<=0) break;
fletter.Write(lpBuf,BufLen);
}
Когда делаю это дебаггером пошагово, то все принимается.
Иначе, в файл записывается тока 3 первых байта.
Если сделать Sleep(500), тогда такой файл благополучно принимается.
Если туда скинуть файл размером 2 метра, тогда такая же фигня получается.
Если сделать Sleep слишком большой, тогда прием файла будет очень долгим.
Подскажите плз как это технично реализовать.
Re: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 10.06.04 12:36
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>while (BufLen>0)
Z>{
Z>    Sleep(100);
Z>    BufLen=ServerSocket.Receive(lpBuf,65536,0);
Z>// write to the file
Z>    if (BufLen<=0) break;
Z>    fletter.Write(lpBuf,BufLen);
Z>}

Z>Когда делаю это дебаггером пошагово, то все принимается.
Z>Иначе, в файл записывается тока 3 первых байта.
Z>Если сделать Sleep(500), тогда такой файл благополучно принимается.
Z>Если туда скинуть файл размером 2 метра, тогда такая же фигня получается.
Z>Если сделать Sleep слишком большой, тогда прием файла будет очень долгим.
Z>Подскажите плз как это технично реализовать.

Вы используете MFC сокеты?
Вот тут нверно нужно быть менее жестоким
if (BufLen<=0) break; и проверять, что возвращает WSAGetLastError(), соответсвтенно читать только тогда, когда есть что читать..

Нет ничего невозможного..
Re: CAsyncSocket&Receive
От: Maxim S. Shatskih Россия  
Дата: 10.06.04 12:45
Оценка:
Z>while (BufLen>0)
Z>{
Z> Sleep(100);
Z> BufLen=ServerSocket.Receive(lpBuf,65536,0);
Z>// write to the file
Z> if (BufLen<=0) break;
Z> fletter.Write(lpBuf,BufLen);
Z>}
Z>Когда делаю это дебаггером пошагово, то все принимается.
Z>Иначе, в файл записывается тока 3 первых байта.
Z>Если сделать Sleep(500), тогда такой файл благополучно принимается.
Z>Если туда скинуть файл размером 2 метра, тогда такая же фигня получается.
Z>Если сделать Sleep слишком большой, тогда прием файла будет очень долгим.
Z>Подскажите плз как это технично реализовать.

Простейший способ — заменить async socket на Berkeley вызовы, и вот тогда все заработает без Sleep, конечно.

Async socket дает много функциональности, которая в данном случае просто лишняя и ненужная (за исключением случая, когда нужен минимум нагрузки процессора на гигабитной сети).
Занимайтесь LoveCraftом, а не WarCraftом!
Re[2]: CAsyncSocket&Receive
От: zaxs  
Дата: 11.06.04 05:35
Оценка:
Здравствуйте, butcher, Вы писали:

B>Вы используете MFC сокеты?

B>Вот тут нверно нужно быть менее жестоким
B>if (BufLen<=0) break; и проверять, что возвращает WSAGetLastError(), соответсвтенно читать только тогда, когда есть что читать..

Да, я использую CAcyncSocket:
////////////////////////////////////////////////////////////////////////////
if (!ServerSocket.Create(0,SOCK_STREAM,FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,NULL))
{
AfxMessageBox("&Icirc;&oslash;&egrave;&aacute;&ecirc;&agrave; &ntilde;&icirc;&ccedil;&auml;&agrave;&iacute;&egrave;&yuml; &ntilde;&icirc;&ecirc;&aring;&ograve;&agrave;");
}
ServerSocket.Connect(serverIP,110);
////////////////////////////////////////////////////////////////////////////
Проверил что возвращает функция GetLastError() — она говорит WSAEWOULDBLOCK, т.е. как написано в МСДН нет входящих данных. На самом деле, если пройти этот кусок кода дебаггером, то данные получим. Принимается даже двухметровый файл. Когда в режиме выполнения, то — WSAEWOULDBLOCK . Если я скажу читать с сокета и при WSAEWOULDBLOCK, то по какому признаку выходить из цикла??? Я думаю что в режиме выполнения почтовый сервер не успевает запихивать данные в сокет, а программа думает что данные кончились. Как быть???
Re[3]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 11.06.04 05:51
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>Да, я использую CAcyncSocket:

Z>Проверил что возвращает функция GetLastError() — она говорит WSAEWOULDBLOCK, т.е. как написано в МСДН нет входящих данных. На самом деле, если пройти этот кусок кода дебаггером, то данные получим. Принимается даже двухметровый файл. Когда в режиме выполнения, то — WSAEWOULDBLOCK . Если я скажу читать с сокета и при WSAEWOULDBLOCK, то по какому признаку выходить из цикла??? Я думаю что в режиме выполнения почтовый сервер не успевает запихивать данные в сокет, а программа думает что данные кончились. Как быть???

WSAEWOULDBLOCK означает, что операция ввода/вывода требует блокировки для удачного завершения, другими словами в данный момент система не готова вам отдать данные. Когда вы в дебагере принмаете, то между каждым вызовом recv система успевает принять данные, обработать их соответсвующим образом и отдать вам.
Что делать вам: вам нужно изменить алгоритм приёма, т.е. читать данные до ошибки WSAEWOULDBLOCK, потом ждать следующего FD_READ и читать дальше, в случае возниконовения другой ошибки обрабатывать её соответствующим образом.

Нет ничего невозможного..
Re[2]: CAsyncSocket&Receive
От: zaxs  
Дата: 11.06.04 06:40
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Простейший способ — заменить async socket на Berkeley вызовы, и вот тогда все заработает без Sleep, конечно.


MSS>Async socket дает много функциональности, которая в данном случае просто лишняя и ненужная (за исключением случая, когда нужен минимум нагрузки процессора на гигабитной сети).


Да, видимо придется пользоваться блокирующим сокетом, а не асинхронным. Надо было сразу таким пользоваться. Решил МФСишными функциями пользоваться и зря. С ними одни проблемы — даже в многопоточном режиме они глючат в версии 6 и ниже. Спасибо за совет.
Re[4]: CAsyncSocket&Receive
От: zaxs  
Дата: 11.06.04 10:40
Оценка:
Здравствуйте, butcher, Вы писали:

B>WSAEWOULDBLOCK означает, что операция ввода/вывода требует блокировки для удачного завершения, другими словами в данный момент система не готова вам отдать данные. Когда вы в дебагере принмаете, то между каждым вызовом recv система успевает принять данные, обработать их соответсвующим образом и отдать вам.

B>Что делать вам: вам нужно изменить алгоритм приёма, т.е. читать данные до ошибки WSAEWOULDBLOCK, потом ждать следующего FD_READ и читать дальше, в случае возниконовения другой ошибки обрабатывать её соответствующим образом.

Точно так. Стормозил и неправильно понял МСДН — библию программиста . Т.е. проблема решаема. Спасибо. Только я вот почитал ответ от "Maxim S. Shatskih" и сделал аналогичный пример, но на блокирующих сокетах без использования МФС. Там действительно не нужно делать Sleep. Вот как выглядит последний самый интересный кусок кода:
////////////////////////////////////////////////////////
CStdioFile zaxs;
zaxs.Open("letter",CFile::modeCreate|CFile::modeWrite);

while(bait>0)
{
bait=recv (srv_socket , szBuf, 65536, 0);
zaxs.Write(szBuf,bait);
}
zaxs.Close();
AfxMessageBox("файл скачан");
closesocket (srv_socket);
/////////////////////////////////////////////////////////////////
Т.е. принимает она нормально, но до поры до времени — пока данные не кончатся, а потом тормоза... В принципе как в теме CAcyncSocket&recv указано, можно через list узнать сколько байт точно нужно читать, но решение честно говоря некрасивое. Неужели нет другого выхода???
Re[2]: CAsyncSocket&Receive
От: zaxs  
Дата: 11.06.04 10:43
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Простейший способ — заменить async socket на Berkeley вызовы, и вот тогда все заработает без Sleep, конечно.


MSS>Async socket дает много функциональности, которая в данном случае просто лишняя и ненужная (за исключением случая, когда нужен минимум нагрузки процессора на гигабитной сети).


сделал аналогичный пример, но на блокирующих сокетах без использования МФС. Там действительно не нужно делать Sleep. Вот как выглядит последний самый интересный кусок кода:
////////////////////////////////////////////////////////
CStdioFile zaxs;
zaxs.Open("letter",CFile::modeCreate|CFile::modeWrite);

while(bait>0)
{
bait=recv (srv_socket , szBuf, 65536, 0);
zaxs.Write(szBuf,bait);
}
zaxs.Close();
AfxMessageBox("файл скачан");
closesocket (srv_socket);
/////////////////////////////////////////////////////////////////
Т.е. принимает она нормально, но до поры до времени — пока данные не кончатся, а потом тормоза... В принципе как в теме CAcyncSocket&recv указано, можно через list узнать сколько байт точно нужно читать, но решение честно говоря некрасивое. Неужели нет другого выхода???
Re[5]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 11.06.04 11:13
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>Точно так. Стормозил и неправильно понял МСДН — библию программиста . Т.е. проблема решаема. Спасибо. Только я вот почитал ответ от "Maxim S. Shatskih" и сделал аналогичный пример, но на блокирующих сокетах без использования МФС. Там действительно не нужно делать Sleep. Вот как выглядит последний самый интересный кусок кода:

Дак на то они и блокирующие сокеты.

Z>Т.е. принимает она нормально, но до поры до времени — пока данные не кончатся, а потом тормоза... В принципе как в теме CAcyncSocket&recv указано, можно через list узнать сколько байт точно нужно читать, но решение честно говоря некрасивое. Неужели нет другого выхода???


Нужно делать select перед вызовом recv, если select скажет вам что можно читать — вперёд, иначе попадаете в блокировку.
Воспользуйтесь поиском по форуму, неделю или две назад я кидал сюда пример с использованием select.

Нет ничего невозможного..
Re[6]: CAsyncSocket&Receive
От: zaxs  
Дата: 11.06.04 11:39
Оценка:
Здравствуйте, butcher. Сделал обработку ошибок:
while (BufLen>0)
{
Sleep(100);
BufLen=ServerSocket.Receive(lpBuf,65536,0);
// tema.Format("%d",BufLen);
// AfxMessageBox(tema);

// write to the file
if (BufLen<=0)
{
equal_flag=ServerSocket.GetLastError();
SetLastError(10000);
// equal_flag=ServerSocket.GetLastError();
if(equal_flag==WSAEWOULDBLOCK)
{
BufLen=1;
continue;
}
break;
}
fletter.Write(lpBuf,BufLen);
}
только очередная засада: даже когда данные кончаются все равно выдается ошибка WSAEWOULDBLOCK. Думал что он запомнил последнюю ошибку и не обновляет ее поэтому вставил строку: SetLastError(10000); но все равно продолжает генерироваться такая ошибка.
Re[3]: CAsyncSocket&Receive
От: Maxim S. Shatskih Россия  
Дата: 11.06.04 12:46
Оценка:
Z>bait=recv (srv_socket , szBuf, 65536, 0);
Z>zaxs.Write(szBuf,bait);

Вот перед Write надо bait на нуль проверить. Оно станет нулем в конце потока.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[7]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 15.06.04 04:04
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>Здравствуйте, butcher. Сделал обработку ошибок:

Z>while (BufLen>0)
Z>{
Z> Sleep(100);
Z> BufLen=ServerSocket.Receive(lpBuf,65536,0);
Z>// tema.Format("%d",BufLen);
Z>// AfxMessageBox(tema);

Z> // write to the file

Z> if (BufLen<=0)
Когда функция Receive возвращает 0 — это означает, что соединение закрыто с "той" стороны, это не ошибка и это нужно обрабатывать соответсвующим образом.

Z> {

Z> equal_flag=ServerSocket.GetLastError();
Z> SetLastError(10000);
Z>// equal_flag=ServerSocket.GetLastError();
Z> if(equal_flag==WSAEWOULDBLOCK)
Z> {
Z> BufLen=1;
Z> continue;
Z> }
Z> break;
Z> }
Z> fletter.Write(lpBuf,BufLen);
Z>}

Z>только очередная засада: даже когда данные кончаются все равно выдается ошибка WSAEWOULDBLOCK. Думал что он запомнил последнюю ошибку и не обновляет ее поэтому вставил строку: SetLastError(10000); но все равно продолжает генерироваться такая ошибка.

Я же говорю, в Re[3]: CAsyncSocket&amp;Receive
Автор: butcher
Дата: 11.06.04
, эта ошибка будет произходить всегда при работе с неблокирующими сокетами, когда результат не может быть возвращён немедленно.

Нет ничего невозможного..
Re[8]: CAsyncSocket&Receive
От: Аноним  
Дата: 16.06.04 06:58
Оценка:
Снова жопа: написал код блокирующими сокетами и сделал вызов select
сначала все было круто, а потом я снова попал. селект все время возвращает 0 — выходит по таймауту. И когда там данных нет, и когда их туда не успевают засунуть. Снова таже бодяга: увеличивать таймаут в ущерб производительности, а вдруг там окажется файл чуть больше чем я предполагаю?
Re[9]: CAsyncSocket&Receive
От: zaxs  
Дата: 16.06.04 07:10
Оценка:
видимо не получится корректно скачивать файл чисто системными средсвами — придется делать проверку на точку в конце данных и тогда прекращать прием данных с сокета.
Re[9]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 16.06.04 07:48
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Снова жопа: написал код блокирующими сокетами и сделал вызов select

А>сначала все было круто, а потом я снова попал. селект все время возвращает 0 — выходит по таймауту. И когда там данных нет, и когда их туда не успевают засунуть. Снова таже бодяга: увеличивать таймаут в ущерб производительности, а вдруг там окажется файл чуть больше чем я предполагаю?

Что значит "увеличивать таймаут в ущерб производительности", вы можете определить величину таймаута после которого вы будете "думать, что всё плохо". После этого таймаута рвёте связь и считаете, что передача не удалась.
Величина таймаута, когда "всё нормально" — не влияет на производительность, так как select не "держит" программу пока не истечёт таймаут, а "отпускает" как только данные появятся.

Нет ничего невозможного..
Re[10]: CAsyncSocket&Receive
От: zaxs  
Дата: 16.06.04 07:53
Оценка:
Здравствуйте, butcher
а может я что-то не так делаю?

struct fd_set zaxs12;
zaxs12.fd_count=1;
zaxs12.fd_array[0]=srv_socket;
int flag,err;
struct timeval timez;
timez.tv_sec=1;
timez.tv_usec=0;

while(1)
{
flag=select(0,&zaxs12,NULL,NULL,&timez);
if(flag==0)
{
zaxs12.fd_count=1;
continue;
}
if(flag==1)
{
bait=recv (srv_socket , szBuf, 32768, 0);
zaxs.Write(szBuf,bait);
}
else
{
err=WSAGetLastError();
temp.Format("%d",err);
AfxMessageBox(temp);
break;
}
}
дело в том, что я данные не все получаю как выяснилось, а только процентов 99. и функция селект все время выдает 0.
Re[11]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 16.06.04 08:55
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>Здравствуйте, butcher

Z>а может я что-то не так делаю?

Z>struct fd_set zaxs12;
Z>int flag,err;
Z>struct timeval timez;

Z>while(1)
Z>{
        FD_ZERO(&zaxsl2);
        FD_SET(srv_socket, &zaxsl2);
        timez.tv_sec=60;
        timez.tv_usec=0;

        flag = select(0, &zaxs12 ,NULL, NULL, &timez);
        if(flag == SOCKET_ERROR) {
            /* error handling */
        } else if(flag == 1){
            bait = recv (srv_socket , szBuf, 65535, 0);
            if(bait == SOCKET_ERROR){
                /* error handling */
            } else if(bail != 0){
                zaxs.Write(szBuf,bait);
            } else {
                /* close connection */
            }
        }
Z>}

что-то типа такого..

Нет ничего невозможного..
Re[12]: CAsyncSocket&Receive
От: zaxs  
Дата: 16.06.04 09:20
Оценка:
так примерно я и делаю и даже использую эти макросы, результат тот же. Но ведь это как то реализовано! например в оутлуке или еще где то.
Re[13]: CAsyncSocket&Receive
От: butcher Россия http://bu7cher.blogspot.com
Дата: 16.06.04 09:25
Оценка:
Здравствуйте, zaxs, Вы писали:

Z>так примерно я и делаю и даже использую эти макросы, результат тот же.

значит что-то не так делаете
Почему же вы тогда код приводите не такой?

Z>Но ведь это как то реализовано! например в оутлуке или еще где то.

А кто сомневается, если делать правильно — всё будет работать, даже с асинхронными сокетами

Нет ничего невозможного..
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.