Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

WSAWaitForMultipleEvents не отпускает по FD_ACCEPT


ProgRAMmer Dimonych ©   (2011-12-16 16:37:00) 

Доброго времени суток. Набросал простенькую заготовку TCP-сервера, слушающего входящие соединения и создающего для каждого новый поток. Делаю WSACreateEvent()-WSAEventSelect() после чего вызываю WSAWaitForMultipleEvents() для двух event'ов: привязанного к слушающему сокету и моего, выставляемого при завершении приложения.

procedure TfrmMain.FormCreate(Sender: TObject);
var
 wsadata    : TWSAData;
 dwThreadId : Cardinal;
begin
 WSAStartup(2, wsadata);
 hTerminateEvent := CreateEvent(nil, True, False, nil);
 hLstnThread := BeginThread(nil, 0, @Thread_Listen, nil, 0, dwThreadId);
end;

function Thread_Listen(lpParams: Pointer): LRESULT;
var
 hLstnSocket : TSocket;
 hTermEvent  : THandle;
 hLstnEvent  : THandle;
 hDataSocket : TSocket;
 addr        : TSockAddrIn;
 addrlen     : Integer;
 ne          : TWSANetworkEvents;
begin
 Result := 0;
 hLstnSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if hLstnSocket = INVALID_SOCKET then Exit;
 hLstnEvent := WSACreateEvent;
 if hLstnEvent = 0 then Exit;
 hTermEvent := hTerminateEvent;

 ZeroMemory(@addr, SizeOf(addr));
 addr.sin_family      := AF_INET;
 addr.sin_port        := htons(7);
 addr.sin_addr.S_addr := INADDR_ANY;

 if bind(hLstnSocket, @addr, SizeOf(addr)) <> 0 then Exit;
 if listen(hLstnSocket, SOMAXCONN) <> 0 then Exit;
 if WSAEventSelect(hLstnSocket, hLstnEvent, FD_ACCEPT or FD_CLOSE) <> 0 then Exit;

 while WSAWaitForMultipleEvents(2, @hTermEvent, False, INFINITE, True) = WAIT_OBJECT_0+1 do
 begin
   WSAEnumNetworkEvents(hLstnSocket, hLstnEvent, @ne);
   hDataSocket := accept(hLstnSocket, addr, addrlen);
   CreateServiceThread(hDataSocket);
 end;  
end;


Беда в том, что WSAWaitForMultipleEvents() уводит поток в ожидание до срабатывания hTermEvent. В это время клиент успешно устанавливает соединение с сервером и отправляет данные, но сервер в тело цикла не входит :( Уже всё, что могло в голову прийти, перерыл :(

В чём может быть причина?


Сергей М.   (2011-12-17 21:47:00) [1]

> Ну-ну-ну, нет такого правила

Есть, если под лок.переменные задействуется стековый фрейм с EBP-адресацией.

Убедись сам на приведенном мною коде, включив опцию "Stack frames" и/или выключив опцию "Optimization"


procedure Params(var A, B, C: Integer);
begin
 WriteLn(IntToHex(Integer(@a), 8), ' = ', a);
 WriteLn(IntToHex(Integer(@b), 8), ' = ', b);
 WriteLn(IntToHex(Integer(@c), 8), ' = ', c);
end;

procedure proc;
var
A : Integer;
B : Integer;
C : Integer;
begin
A := 10;
B := 20;
C := 30;
Params(A, B, C);
end;

..

proc;


ProgRAMmer Dimonych   (2011-12-16 23:10:00) [2]

> [3] Сергей М. ©   (16.12.11 17:23)
> > Delphi, видимо, решила по-своему разместить локальные
> переменные
> Она разместила их в стеке по известному правилу - переменные
> размещается в стеке в порядке обратном их объявлению.

Ну-ну-ну, нет такого правила. Есть соглашения вызова типа stdcall, определяющие порядок передачи аргументов. Но в данном случае речь идёт о локальных переменных. Набросал тестовый проектик (процедурка с локальными переменными), побрал указатели от локальных переменных, чтобы Delphi не вздумала в регистрах их держать всё время (вот это по ходу и было в моём случае), и открыл окно CPU:



var
 A : Integer;
 B : Integer;
 C : Integer;
begin
 A := 10;
 B := 20;
 C := 30;
 Params(@A, @B, @C);
end;

=======

A := 10;
mov [esp], $0000000a
B := 20;
mov [esp+4], $00000014
C := 30;
mov [esp+8], $0000001e

; Дальше вызов процедуры, параметры определяются через LEA
...


Соответственно


> По идее WSAWaitForMultipleEvents и висеть-то не должна в
> ожидании hTermEvent - один из двух тобой указанных хендлов
> не является хэндлом объекта синхронизации, потому ф-ция
> должна бы вернуть WSA_WAIT_FAILED


не вело себя так на самом деле, потому что оказывался там не хэндл сокета, а что-то другое:

* либо неинициализированный к тому времени hDataSocket
* либо первые 4 байта sockaddr_in - но они меньше похожи на хэндл объекта синхронизации.

Вот такое расследование :)


Сергей М.   (2011-12-16 17:29:00) [3]


> в тело цикла не входит


По идее WSAWaitForMultipleEvents и висеть-то не должна в ожидании hTermEvent - один из двух тобой указанных хендлов не является хэндлом объекта синхронизации, потому ф-ция должна бы вернуть WSA_WAIT_FAILED


Сергей М.   (2011-12-16 17:24:00) [4]


> а конкретно hLstnEvent

hLstn Socket


Сергей М.   (2011-12-16 17:23:00) [5]


> в стеке за hTermEvent оказывался не hLstnEvent, а что-то
> ещё


Не "что-то еще", а конкретно hLstnEvent


> Delphi, видимо, решила по-своему разместить локальные переменные


Она разместила их в стеке по известному правилу - переменные размещается в стеке в порядке обратном их объявлению.


ProgRAMmer Dimonych   (2011-12-16 17:18:00) [6]

> [1] Сергей М. ©   (16.12.11 16:53)
> А ты и не ждешь hLstnEvent

Уже разобрался. Ждать-то я его ждал. Точнее должен был ждать, но Delphi, видимо, решила по-своему разместить локальные переменные и в стеке за hTermEvent оказывался не hLstnEvent, а что-то ещё. В результате действительно получалось, что не ждал. Подвела ассемблерная привычка :) Решил созданием локального массива на 2 элемента.


Сергей М.   (2011-12-16 16:53:00) [7]

А ты и не ждешь hLstnEvent


Наверх

Титульная страница Поиск, карта сайта Написать письмо