c# - TCP 재설정 후 임의 "기존 연결이 원격 호스트에 의해 강제로 닫혔습니다."




.net sockets (3)

클라이언트와 서버의 두 부분으로 구성되어 있습니다. 그리고 클라이언트에서 서버로 데이터 (크기> 5840 바이트)를 보내려고하면 서버가 데이터를 다시 보냅니다. 매번 두 번씩 기다리는 횟수를 반복합니다. 언젠가는 서버 응용 프로그램 충돌, 충돌이 매우 임의의 오류 보인다 :

처리되지 않은 예외 : System.IO.IOException : 전송 연결에서 데이터를 읽을 수 없습니다. 기존 연결이 원격 호스트에 의해 강제로 닫혔습니다. --->

System.Net.Sockets.SocketException : 기존 연결이 원격 호스트에 의해 강제로 닫혔습니다.

System.Net.Sockets.Socket.Receive (Byte [] 버퍼, Int32 오프셋, Int32 크기, SocketFlags socketFlags)

System.Net.Sockets.NetworkStream.Read (Byte [] 버퍼, Int32 오프셋, Int32 size)

--- 내부 예외 스택 추적 끝 ---

System.Net.Sockets.NetworkStream.Read (Byte [] 버퍼, Int32 오프셋, Int32 size)

at TCP_Server.Program.Main (String [] args)

클라이언트 코드 (루프 내부에 있음) :

            try
            {
                Int32 port = 13777;
                using (TcpClient client = new TcpClient(ip, port))
                using (NetworkStream stream = client.GetStream())
                {
                    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    var data = GenerateData(size);

                    sw.Start();
                    // Send the message to the connected TcpServer. 
                    stream.Write(data, 0, data.Length);

                    // Buffer to store the response bytes.
                    data = new Byte[size];

                    // Read the first batch of the TcpServer response bytes.
                    Int32 bytes = stream.Read(data, 0, data.Length);

                    sw.Stop();
                    Console.WriteLine(i + ": Done transporting " + size + " bytes to and from " + ip + " time: " +
                                      sw.ElapsedMilliseconds + " ms");
                    // Close everything.
                    stream.Close();
                    client.Close();
                }

            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }

            sw.Reset();

서버 코드 :

            Byte[] bytes = new Byte[size];

            // Enter the listening loop. 
            for (int i = 0; i < numberOfPackages; i++)
            {
                using (TcpClient client = server.AcceptTcpClient())
                using (NetworkStream stream = client.GetStream())
                {
                    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    // Loop to receive all the data sent by the client. 
                    while ((stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        // Send back a response.
                        stream.Write(bytes, 0, size);

                    }
                    client.GetStream().Close();
                    client.Close();
                }


                Console.WriteLine("Receive data size " + size);

            }

나는 wireshark를 사용하여 전송 된 tcp 패키지를 모니터하고 프로그램이 충돌하기 전에 TCP RST가 클라이언트에서 서버로 전송된다는 것을 발견했다. 그래서 문제는 RST가 올바르게 처리되지 않는다고 가정합니다. 클라이언트와 호스트 사이에 방화벽이 없으므로 문제가되지 않습니다.

클라이언트와 서버 모두를위한 wireshark 파일은 다음과 같습니다. https://www.dropbox.com/sh/ctl2chq3y2c20n7/AACgIJ8IRiclqnyOyw8sqd9La?dl=0

따라서 TCP RST를 없애거나 서버를 어떤 방식 으로든 처리 할 수 ​​있어야하며 충돌이 발생하지 않아야합니다.

나는 더 긴 대기 시간을 사용하려고 노력했지만 도움이되지 않습니다. 데이터가 5840 바이트 미만인 경우 충돌이 발생하지 않습니다.

어떤 제안이나 아이디어?

편집 : 대답 덕분에 나는 다음과 같은 변화와 함께 작동하게 :

섬기는 사람:

// Loop to receive all the data sent by the client.
int k = 0;
while (k < size)
{
   int bytesRead = stream.Read(bytes, 0, bytes.Length);
   k += bytesRead;
}
// Send back a response.
stream.Write(bytes, 0, size);

그리고 클라이언트 쪽에서 수신 할 때도 마찬가지입니다. 먼저 모든 데이터를 보내고 서버가 내 애플리케이션에 대해이 작업에 응답하도록하고 싶습니다.


HttpClient 임의 오류를 사용할 때이 문제가 발생했습니다.

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
 ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

또는

 ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.

TCP 스택의 Explicit Congestion Notification 또는 ECN에있는 문제. 이것은 ECN 기능이라고도합니다. 명시 적 혼잡 통보는 인터넷 프로토콜과 전송 제어 프로토콜의 확장이며 RFC 3168에 정의되어 있습니다. ECN은 패킷을 삭제하지 않고 네트워크 정체에 대한 종단 간 통보를 허용합니다. ECN Capability가 활성화 된 경우 비활성화 할 수 있습니다.

netsh interface tcp set global ecncapability=disabled

다시 부팅 할 필요가 없습니다.


iis 루트 항목의 캐시 제한을 늘리는 것과 동일한 문제를 해결했습니다. 캐싱을 사용하지 않을 수도 있습니다. 희망이 도움이됩니다.


내가 보는 두 가지 문제 :

  1. size 바이트에 대한 읽기가 실제로 size 바이트를 읽는다고 가정합니다. 그렇지 않습니다. TCP는 스트리밍 프로토콜입니다. read는 적어도 1 바이트를 읽습니다. 그게 유일한 보증입니다.
  2. 코드가 임의로 교착 상태가됩니다. 클라이언트는 서버에 데이터를 씁니다. 서버가 에코 백합니다. 그러나 클라이언트는 모든 것을 기록 할 때까지 읽지 않습니다. 네트워크 버퍼보다 ​​많은 양을 기록하면 교착 상태가됩니다. 클라이언트는 동시에 읽고 쓸 수 있어야합니다. 아마도 데이터를 다시 읽는 다른 스레드 / 작업이 필요할 것입니다. 이 문제를 해결하는 좋은 패턴은 하나의 작가 작업, 하나의 판독기 작업을 시작하고 Task.WhenAll / WaitAll을 다시 시작하는 것입니다.

정확한 상황에서 TCP 스택이 RST를 보낼지 확실하지 않습니다. 교착 상태로 인해 시간 초과가 발생할 수 있습니다.

당신은 예외를 삼키는 것이 아닙니다, 그렇죠?

일반적으로 연결을 닫으면 백그라운드에서 정상적으로 종료됩니다. 그러나 나는이 시점에서 상대방이 아직 글을 쓰고있을 때 어떤 일이 일어나는지 확신 할 수 없다. 아마도 답은 RST가 수신자에 의해 생성된다는 것입니다. 분명히, TCP spec은이 질문에 답할 것입니다. 이 대답 이 신뢰할만한 것이라면 실제로 Shutdown (Read) / Close에 이어 수신 쓰기가 연결을 RST합니다.

두 가지 문제를 모두 수정하고 결과를보고하십시오.