wierzę sekwencji wyłączania się następująco (w sposób opisany here)zamykania gniazda: kiedy należy skorzystać SocketShutdown.Both
MSDN documentation (rozdział Uwagi) brzmi:
Podczas korzystania z połączenia zorientowanego na połączenie
Socket
, zawsze wywołaj metodęShutdown
przed zamknięciemSocket
. Zapewnia to, że wszystkie dane są wysyłane i odbierane na podłączonym gnieździe przed jego zamknięciem.
To zdaje się sugerować, że jeśli używam Shutdown(SocketShutdown.Both)
, wszelkie dane, które nie zostały jeszcze odebrane, może nadal być spożywane. Aby przetestować to:
- Ciągle przesyłam dane do klienta (poprzez
Send
w osobnym wątku). - Klient wykonał
Shutdown(SocketShutdown.Both)
. - Oddzwanianie na serwerze
BeginReceive
powoduje jednak, że zgłasza wyjątek: Istniejące połączenie zostało przymusowo zamknięte przez zdalny host. Oznacza to, że nie mogę odebrać wartości zwracanej0
, a następnie wywołaćShutdown
.
Zgodnie z życzeniem opublikowałem poniższy kod po stronie serwera (jest zawinięty w formularz Windows i został utworzony jako eksperyment). W moim scenariuszu testowym nie widziałem stanu CLOSE_WAIT
stanu w TCPView jak zwykle, bez wysyłania ciągłych danych. Potencjalnie zrobiłem coś złego i niepoprawnie przerywa mi konsekwencje. W innym eksperymencie:
- Klient łączy się z serwerem.
- Klient wykonuje
Shutdown(SocketShutdown.Both)
. - Serwer otrzymuje potwierdzenie zamknięcia i wysyła niektóre dane w odpowiedzi. Serwer wykonuje również
Shutdown
. - Klient otrzymuje dane z serwera, ale obok
BeginReceive
nie jest dozwolone: prośbą, aby wysyłać i odbierać dane było zabronione, ponieważ gniazdo już zostały zamknięte w tym kierunku z poprzedniego wywołania shutdown
W w tym scenariuszu nadal oczekiwałem wartości zwracanej z 0
z EndReceive
do Close
gniazda. Czy to oznacza, że powinienem zamiast tego użyć Shutdown(SocketShutdown.Send)
? Jeśli tak, to kiedy należy użyć Shutdown(SocketShutdown.Both)
?
Kod z pierwszego eksperymentu:
private TcpListener SocketListener { get; set; }
private Socket ConnectedClient { get; set; }
private bool serverShutdownRequested;
private object shutdownLock = new object();
private struct SocketState
{
public Socket socket;
public byte[] bytes;
}
private void ProcessIncoming(IAsyncResult ar)
{
var state = (SocketState)ar.AsyncState;
// Exception thrown here when client executes Shutdown:
var dataRead = state.socket.EndReceive(ar);
if (dataRead > 0)
{
state.socket.BeginReceive(state.bytes, 0, state.bytes.Length, SocketFlags.None, ProcessIncoming, state);
}
else
{
lock (shutdownLock)
{
serverShutdownRequested = true;
state.socket.Shutdown(SocketShutdown.Both);
state.socket.Close();
state.socket.Dispose();
}
}
}
private void Spam()
{
int i = 0;
while (true)
{
lock (shutdownLock)
{
if (!serverShutdownRequested)
{
try { ConnectedClient.Send(Encoding.Default.GetBytes(i.ToString())); }
catch { break; }
++i;
}
else { break; }
}
}
}
private void Listen()
{
while (true)
{
ConnectedClient = SocketListener.AcceptSocket();
var data = new SocketState();
data.bytes = new byte[1024];
data.socket = ConnectedClient;
ConnectedClient.BeginReceive(data.bytes, 0, data.bytes.Length, SocketFlags.None, ProcessIncoming, data);
serverShutdownRequested = false;
new Thread(Spam).Start();
}
}
public ServerForm()
{
InitializeComponent();
var hostEntry = Dns.GetHostEntry("localhost");
var endPoint = new IPEndPoint(hostEntry.AddressList[0], 11000);
SocketListener = new TcpListener(endPoint);
SocketListener.Start();
new Thread(Listen).Start();
}
'Wyślij' zwraca także' 0' podczas sekwencji wyłączania. Jeśli zaktualizuję * serwer *, aby to uwzględnić, zanim moja flaga zostanie ustawiona w metodzie 'Spam',' EndReceive' nadal zgłasza wyjątek. Podejrzewam, że to wszystko dzieje się, ponieważ stan gniazda 'CLOSE_WAIT' został w jakiś sposób pominięty. – Pooven