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

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

WinSock + Threads! [D7, WinXP]


Страницы:   1 2

Nigger ©   (2003-04-27 19:09:00) 

Мастера... у меня большая проблемма! Пишу программу клиент с WinSock"ом и потоками... если делаю чтоб несколько потоков было,то на вторые пакеты ответ с сервера не идет (создается впечатление, что это один сокет с N-м количеством одинаковых пакетов - то есть все сокеты работают как один "большой") Может все дело в блокированых и неброкированых сокетах или потоках???
Как вообще реализовать многопоточное соединение клиента на WinSock???
И ещё: прога ругается что OutOfMemory - но это похоже, что с другим связано. И про recv хотел ещё спросить... что за странный тип переменной там используется - может я его как то не правильно использую? Помогите разобраться!

var
Buff: pchar;
z: string;

GetMem(Buff,size);
FillChar(Buff[1],size,0);
ret:=recv(S,Buff^,size,0);
z:=buff;
FreeMem(Buff, size);


Nigger   (2003-05-05 14:10:00) [1]

2 nikkie: хмм....спасибо за ссылку... я буду использовать все методы, включая этот пока не найду оптимальный.


nikkie   (2003-05-05 13:46:00) [2]

>1) Компоненты Client\ServerSocket или TCPClient\Server - работают медленно и в некоторых случиях некорректно, так что рекомендуется использовать WinSockets

Здрасьте... Это кто ж такое говорил-то? Ты изначально задал вопрос про recv() задал, про компоненты никто и речи не было.

>2) Легче использовать вместо TThread асинхронное подключение.

По моему опыту, программирование асинхронных сокетов выходит гораздо сложнее с точки зрения программирования логики протокола.

Я пробовал писать программы с использованием как стандартных компонент, так и компонент ICS. Потом услышал о библиотеке Winshoes и, попробовав ее, окончательно переключился на нее. Через некоторое время ее переименовали в Indy. Очень доходчивое описание плюсов блокирующих сокетов (а также откуда взялись асинхронные сокеты на windows :)) можно прочитать в статье Chad Z. Hower (Kudzu) "Introduction to Indy".

http://www.swissdelphicenter.ch/en/showarticle.php?id=4


Nigger   (2003-05-05 07:27:00) [3]

Вообщим подведу итог для себя и для тех кому данная проблема интересна, поправьте если что-то не то сказал....
1) Компоненты Client\ServerSocket или TCPClient\Server - работают медленно и в некоторых случиях некорректно, так что рекомендуется использовать WinSockets.
2) Легче использовать вместо TThread асинхронное подключение.
3) Лучше сразу повешаться, чем все это понять.

Спасибо Alex Konshin, Polevi, Digitman, Illusion за продолжительную дисскусию и исчерпывающие ответы на всю эту тему...
Мне этой информации будет вполне достаточно, чтобы закончить написание этой ....ной программы просто пока времени не было с этим разбираться (в другой город ездил)...
И ещё один маленький вопрос на последок про функции RecvFrom
и SendTo: эти функции получают и отправляют информацию по заданному сокету???? То есть можно например все таки сделать несколько потоков и в них поместить эти функции(если они конечно, для заданного сокета)??? Или спрашиваю еще проще можно например с функцией RecvFrom получить данные с разных сокетов в разных нитях и в один и тот же момент??? или я сильно показалось..... перекрестюсь на всякий случай....


Polevi   (2003-05-04 09:29:00) [4]

2Alex Konshin © (01.05.03 10:44)
прошу прощение за вчерашний пост, ерунду какуюто написал, сорри


Polevi   (2003-05-03 11:52:00) [5]

2Alex Konshin © (01.05.03 10:44)
completion routines вообщето не имеет прямого отношения wincosk api, и некоторые уважаемые авторы не рекомендуют использовать ReadFile и WriteFile при работе с сокетами
и управлять транспортным потоком из другого процесса именованым ивентом это и есть извращение
и к флагам тоже не надо придираться
приведенный мной вариант транспорта весьма прост, и в этом его преимущество, имхо
а понты и пальцы не надо тут растопыривать
>Работает в overlapped режиме с completion routines.
я плакаль

с уважением


Illusion   (2003-05-01 20:41:00) [6]

Блин, ну вы тут развели... Слов нет...


Alex Konshin   (2003-05-01 10:44:00) [7]

2 Polevi: не надо мне объяснять как работает Winsock :)
Кто тебя заставляет для каждого сокета создавать Event?
Кто тебе мешает использовать completion routines?
Можно обойтись даже одним Event.

2 Digitman: а Event использовать религия не позволяет?
Если его еще и именованным сделать, то тогда не только из другого потока, но даже из другого процесса можно будет управлять. Кстати, я знаю, для чего, где и как применяется MsgWaitForMultipleObjects, и мне в любом случае непонятно, зачем там ждать QS_ALLINPUT, хотя оно конечно, чем шире откроешь, тем толще события ловить будешь :)

Ну и зачем транспорту окно?
Если приложению окно все-таки нужно, то пусть этим занимается та, нить, которой это более сподручно, а именно главная нить VCL.

Не, друзья, я решительно не понимаю, почему вы так хотите создать себе трудностей, чтобы потом героически их преодолеть и в результате получить еще и урезанную функциональность.


Polevi   (2003-04-30 13:55:00) [8]

>Alex Konshin © (30.04.03 12:16)
>Ну вот скажите, ну зачем транспорту окно, пусть даже и невидимое?
чтобы передать его хендл в WSAAsyncSelect

и насколько я помню ф-ии ожидания, подобные MsgWaitForMultipleObjects не могут ждать более 64 объектов, это довольно существенный недостаток для некоторых задач, транспорт который я привел используется в прокси сервере, на каждого клиента 2 сокета, так что в одном потоке может быть не более 32 клиентов, у меня их больше 100. Можно конечно организовать пул потоков, но это усложнит задачу.


Digitman   (2003-04-30 12:52:00) [9]


> Alex Konshin


В принципе, я с тобой согласен. Реализация транспорта с использованием событийной модели нотификации во многих случаях наглядней и эффективней.
Единственное, с чем могу не согласиться, так это с фразой :


> используется MsgWaitForMultipleObjects ..чем это лучше WSAWaitForMultipleEvents


WSAWaitForMultipleEvents не реализует ожидания сообщений потоку, только - ожидание событий. Если же потоку-диспетчеру требуется срочным образом известить транспортный поток о необходимости немедленно закрыть транспорт и "закруглиться по хозяйству", то наилучший способ сделать это - послать транспортному потоку сообщение с помощью PostThreadMessage. Но если трансп.поток в это время блокирован вызовом WSAWaitForMultipleEvents, он не сможет принять/диспетчеризовать/обработать это сообщение !
MsgWaitForMultipleObjects же, включая в себя полную функциональность от WSAWaitForMultipleEvents, позволяет еще и ждать сообщения, адресованные транспортному потоку.

т.о. желательный в этом случае "джентльменский набор" WS2-ф-ций :

MsgWaitForMultipleObjects + WSAEnumNetworkEvents


Alex Konshin   (2003-04-30 12:16:00) [10]

Не, я, конечно, понимаю, что гланды через разные отверстия выдирать можно...
Ну вот скажите, ну зачем транспорту окно, пусть даже и невидимое? И ведь один хрен используется MsgWaitForMultipleObjects и организуется цикл ожидания-обработки. И чем это лучше WSAWaitForMultipleEvents? Тем, что нужно еще написать кучу неотносящегося к делу кода? И ради чего? Так не проще ли по-нормальному сделать? Не, я понимаю, что в Winsock 1.x нельзя иначе, но кто ж вас заставляет его использовать-то?


Polevi   (2003-04-30 10:08:00) [11]

constructor TClient.Create(ATransport: TTransport);
var
i:integer;
begin
FTransport:=ATransport;
FIndex:=-1;
with FTransport.FClientList.LockList do
try
for i:=0 to Count-1 do
if Items[i]=nil then
begin
FIndex:=i;
break;
end;
if FIndex=-1 then
FIndex:=Add(self)
else
Items[FIndex]:=self;
finally
FTransport.FClientList.UnlockList;
end;
end;


procedure TClient.Listen(AAddress:string;APort: Word);
begin
FSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
FAddr.sin_family := AF_INET;
if AAddress<>"" then
FAddr.sin_addr.s_addr := inet_addr(PChar(AAddress))
else
FAddr.sin_addr.s_addr := INADDR_ANY;
FAddr.sin_port := htons(APort);
if bind(FSocket, FAddr, SizeOf(FAddr))=SOCKET_ERROR then
raise Exception.Create(Format("Cannot bind on port %d",[APort]));

if Winsock.listen(FSocket, SOMAXCONN)=SOCKET_ERROR then
raise Exception.Create(Format("Cannot listen on port %d",[APort]));
WSAAsyncSelect(FSocket,FTransport.FWindowHandle,WM_SOCKETMESSAGE+FIndex, FD_ACCEPT or FD_CLOSE);
end;



procedure TClient.Connect(Address: string; Port: Word);
var
Err:DWORD;
begin
FSocket:=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
WSAAsyncSelect(FSocket,FTransport.FWindowHandle,WM_SOCKETMESSAGE+FIndex, FD_READ or FD_WRITE or FD_CLOSE or FD_CONNECT);
FAddr.sin_family:=AF_INET;
FAddr.sin_addr.s_addr :=inet_addr(PChar(Address));
FAddr.sin_port := htons(Port);
if WinSock.connect(FSocket, FAddr, SizeOf(FAddr))=SOCKET_ERROR then
begin
Err:=WSAGetLastError;
if Err<>WSAEWOULDBLOCK then
Raise Exception.Create(Format("Cannot connect to %s:%d - WSAGetLastError return %d",[Address,Port,Err]));
end
else
OnConnect;
end;


procedure TClient.Accept(AListenClient:TClient);
var
// Addr: TSockAddrIn;
Len: Integer;
begin
FListenClient:=AListenClient;
Len := SizeOf(FAddr);
FSocket:=WSAAccept(AListenClient.FSocket, @FAddr, @Len, @ConditionProc,DWORD(self));
if FSocket=INVALID_SOCKET then
raise Exception.Create(Format("Cannot accept client - WSAGetLastError return %d.",[WSAGetLastError]));
WSAAsyncSelect(FSocket,FTransport.FWindowHandle,WM_SOCKETMESSAGE+FIndex, FD_READ or FD_WRITE or FD_CLOSE);
FAddr.sin_port:=AListenClient.FAddr.sin_port;
end;


Polevi   (2003-04-30 10:01:00) [12]

2Nigger © (29.04.03 17:46)
вот тебе ядро транспорта, если что пиши на e-mail

procedure TTransport.Execute;
var
msg:TMsg;
wc:WNDCLASS;
client:TClient;
i:integer;
begin
try
with wc do
begin
style:= 0;
lpfnWndProc:= @DefWindowProc;
cbClsExtra:= 0;
cbWndExtra:= 0;
hInstance:= 0;
hIcon:= 0;
hCursor:= 0;
hbrBackground:= 0;
lpszMenuName:= nil;
lpszClassName:= "FCTransportWndClass";
hInstance := HInstance;
end;
Windows.RegisterClass(wc);
FWindowHandle:=CreateWindowEx(WS_EX_TOOLWINDOW, wc.lpszClassName,
"", WS_POPUP {!0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
if FWindowHandle<>0 then
SetEvent(FTransportReadyEvent); //флаг готовности транспорта

while not Terminated do
case MsgWaitForMultipleObjects(1, FWaitEvent, False, INFINITE, QS_ALLINPUT) of //ждем флаг завершения или сообщение windows
WAIT_OBJECT_0:
Terminate; //был установлен флаг завершения
WAIT_OBJECT_0 + 1:
with FClientList.LockList do //enter critical section
begin
try
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do //в очереди есть сообщения, выбираем их
begin
client:=nil;
try
client:=Items[msg.Message-WM_SOCKETMESSAGE]; //получем объект
if not Assigned(client) then
Continue
else
if HIWORD(msg.lParam)<>0 then
client.OnError(LOWORD(msg.lParam),HIWORD(msg.lParam))
else
case LOWORD(msg.lParam) of //тип события winsock
FD_ACCEPT:client.OnAccept;
FD_CONNECT:client.OnConnect;
FD_READ:Read(client);
FD_WRITE:Write(client);
FD_CLOSE:client.Free;
end;
except on E:Exception do
begin
if E.Message<>"" then Writeln(E.Message);
if Assigned(client) then client.Free;
end;
end;
end;
finally
FClientList.UnlockList; //leave critical section
end;
end;
end;
finally
DestroyWindow(FWindowHandle);
Writeln("Transport thread terminated.");
end;
end;


Alex Konshin   (2003-04-30 03:54:00) [13]

Но это же не значит, что это никому не по силам, не так ли?
И не так уж там и все и сложно. А в один сокет ты не запихнешь, соединений-то много. В один поток, конечно можно, но задачи разные бывают...


Illusion   (2003-04-29 23:44:00) [14]

2Alex Konshin, с несколькими потоками в ансихронном протоколе НИКОГДА не сталкивался... Хотя бы потому, что написать вещь, которую будет правильно всё разбирать и т.п.... Это ТАКОЙ ГИМОР! Чем меньше поток, тем лучше. Вообще всё можно запихнуть в ОДИН сокет... АБСОЛЮТНО ВСЁ. Главное - хорошо продумать каждый тип сообщений, и как это будет разбиратся. Но писать асинхронные протоколы в несколько потоков... Слишком много камней там...


Alex Konshin   (2003-04-29 22:30:00) [15]

2 Polevi: НЕ ВЫДИРАЙ ФРАЗЫ ИЗ КОНТЕКСТА. Я сказал:

Вот как раз-таки под Winsock 1.xпытаться работать в нескольких потоках и есть извращение.

Согласись, смысл совсем иной.

2 Illusion: Надо ждать почти всегда. Обработка либо должна быть быстрой, либо должна передаваться другим нитям. WSAAsyncSelect работает через очередь сообщений windows, которая привязана к конкретной нити (у тебя к главной нити VCL), так что тебе повезло, что ты и не пытался работать этой функцией с несколькими потоками. Я не говорю, что это невозможно, я говорю, что это будет выглядеть не так просто, как ты думаешь - нужно будет самому организовывать цикл обработки сообщений windows, а учитывая еще и то, что TThread сам создает окно, то ты поимешь много развлечений и надолго. При этом ты не получаешь никакой выгоды, только лишний повод для мата. Работа же через Event намного прозрачнее и проще для понимания.

2 Nigger: У меня юнит слишком сложный, и если ты только что начал разбираться с Delphi, то тебе это будет просто не по зубам.
Он работает и с TCP, и с SPXII. Там создается по одной нити на каждое соединение, в каждой нити организуется цикл ожидания-обработки сообщений. Работает в overlapped режиме с completion routines. В добавок реализация классов для нитей своя (не TThread). Боюсь, что это будет слишком сложно для начинающего.


Nigger   (2003-04-29 17:46:00) [16]

2Illusion:
Спасибо... я лучше со старыми классами поработаю - копаться в чем то новом ещё больше времени убьет.
2Polevi:
Смотрел, мало что понял... мне хотябы простой пример посмотреть.


Polevi   (2003-04-29 12:52:00) [17]

2Nigger © (29.04.03 11:59)
посмотри как сделано у борланда - scktcomp.pas

2Illusion © (29.04.03 12:37)
если сообщений не ждать c WSASyncSelect тоже ничего не придет


Illusion   (2003-04-29 12:37:00) [18]

смысл использовать WSAWaitForMultipleEvents, если в винде есть WSAsyncSelect? С ним на много удобней реализовывать ансихнорнные протоколы, когда сообщение может придти в любую секунду, и совесм не факт, что оно придёт, или придёт тогда, когда его будут ждать... Использую эту функцию можно вообще про треды забыть. Всё будет скидыватся в обрабочтик (который сам задаш в Application::OnMessage)...

2 Nigger, могу скинуть готовые классы для работы с TCP и UDP. ТОлько они написанны на С++ и мною.. Так что за безглючность не отечаю :).


Nigger   (2003-04-29 11:59:00) [19]

2Alex Konshin:
Я уже совсем этим законопатился по самое улыбаться... Не мог бы ты привести простейший (работающий) код для инициализации, подключения, отправки и приема данных в асинхронном режиме под WSA функциями... очень прошу! Потому, что я сел за делфи только три недели назад и толком не знаю ни структуры ни каких-нибудь особенных переменных etc... А если можешь - скинь готовый проект с твоим юнитом, буду жутко благодарен, т.к я думаю на разгребание WS функций уйдет неделя или даже не одна...
2Digitman: в процессом кл.приложения создается N код.потоком, в ходе работы каждого из которых создается ОТДЕЛЬНОЕ гнездо (ассоциированное с отдельным код.потоком) и устанавливается соединение с ОТДЕЛЬНЫМ гнездом сервера
^^^^^^^^
я про это и хотел сказать... я не так просто выразился


Polevi   (2003-04-29 09:19:00) [20]

>Alex Konshin © (29.04.03 05:29)
>пытаться работать в нескольких потоках и есть извращение
на этот счет существуют разные точки зрения, хотя я тоже приверженец асинхронного режима


Страницы:   1 2

Наверх

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