Я хочу иметь возможность закрыть сокет, который в данный момент прослушивает порт, а затем вернуться к нему и восстановить прослушивание порта. Я не могу этого сделать, потому что второй вызов acceptOnPort на другом сокете прослушивания сервера всегда заканчивается ошибкой (адрес уже используется). Как я могу закрыть сокет для прослушивания и восстановить новый?
GCDAsyncSocket не может принять OnPort более одного раза даже после его отключения
Ответы (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 до того, как произойдет дело.