![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Сообщения сокетов [D5, Win95/98, WinME, NT4, Win2k, WinXP]
Repeater © (2002-11-19 11:11:00)Вобщем такой вопрос. У меня организуется прозрачная передача данных от сервера к клиенту и обратно через мою прогу. Для каждого оединения создается нить в которой работает такой цикл.
While not cmd_stop Do Begin //Цикл работает до тех пор пока не получим команду завершить
//Ждем пока придет сообщение
res:=WSAWaitForMultipleEvents(2,@Events,False,1000,False);
Case res Of
WSA_WAIT_EVENT_0:Begin //Client
//Смотрим какие сообщения произошли
res:=WSAEnumNetworkEvents(threadsocket, Events[0], @NetEvnt);
//Если ошибка то глушим нить
If res=SOCKET_ERROR Then CloseThread;
//Если надо читать - читаем
If (NetEvnt.lNetworkEvents and FD_READ)>0 Then Begin
ZeroMemory(@buf,SizeOf(Buf));
//берем данные от клиента
rcv:=recv(threadsocket,Buf,Length(Buf),0);
gle:=WSAGetLastError;
if (rcv=0) or (gle=WSAECONNRESET) then CloseThread;
//отправляем серверу
snd:=send(ProxySock,Buf,rcv,0);
gle:=WSAGetLastError;
If snd=SOCKET_ERROR Then If WSAGetLastError<>WSAEINPROGRESS Then CloseThread;
End;
//Если разорвался коннект, то глушим нить
If (NetEvnt.lNetworkEvents and FD_CLOSE)>0 Then CloseThread;
End;
WSA_WAIT_EVENT_0+1:Begin //Server
//Смотрим какие сообщения произошли
res:=WSAEnumNetworkEvents(ProxySock, Events[1], @NetEvnt);
//Если ошибка то глушим нить
If res=SOCKET_ERROR Then CloseThread;
If NetEvnt.lNetworkEvents=0 Then CloseThread;
//Если надо читать - читаем
If (NetEvnt.lNetworkEvents and FD_READ)>0 Then Begin
ZeroMemory(@buf,SizeOf(Buf));
//берем данные от сервера
rcv:=recv(ProxySock,Buf,Length(Buf),0);
gle:=WSAGetLastError;
if (rcv=0) or (gle=WSAECONNRESET) then CloseThread;
//Отправляем клиенту
snd:=send(threadsocket,Buf,rcv,0);
gle:=WSAGetLastError;
If snd=SOCKET_ERROR Then If WSAGetLastError<>WSAEINPROGRESS Then CloseThread;
End;
//Если разорвался коннект, то глушим нить
If (NetEvnt.lNetworkEvents and FD_CLOSE)>0 Then CloseThread;
End;
End;
end; //end while
В принципе все работает. Только иногда нить зависает. То есть не приходят никакие сообщения, хотя один из сокетов уже закрыт. Может я че не так делаю? Почему не приходят эти сообщения?
Digitman (2002-11-21 17:24:00) [1]см. WinsockAPI : GetSockOpt()
Repeater (2002-11-21 17:21:00) [2]А откуда взять rcvbuf_size?
Digitman (2002-11-21 17:13:00) [3]1-й FD_WRITE просто извещает тебя о готовности буфера передачи гнезда. Можешь его проигногировать, если на этот момент нечего передавать.
FD_READ извещает тебя о том, что буфер приема не пуст и ты можешь попытаться прочитать из него (recv) ожидаемый по логике блок данных (но - оч желательно - не более чем rcvbuf_size)
Repeater (2002-11-21 17:08:00) [4]Спасибо за подробное объяснения.
Такой вопрос. Почему у меня сразу на один сокет приходят 2 сообщенеия FD_WRITE и FD_READ. Как их обрабатывать?
Digitman (2002-11-19 12:48:00) [5]send() не является ф-цией, непосредственно осуществляющей передачу. Этот вызов лишь ставит указанные тобой данные в хвост внутренней передающей очереди гнезда, из головы которой по мере готовности трансп.ресурсов ядром Winsock выбираются данные для факт.попытки отправки. При успешной отправке фрагмента данных из головы очереди в хвосте этой же очереди освобождается ровно столько места, сколько успешно отправлено.
Очередь передачи являет собой внутр.буфер в памяти, контролируемой ядром Winsock. Буфер не "резиновый" и имеет определ.размер. Если ты пытаешься вызовом send() поставить в очередь на передачу данные размером большим, чем позволяет "свободное" в дан.момент "место" в хвосте буфера-очереди, Winsock имеет право отвергнуть полностью или частично запрошенные к постановке в очередь данные.
При полном отвержении (*), если код ошибки = WSAEWOULDBLOCK, это означает, что попытку повтора передачи тех же данных тем же send-вызовом можно будет повторить не ранее, чем возникнет событие FD_WRITE, извещающее об освобождении очереди передачи.
При частичном отвержении допустимо повторно вызвать send() для попытки постановки в очередь оставшегося фрагмента отвергнутых в предыдущем send-вызове данных; можно повторять send-вызов в цикле, пока либо все оставшиеся данные не будут поставлены в очередь, либо см. (*)
Repeater (2002-11-19 12:21:00) [6]To Digitman
>>Где у тебя обработка события FD_WRITE ? У тебя же неблок.режим !
А мне это сообщение не нужно. Мне нужно только когда данные придут передать их из одного сокета в другой.
Не, ну может я чего то не понимаю. Обясни, пожалуйста.
Repeater (2002-11-19 12:18:00) [7]Спасибо, буду разбираться.
Digitman (2002-11-19 12:07:00) [8]в дополнение к Polevi © (19.11.02 11:44)
Структура NetworkEvents содержит коды ошибок для каждого из трансп.событий, перечисленных в массиве. Нет смысла запрашивать WSAGetLastError(), ибо, если трансп.метод вызвал ошибку (в т.ч. WASEWOULDBLOCK), возникнет соотв.событие с ненулевым кодом ошибки.
Polevi (2002-11-19 11:44:00) [9]в дополнение к Digitman © (19.11.02 11:32)
//отправляем серверу
snd:=send(ProxySock,Buf,rcv,0);
gle:=WSAGetLastError;
If snd=SOCKET_ERROR Then If WSAGetLastError<>WSAEINPROGRESS Then CloseThread;
где обработка WASEWOULDBLOCK ??
в этом коде при первом-же переполнении буфера winsock поток завершится, а надо сохранять буфер и пытаться отправить его при первом FD_WRITE
Digitman (2002-11-19 11:32:00) [10]Где у тебя обработка события FD_WRITE ? У тебя же неблок.режим !
Repeater (2002-11-19 11:15:00) [11]Да, забыл сказать. Ивенты установлены на все события
CliEvent:=WSACreateEvent;
ServEvent:=WSACreateEvent;
res:=WSAEventSelect(ServSock,ServEvent, FD_ALL_EVENTS);
res:=WSAEventSelect(threadsocket,CliEvent, FD_ALL_EVENTS);
Events[0]:=CliEvent;
Events[1]:=ServEvent;