Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2002.09.16;
Скачать: CL | DM;

Вниз

Ошибка 10022 при попытке послать данные   Найти похожие ветки 

 
Sapersky_   (2002-07-02 21:47) [0]

Привет всем!

Ситуация следующая: при подсоединении клиент посылает серверу информацию о том, что он клиент данного приложения и свою версию; сервер, если его это устраивает, посылает информацию о себе... и вот здесь лезет глюк (см. заголовок). Сообщение посылается следующим образом: перебираются ActiveConnections, находится нужный клиент (я их различаю по SocketHandle - полагаю, он у всех должен быть разный) и ему посылается информация. Мне знакомый посоветовал посылать ответ тому сокету, который был в параметрах OnRead - как ни странно, помогает (по идее это тот же самый сокет, что находится через ActiveConnections - SocketHandle у них одинаковые!) -
но, во-первых, это неудобно, во-вторых, иногда нужно слать информацию не в ответ на запрос клиента, а просто так (оповещение всем клиентам о присоединении нового, например) - в этом случае без ActiveConnections не обойтись...
Самое интересное, что на этой же библиотеке сделан чат, и он работает - хотя там немного другая система, не "запрос - ответ", а "пользователь ввёл данные - отправили, данные пришли - отобразили", т.е. получение и отправка никак не связаны.
Вообще, что такое 10022? В Help"е написано:
Socket not bound, invalid address or listen is not invoked prior to accept.
Тут скорее первое - но что это (not bound) значит?


 
Digitman ©   (2002-07-03 08:03) [1]

Приведи фрагмент кода.


 
Sapersky_   (2002-07-03 10:53) [2]

Функция отправки сообщения:

function TClientServer.SendDataTo(ClientID : Integer; AllExceptThis : Boolean; data : PByte; bCount : DWord): Boolean;
Var n, cID : Integer;
begin
Result:=True;
If bCount=0 then Exit;
If IsServer then With Server do
For n:=0 to ActiveConnections-1 do begin
cID:=Connections[n].SocketHandle;
If ((not AllExceptThis) and (cID=ClientID)) or
((AllExceptThis) and (cID<>ClientID)) then
If Connections[n].SendBuf(data^,bCount)<=0 // ошибка!
then Result:=False;
end;

end;

ClientID - это SocketHandle клиента, на запрос которого мы отвечаем; при получении сообщения (OnRead сервера) к нему присоединяется этот SocketHandle и оно добавляется в буфер, из которого его затем вынимает основная программа (кода здесь много, так что приводить не буду) и посылает ответ клиенту.

А вот такое работает:

function TClientServer.SendDataToSocket(Socket: TCustomWinSocket;
data : PByte; bCount : DWord): Boolean;
begin
Result:=True;
If bCount=0 then Exit;
If Socket.SendBuf(data^,bCount)<=0 then Result:=False;
end;

В данном случае в OnRead приходится запоминать весь Socket.
Но иногда, как я уже писал, требуется послать сообщение 1-м методом; а хранить для этой цели ещё один массив сокетов (в дополнение к стандартному Connections) не хочется.


 
Digitman ©   (2002-07-03 12:41) [3]

Приведи обработчик (или хотя бы - фрагмент) OnClientRead(), где ты читаешь у некоего объекта и запоминаешь (для дальнейшей идентификации) св-во SocketHandle.

Склонен предпологать, что читаешь ты это св-во не у наследника TServerClientWinSocket, а у наследника TServerWinSocket. Совершенно разные вещи !
Кр.того, концептуально неверно (или как минимум - некорректно) фиксировать SocketHandle в обработчике OnClientRead() - делать это нужно в OnClientConnect(). И фиксировать-таки гораздо корректнее не SocketHandle, а ссылку на экз-р TServerClientWinSocket (какая разница, что фиксировть в списке целочисленных идентификаторов - целое число, содержащее SocketHandle или то же целое число, содержащее ссылку на экз-р некоего TCustomWinSocket). При фиксации ссылок и цикл поиска в списке Connections[] упрощается :

function TClientServer.SendDataTo(Client : TCustomWinSocket; AllExceptThis : Boolean; data : PByte; bCount : DWord): Boolean;
Var n: Integer;
begin
Result:=True;
If bCount=0 then Exit;
If IsServer then With Server do
For n:=0 to ActiveConnections-1 do begin
If ((not AllExceptThis) and (Client = Connections[n])) or
((AllExceptThis) and (Client <> Connections[n])) then
If Connections[n].SendBuf(data^,bCount)<=0 // вот и никаких ошибок нет !
then Result:=False;
end;




 
Sapersky_   (2002-07-04 01:21) [4]

Переделал на указатели... результат тот же (глюк).
Вот новая версия обработчика приёма... TClientServer - это мой класс, содержащий объекты TServerWinSocket и TClientWinSocket.
Обработчик OnRead сервера:

procedure TClientServer.OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
ReceiveData(Socket);
end;

procedure TClientServer.ReceiveData(Socket: TCustomWinSocket);
// используется и клиентом тоже
Var bc : Integer;
pb,pb1 : PByte;
header : TMsgHeaderLong;
begin
bc:=Socket.ReceiveLength;
If bc>0 then begin
GetMem(pb,bc);
Socket.ReceiveBuf(pb^,bc);
If bc>0 then begin
header.MsgHead:=PMsgHeader(pb)^;
header.SenderID:=Integer(Socket);
// ну хорошо, используем указатель на объект
pb1:=pb; Inc(pb1,SizeOf(TMsgHeader));
AddLocalMsg(header,pb1); // добавляем в буфер
end;
FreeMem(pb);
end;
end;

А фиксируется (в смысле, запоминается) Socket (ранее SocketHandle) действительно в OnClientConnect:

procedure TClientServer.OnClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
AddLocalMsgSimple(nmConnect,Integer(Socket));
//добавляем в буфер сообщение о присоединении клиента
end;

Но поскольку работаем теперь с указателями (объектами), можно воспользоваться моей функцией посылки N2 (SendDataToSocket). Она работает, так что в какой-то степени проблема решена... хотя всё равно интересно, почему глючат ActiveConnections и что такое socket not bound (вспоминается HW:Cataclysm: "We will not been bound!!!" :)).


 
Digitman ©   (2002-07-04 08:30) [5]

1.

procedure TClientServer.ReceiveData(Socket: TCustomWinSocket);
// используется и клиентом тоже
Var bc : Integer;
pb,pb1 : PByte;
header : TMsgHeaderLong;
begin
bc:=Socket.ReceiveLength; // это - ожидаемый размер принятых данных
If bc>0 then begin
GetMem(pb,bc);
Socket.ReceiveBuf(pb^,bc); // а сколько ты реально принял-то ??
If bc>0 then begin
header.MsgHead:=PMsgHeader(pb)^;
header.SenderID:=Integer(Socket);
// только вот какой смысл приводить TCustomWinSocket к Integer ?
// сделай тип поля SenderId : TCustomWinSocket;


pb1:=pb;
// а вот здесь - неверно :
// ты смещаешь указатель на размер заголовка,
// а с чего ты решил , что размер реально принятых данных
// больше чем SizeOf(TMsgHeader) ??
Inc(pb1,SizeOf(TMsgHeader));

//????????????????????????????????????????????????
AddLocalMsg(header,pb1); // добавляем в буфер

// что добавляешь ?? Указатель ?? На что и какого размера ?
// а что потом с ним делать-то будешь, с этим указателем ?
// после завершения этого обработчика ты теряешь
// информацию о размере принятых данных !

end;

// а здесь вообще нонсенс !
// блок памяти, выделенный под буфер приема ты освобождаешь !
// вместе с принятыми данными !
FreeMem(pb);

// как же ты собираешься работать с принятым заголовком и следующими за ним данными, если их уже НЕТ в этой точке ???

end;
end;


2. Почему при SendDataToSocket() "Она работает ... в какой-то степени" ? Поясни.


 
Sapersky_   (2002-07-04 13:57) [6]

Насчёт того, что получить можно не всё сообщение, а его кусок или несколько сообщений, склееных вместе - это да, проблема, и я пока не знаю, как её решать. Ну разделить несколько ещё можно (в заголовке идёт информация о размере данных), а вот собирать сообщение по кусочкам - муторное дело, ведь последующие части придут без заголовков, и как определить, что есть что?
Так что пока считаем - сколько отправили, столько и приняли. И вроде работает, проблем с приёмом не наблюдалось (видимо, оттого, что общаюсь с LocalHost). Информацию от клиента сервер получает нормально, я проверял.
Размер заголовка - 4 байта - может ли в принципе придти меньше?
AddLocalMsg копирует данные в буфер (размер в заголовке), так что ничего страшного в освобождении pb нет.
В общем, добрая половина замечаний - оттого, что я выложил не весь исходник. Ну извини, большой он. Разве что на е-мэйл...
SendDataToSocket() работает в общем нормально, есть некоторые глюки при присоединении более чем одного клиента, но это, я полагаю, вина основной программы (надо копаться...).


 
Digitman ©   (2002-07-04 16:41) [7]

>>"сколько отправили, столько и приняли"

на это надеяться нельзя : если ты ожидаешь, что каждый вызов SendBuffer() на передатчике в точности соответствует отдельному событию OnRead() на приемнике, в котором ты ожидаешь получить в точности то, что было отправлено непосредственно перед этим передатчиком - это не соответствует действительности.

>>Размер заголовка - 4 байта - может ли в принципе придти меньше?

Конечно !

И больше, и меньше, и вообще ничего может не прийти в событии OnRead !

Пойми - протокол-то поточный, а не message-ориентированный !
И к тому же в силу особенностей TCP/IP посылаемый передатчиком поток разбивается на пакеты, который "гуляют" по глоб.сети совершенно различными путями и приходят к точке приема далеко не одновременно и - более того - даже не в порядке их следования в исх.потоке. И задача приемника (на низком уровне) - собирать эти пакеты в оригинальный поток и сообщать тебе событием OnRead() о том, что очер. фрагмент потока достоверен и доступен к чтению.

Пусть передачтчик, например, выполнил ( не ожидая никаких подтверждений от приемника) M последовательных вызовов SendBuffer(), послав тем самым приемнику поток , скажем, в 20 байт. Из них первые 4 есть некий заголовок, оставшиеся 16 - собственно данные.

Приемник может получить все эти 20 байт (в той же последовательности, чтои они отправлены были) как за одно событие OnRead() (сразу все 20 байт) , так и за N событий, в каждом из которых могут быть доступны для чтения очередные 1 или 2 или 7 или иное кол-во байт из ожидаемого приемником вх.потока.

Ты же ожидаешь, что в очередном событии OnRead() пришло ТОЛЬКО ОДНО ОТДЕЛЬНОЕ И ЦЕЛИКОМ ДОСТУПНОЕ В ЭТОТ МОМЕНТ очер.сообщение в 20 байт, из которых первые 4 - заголовок. Но это может быть и не так ! Вдруг передатчик уже послал 4 сообщения (4 х 20 = 80 байт) на момент, когда у тебя впервые возникло событие OnRead() ? А ты отрезал (если успешно, то тебе еще повезло) первые 4 + 16 = 20 байт, сохранил их, а остальное попросту выкинул ! В то время как это может быть, например, заголовок 2-го сообщения и часть из 16-ти байт, сопровождающих его ....

Вот-с ...такие "сложности" ...

Поэтому грамотно реализованная логика приема/обработки поточных гнездовых данных должна предусматривать некий механизм, именуемый часто "машиной состояния". Достаточно грамотно машина состояния реализована в демо-проекте ScktSrvr.dpr. Загляни туда и разберись, что Борланд делает всякий раз вызывая метод ReceiveBuf()


 
Sapersky_   (2002-07-06 10:06) [8]

Ну ладно, ладно, я согласен, зачем так горячиться...
Посмотрел в ScktSrvr.dpr... жуть. Я-то представлял себе сетевое программирование по примеру Chat :).
Может, всё-таки перейду на DirectPlay (с первого взгляда он мне резко не понравился - фирменный стиль Microsoft и всё такое). Там вроде фрагментация/дефрагментация автоматически делается... ну, так написано в Help"е по крайней мере. Хотя сам по себе DPlay - тоже мутное дело.
Ещё - вопрос вроде был уже, но без толкового ответа: что писать в Host при соединении через Интернет (например, кто-нибудь хочет соединиться со мной...). IP не предлагать. Хотя бы ссылку на ответ.


 
Digitman ©   (2002-07-08 08:16) [9]

открывай св-ва TCP/IP-протокола, ассоциированного с интерфейсом связи с провайдером.
Имя_компютера[.доменный_суффикс]


 
Sapersky_   (2002-07-09 13:42) [10]

"Ассоциированного с интерфейсом связи..." - не понял...
Имя компьютера - это имя, под которым я вхожу в сеть?
Т.е., например, Host = "sapersky.atnet.ru". Так?


 
Digitman ©   (2002-07-09 14:08) [11]

Да, имя, под которым ты входишь в сеть провайдера и под которым провайдер регистрирует тебя в своей DNS-базе



Страницы: 1 вся ветка

Текущий архив: 2002.09.16;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.015 c
7-50993
Балков Евгений
2002-07-05 11:44
2002.09.16
где !чайнику! прочитать чего-нить про Com - порт!?


1-50787
sia
2002-09-03 01:15
2002.09.16
Не работает в COMe тип Picture


14-50915
Ferrari_the_best
2002-08-21 03:22
2002.09.16
Ответ на Helpa нужна!!!!!!


14-50968
Black Cat
2002-08-15 15:54
2002.09.16
Дожили...


1-50735
Weare
2002-09-02 12:40
2002.09.16
Сервис в Windows2000Server