GCDAsyncSocket не может принять OnPort более одного раза даже после его отключения

Я хочу иметь возможность закрыть сокет, который в данный момент прослушивает порт, а затем вернуться к нему и восстановить прослушивание порта. Я не могу этого сделать, потому что второй вызов acceptOnPort на другом сокете прослушивания сервера всегда заканчивается ошибкой (адрес уже используется). Как я могу закрыть сокет для прослушивания и восстановить новый?


person Boon    schedule 18.03.2013    source источник


Ответы (1)


См. выпуск 146, который я только что добавил.

GCDAsyncSocket никогда не будет освобождаться, потому что dispatch_source_set_event_handler содержит ссылку на блок, который содержит ссылку на сам GCDAsyncSocket.

Это приводит к невозможности закрыть, а затем снова открыть прослушиватель GCDAsyncSocket, поскольку адрес уже используется.

Это можно решить, изменив ссылку на слабую ссылку. Перед dispatch_source_set_event_handler добавьте строку:

__weak GCDAsyncSocket* weakSelf = self;

а затем использовать weakSelf вместо self для вызова doAccept.

while ([weakSelf doAccept:socketFD] && (++i ‹ numPendingConnections));

Вам нужно повторить это дважды, один раз для ipv4 и один раз для ipv6.

На этом этапе вы обнаружите, что хорошо, что GCDAsyncSocket никогда не освобождается, потому что он аварийно завершает работу сразу же после запуска Dealloc. Это связано с тем, что Dealloc вызывает метод closeWithError, который, в свою очередь, вызывает делегат socketDidDisconnect, передавая в качестве параметра сам GCDAsyncSocket. ARC немедленно сохраняет GCDAsyncSocket, который дает сбой, поскольку GCDAsyncSocket в настоящее время освобождается.

Это можно решить, переместив «delegate = nil» в начало Dealloc, что вы можете сделать, так как в этот момент невозможно безопасно вызвать делегата (ну, это невозможно, если вы хотите иметь возможность передать GCDAsyncSocket, что вы больше не можете делать). В качестве альтернативы в этом случае можно было бы вызвать socketDidDisconnect:nil.

В любом случае это означает, что socketDidDisconnect не будет вызываться или, альтернативно, не будет вызываться с соответствующим GCDAsyncSocket в качестве параметра, что может нарушить контракт API, но на данном этапе неизбежно.

Лучшим API было бы иметь какой-то вызов метода «Kill», чтобы убить GCDAsyncSocket до того, как произойдет дело.

person Peter N Lewis    schedule 09.04.2013
comment
Я внес предложенное вами изменение __weak, но все еще получаю сообщение об ошибке при попытке acceptOnPort во второй раз. Я что-то упустил? - person Boon; 28.02.2014
comment
Питер, ваше предложение работает, как только я ввел небольшую задержку для последующего acceptOnPort. Спасибо! - person Boon; 28.02.2014