C#中的Socket.receive连续接收0字节且不在循环中阻塞

9
我正在尝试用C#编写一个最简单的多线程TCP服务器,该服务器能够从多个客户端接收数据。
每当新客户端连接时,建立套接字连接并将套接字作为参数传递给新类函数,然后while循环运行并接收数据,直到客户端断开连接。
问题在于,“socket.receive”不是阻塞的,并且接收到0字节的数据。因此,循环持续运行,而不会在socket.receive(代码中的“clientSocket.Receive(bb)”)处进行阻塞。
我正在使用Chrome浏览器作为测试客户端。 即使我使用其他任何客户端,TCP服务器的行为也保持不变。
客户端仅发送一次数据,但是服务器持续接收到0字节,而while循环继续运行。
我将服务器输出复制如下,希望您能帮助阻止服务器在socket.receive处等待下一个客户端传输。 奇怪的是,即使接收到0字节,也不会调用异常。
请帮忙解决。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
Threading;
using System.Timers;
using System.Security;
using System.Security.Permissions;
namespace SERVER
{
    static class Constants
    {
        public const int port = 8080;
        public const int buffer_size = 512;


    }
    class Program
    {
        static public string LocalIPAddress()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                    break;
                }
            }
            return localIP;
            //return ip;
        }

        static void Main(string[] args)
        {
            //IPEndPoint ipObj = new IPEndPoint(IPAddress.Parse("Server IP goes here"), 20487); //20487 is port. you can change it according to your wish
            System.Net.IPAddress IP = IPAddress.Any;
            int port = Constants.port;
            TcpListener listnerObj = new TcpListener(IP, port);
            listnerObj.Start();
            string client_addr;
            string[] client_addr_split;
            string IP_string = LocalIPAddress();

            Console.WriteLine("Server Started on {0}:{1}", IP_string, port);
            while (true)
            {
                Console.WriteLine("================================");
                Console.WriteLine("**    Waiting For Client     **");
                Socket clientSocket = listnerObj.AcceptSocket(); // waiting for the client to connect

                client_addr = clientSocket.RemoteEndPoint.ToString();
                client_addr_split = client_addr.Split(':');
                client_addr = client_addr_split[0];

                Console.WriteLine("Client Connected {0}", client_addr);
                ParameterizedThreadStart thread = delegate { new communication().doCommunicate(clientSocket, client_addr); };
                Thread th = new Thread(thread);
                th.Start(); // start the thread here 
            }
        }

        class communication
        {
            public int byteReceived;
            public byte[] bb;
            public void doCommunicate(Socket clientSocket, string client_addr)
            {
                clientSocket.Blocking = true;
                bb = new byte[Constants.buffer_size];

                //Console.WriteLine("***** Entered DoCommunicate *****");
                while (clientSocket.Connected)
                {
                    //Console.WriteLine("Entered While");
                    try
                    {
                        //Console.WriteLine("Entered TRY");
                        Console.WriteLine("Waiting to recieve Data from IP : client_addr");
                        //int ReceivedDataLength = Client.Receive(ReceivedBytes, 0, ReceivedBytes.Length, SocketFlags.None);
                        byteReceived = clientSocket.Receive(bb, 0, bb.Length, SocketFlags.None);
                        //byteReceived = clientSocket.Receive(bb);
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine("Error: Socket Exception.\n{0}\n{1}.", e.Message, e.ErrorCode);
                        break;
                    }
                    catch (ArgumentNullException e)
                    {
                        Console.WriteLine("Error : Argument Null Exception.\n{0}", e.Message);
                        break;
                    }
                    catch (ObjectDisposedException e)
                    {
                        Console.WriteLine("Error : Socket Disposed Exception Caught.\n{0}", e.Message);
                        break;
                    }
                    catch (SecurityException e)
                    {
                        Console.WriteLine("Error: Security Exception.\n{0}", e.Message);
                        break;
                    }
                    //clientSocketglobal.Send(Encoding.Default.GetBytes("Hello Client"), SocketFlags.None);
                    Console.WriteLineWriteLine("Received Byte count : {0}, from IP : {1}", byteReceived, client_addr); // Do whatever you want to do with the data recieved. Parsing and storing etc.
                    Console.WriteLine(Encoding.UTF8.GetString(bb));
                }
                //Console.WriteLine("While Loop Exited");
                Console.WriteLine("Socked and Class Object  Disposed");
                clientSocket.Close();
                clientSocket.Dispose();
                GC.Collect();
            }
        }
    }
}

服务器输出结果:
Server Started on 10.0.0.2:8080
================================
**    Waiting For Client     **
Client Connected 10.0.0.2
================================
**    Waiting For Client     **
Client Connected 10.0.0.2
================================
**    Waiting For Client     **
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 386, from IP : 10.0.0.2
GET /HelloWorld HTTP/1.1
Host: 10.0.0.2:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6


Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Waiting to recieve Data from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
Received Byte count : 0, from IP : 10.0.0.2
1个回答

13
如果你收到了0字节,通常意味着发送方已经关闭了他们的发送套接字。在一个Socket中,发送和接收渠道是分开的;我认为正在发生的是你的发送(他们的接收)仍然是打开并可用的,因此clientSocket.Connected返回true(你仍然可以向他们发送回复),但是:他们在发送有效载荷后立即关闭了他们的发送(你的接收)(这很常见,表示批处理结束)。基本上,你只需要检测到0字节接收,并将其视为结束:一旦你从接收到的非正数回复,就不会再有更多的数据ever传入。因此,只需编写任何需要编写的响应(他们仍然可以听着,尽管他们再也不会说话),然后关闭套接字。
顺便提一句:在HTTP/1.1中,使用Connection: keep-alive,可以保持他们的套接字准备好发送下一个请求,但在这种情况下,它看起来他们只是没有这样做。他们在发出请求后立即关闭了套接字。这没关系,只需提供响应并完全关闭套接字。不会再有更多的请求进来。

感谢Marc,但如果那是情况的话,为什么没有引发异常? - Ashish
2
@Ashish 为什么会引发异常?您正在请求数据,它说“永远没有更多数据”。然后您请求它的数据:它说“永远没有更多数据”。然后您请求它的数据:它说“永远没有更多数据”。然后您请求它的数据:它说“永远没有更多数据”。这里没有任何值得例外的地方...只是要注意回复。计算机不会厌倦地说“不”。 - Marc Gravell
谢谢Marc,我明白了。有更好的接收数据的方法吗? - Ashish
@Ashish 这取决于情况。对于低容量,那样是可以的。对于更高的容量,你可能想要查看异步API(不要与begin/end API混淆,它是不同的)。 - Marc Gravell
在我的情况下,是因为我的读取缓冲区已满且其偏移量位于末尾,因为我的读取缓冲区比以分隔符结尾的消息的最大大小要小。 - Pierre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接