Вверх ↑
Ответов: 4629
Рейтинг: 749
#1: 2013-01-17 12:39:48 ЛС | профиль | цитата
Как я понимаю причину глюка:
- TCP_Server создает "серверный" сокет и назначает ему процедуру чтения поступающих данных


constructor THITCP_Server.Create;
begin
inherited;
Sock := TSocket.Create;
...
Sock.OnRead := _OnRes;
...
end;
- при каждом входящем подключении создается "клиентский" сокет (их может быть много) и этому клиентскому сокету опять же назначается процедура, которая будет принимать присланные клиентом данные. Реализовано это таким образом, что каждый клиент получает ту же процедуру, которая назначалась в коде выше:
TCP.pas


function MWnd(window:hwnd; message:dword; wparam:WPARAM; lparam:LPARAM):LRESULT; stdcall;
var i:integer; sc,sc1:TSocket; buf:string; sz,szMax:integer;
begin
...
case lparam and $FFFF of
FD_ACCEPT: begin // Получено новое подключение
sc1 := TSocket.Create(sc); // <= Похоже, "sc" - это "серверный" сокет; см. конструктор ниже
...
end;
...
end;

constructor TSocket.Create(par:TSocket);
type TChAddr = record c1,c2,c3,c4:byte; end;
var ad:TSockAddr; sz:integer;
begin
FParent := par;
...
OnRead := par.OnRead; // Создаваемому сокету назначается родительская процедура чтения
...
end;

- поскольку THITCP_Server._work_doClose закрывало только "серверный" сокет, "клиентские" сокеты по прежнему могли вызывать процедуру чтения, так как они уже знали её адрес, а объект, в котором она находится (THITCP_Server) всё ещё существует. Именно поэтому не работала отправка данных сервером ("серверный" сокет, отправляющий данные, закрыт).

С чего можно сделать такие выводы:
1) Если принять твою последнюю поправку к THITCP_Server._work_doClose, то также нужно поправить описание к точкам
- doClose - отключает всех присоединенных клиентов и запрещает входящие соединения (останавливает сервер)
- doCloseAll - отключает всех присоединенных клиентов, но не останавливает сервер

2) Вижу одну реальную задачу, когда может применяться старый подход: если серверу нужно иметь не больше указанного количества клиентов. Тогда:
- делается счетчик подключений; если количество равно максимальному, прекращается прием новых подключений, но уже подключенные клиенты продолжают обслуживаться;
- делается счетчик отключений; если клиент отключается - сервер опять начинает принимать входящие подключения;
Но в этом случае нужно, чтобы по doClose не уничтожался "серверный" сокет, а выставлялся флаг запрета входящих подключений в процедуре приема (то есть, не обрабатывать FD_ACCEPT, но обрабатывать остальные команды). Всё это требует дополнительных тестов, так что твой вариант, наверное, на данный момент более предпочтительный.

карма: 26

0