Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当超时设置为无限时,为什么Socket.Receive在半封闭连接上超时?_C#_Sockets_Winsock - Fatal编程技术网

C# 当超时设置为无限时,为什么Socket.Receive在半封闭连接上超时?

C# 当超时设置为无限时,为什么Socket.Receive在半封闭连接上超时?,c#,sockets,winsock,C#,Sockets,Winsock,下面是一个C#程序,演示了这个问题 服务器开始侦听套接字。客户端连接到服务器,发送消息,使用Shutdown(SocketShutdown.Send)关闭其发送连接的一半,让服务器知道消息的结尾在哪里,并等待服务器的响应。服务器读取消息,进行一些长时间的计算(这里用睡眠调用模拟),向客户端发送消息,然后关闭连接 在Windows上,即使超时设置为无限,客户端的接收呼叫始终在恰好2分钟后失败,“连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应” 如果我在

下面是一个C#程序,演示了这个问题

服务器开始侦听套接字。客户端连接到服务器,发送消息,使用Shutdown(SocketShutdown.Send)关闭其发送连接的一半,让服务器知道消息的结尾在哪里,并等待服务器的响应。服务器读取消息,进行一些长时间的计算(这里用睡眠调用模拟),向客户端发送消息,然后关闭连接

在Windows上,即使超时设置为无限,客户端的接收呼叫始终在恰好2分钟后失败,“连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应”

如果我在Linux中使用Mono运行该程序,即使我将“长时间操作”设置为10分钟,超时也不会发生,但无论我使用Mono还是.NET运行它,它都会在Windows中发生。如果我将超时设置为1秒,它将在1秒后超时。换句话说,它在我设置的超时或2分钟内超时,以较短者为准

在一个类似的示例程序中,服务器向客户机发送消息,没有从客户机到服务器的消息,也没有半关闭,按照预期工作,没有超时

我可以通过修改协议来解决这个问题,以使用其他一些方法来指示消息何时完成(可能在消息前面加上消息长度)。但我想知道这里发生了什么。当超时设置为无限时,为什么Socket.Receive在半关闭连接上超时

据我所知,一个只关闭了一半发送的连接应该能够无限期地继续接收数据。Windows的这一基本部分似乎不太可能有bug。我做错什么了吗

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Start server thread
            Thread serverThread = new Thread(ServerStart);
            serverThread.IsBackground = true;
            serverThread.Start();

            // Give the server some time to start listening
            Thread.Sleep(2000);

            ClientStart();
        }

        static int PortNumber = 8181;

        static void ServerStart()
        {
            TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber));
            listener.Start();
            while (true)
            {
                TcpClient client = listener.AcceptTcpClient();
                Task connectionHandlerTask = new Task(ConnectionEntryPoint, client);
                connectionHandlerTask.Start();
            }
            listener.Stop();
        }

        static void ConnectionEntryPoint(object clientObj)
        {
            using (TcpClient client = (TcpClient)clientObj)
            using (NetworkStream stream = client.GetStream())
            {
                // Read from client until client closes its send half.
                byte[] requestBytes = new byte[65536];
                int bufferPos = 0;
                int lastReadSize = -1;
                while (lastReadSize != 0)
                {
                    lastReadSize = stream.Read(requestBytes, bufferPos, 65536 - bufferPos);
                    bufferPos += lastReadSize; 
                }
                client.Client.Shutdown(SocketShutdown.Receive);
                string message = Encoding.UTF8.GetString(requestBytes, 0, bufferPos);

                // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back
                byte[] responseBytes = Encoding.UTF8.GetBytes(message);
                Console.WriteLine("Waiting 2 minutes 30 seconds.");
                Thread.Sleep(150000);

                try
                {
                    stream.Write(responseBytes, 0, responseBytes.Length);
                }
                catch (SocketException ex)
                {
                    Console.WriteLine("Socket exception in server: {0}", ex.Message);
                }
            }
        }

        static void ClientStart()
        {
            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                // Set receive timeout to infinite.
                socket.ReceiveTimeout = -1;

                // Connect to server
                socket.Connect(IPAddress.Loopback, PortNumber);

                // Send a message to the server, then close the send half of the client's connection
                // to let the server know it has the entire message.
                string requestMessage = "Hello";
                byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage);
                socket.Send(requestBytes);
                socket.Shutdown(SocketShutdown.Send);

                // Read the server's response. The response is done when the server closes the connection.
                byte[] responseBytes = new byte[65536];
                int bufferPos = 0;
                int lastReadSize = -1;

                Stopwatch timer = Stopwatch.StartNew();
                try
                {
                    while (lastReadSize != 0)
                    {
                        lastReadSize = socket.Receive(responseBytes, bufferPos, 65536 - bufferPos, SocketFlags.None);
                        bufferPos += lastReadSize;
                    }

                    string responseMessage = Encoding.UTF8.GetString(responseBytes, 0, bufferPos);
                    Console.WriteLine(responseMessage);
                }
                catch (SocketException ex)
                {
                    // Timeout always occurs after 2 minutes. Why?
                    timer.Stop();
                    Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message);
                }
            }
        }
    }
}
以下程序使用4字节消息长度作为消息前缀,而不是使用socket.Shutdown(SocketShutdown.Send)作为消息结束的信号。超时在此程序中不发生

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;

namespace WithoutShutdown
{
    class Program
    {
        static void Main(string[] args)
        {
            // Start server thread
            Thread serverThread = new Thread(ServerStart);
            serverThread.IsBackground = true;
            serverThread.Start();

            // Give the server some time to start listening
            Thread.Sleep(2000);

            ClientStart();
        }

        static int PortNumber = 8181;

        static void ServerStart()
        {
            TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber));
            listener.Start();
            while (true)
            {
                TcpClient client = listener.AcceptTcpClient();
                Task connectionHandlerTask = new Task(ConnectionEntryPoint, client);
                connectionHandlerTask.Start();
            }
            listener.Stop();
        }

        static void SendMessage(Socket socket, byte[] message)
        {
            // Send a 4-byte message length followed by the message itself
            int messageLength = message.Length;
            byte[] messageLengthBytes = BitConverter.GetBytes(messageLength);
            socket.Send(messageLengthBytes);
            socket.Send(message);
        }

        static byte[] ReceiveMessage(Socket socket)
        {
            // Read 4-byte message length from the client
            byte[] messageLengthBytes = new byte[4];
            int bufferPos = 0;
            int lastReadSize = -1;
            while (bufferPos < 4)
            {
                lastReadSize = socket.Receive(messageLengthBytes, bufferPos, 4 - bufferPos, SocketFlags.None);
                bufferPos += lastReadSize;
            }
            int messageLength = BitConverter.ToInt32(messageLengthBytes, 0);

            // Read the message
            byte[] messageBytes = new byte[messageLength];
            bufferPos = 0;
            lastReadSize = -1;
            while (bufferPos < messageLength)
            {
                lastReadSize = socket.Receive(messageBytes, bufferPos, messageLength - bufferPos, SocketFlags.None);
                bufferPos += lastReadSize;
            }

            return messageBytes;
        }

        static void ConnectionEntryPoint(object clientObj)
        {
            using (TcpClient client = (TcpClient)clientObj)
            {
                byte[] requestBytes = ReceiveMessage(client.Client);
                string message = Encoding.UTF8.GetString(requestBytes);

                // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back
                byte[] responseBytes = Encoding.UTF8.GetBytes(message);
                Console.WriteLine("Waiting 2 minutes 30 seconds.");
                Thread.Sleep(150000);

                try
                {
                    SendMessage(client.Client, responseBytes);
                }
                catch (SocketException ex)
                {
                    Console.WriteLine("Socket exception in server: {0}", ex.Message);
                }
            }
        }

        static void ClientStart()
        {
            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                // Set receive timeout to infinite.
                socket.ReceiveTimeout = -1;

                // Connect to server
                socket.Connect(IPAddress.Loopback, PortNumber);

                // Send a message to the server
                string requestMessage = "Hello";
                byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage);
                SendMessage(socket, requestBytes);

                // Read the server's response.
                Stopwatch timer = Stopwatch.StartNew();
                try
                {
                    byte[] responseBytes = ReceiveMessage(socket);
                    string responseMessage = Encoding.UTF8.GetString(responseBytes);
                    Console.WriteLine(responseMessage);
                }
                catch (SocketException ex)
                {
                    // Timeout does not occur in this program because it does not call socket.Shutdown(SocketShutdown.Send)
                    timer.Stop();
                    Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message);
                }
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Net.Sockets;
Net系统;
使用System.Threading.Tasks;
使用系统诊断;
使用系统线程;
不带关闭的命名空间
{
班级计划
{
静态void Main(字符串[]参数)
{
//启动服务器线程
线程serverThread=新线程(ServerStart);
serverThread.IsBackground=true;
serverThread.Start();
//给服务器一些时间开始侦听
《睡眠》(2000年);
ClientStart();
}
静态int端口号=8181;
静态void ServerStart()
{
TcpListener listener=newtcplistener(newipendpoint(IPAddress.Any,PortNumber));
listener.Start();
while(true)
{
TcpClient client=listener.AcceptTcpClient();
任务connectionHandlerTask=新任务(ConnectionEntryPoint,客户端);
connectionHandlerTask.Start();
}
listener.Stop();
}
静态void SendMessage(套接字,字节[]消息)
{
//发送一个4字节的消息长度,后跟消息本身
int messageLength=message.Length;
byte[]messageLengthBytes=位转换器.GetBytes(messageLength);
socket.Send(messageLengthBytes);
socket.Send(消息);
}
静态字节[]接收消息(套接字)
{
//从客户端读取4字节的消息长度
字节[]messageLengthBytes=新字节[4];
int bufferPos=0;
int lastReadSize=-1;
while(bufferPos<4)
{
lastReadSize=socket.Receive(messageLengthBytes,bufferPos,4-bufferPos,SocketFlags.None);
bufferPos+=lastReadSize;
}
int messageLength=BitConverter.ToInt32(messageLengthBytes,0);
//读留言
byte[]messageBytes=新字节[messageLength];
bufferPos=0;
lastReadSize=-1;
while(bufferPosstring boundary = string.Format("--{0}--", Guid.NewGuid());
byte[] boundaryBytes = Encoding.ASCII.GetBytes(boundary);

//Every 15 seconds write a byte to the stream.
for (int i = 0; i < 10; i++)
{
    stream.WriteByte(0);
    Thread.Sleep(15000);
}

//Indicate where the end of the heartbeat bytes is.
stream.Write(boundaryBytes, 0, boundaryBytes.Length);

//Same code as before.
try
{
    stream.Write(responseBytes, 0, responseBytes.Length);
}
catch (SocketException ex)
{
    Console.WriteLine("Socket exception in server: {0}", ex.Message);
}