首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

TCP Socket一端关闭之后不报错误,而是接受0字节

2013-02-25 
TCP Socket一端关闭之后不报异常,而是接受0字节。我用.net 3.5, VS 2008 开发, TCP Socket通讯,递归调用接

TCP Socket一端关闭之后不报异常,而是接受0字节。
我用.net 3.5, VS 2008 开发, TCP Socket通讯,递归调用接受信息。在实际项目中发现当 Socket一端关闭(调用Socket.Close())后,另外一端不是报出异常(我期望是如此),而是不断递归调用接受0字节。我整理了下代码,下面Demo能重现此问题。期望有人能给出解释。
服务器端代码


    class TestServer
    {
        static void Main()
        {
            try
            {
                TCPServer tcpServer = (TCPServer)TCPServer.GetInstance();
                tcpServer.ServerIP = "127.0.0.1";
                tcpServer.ServerPort = 3721;
                tcpServer.CreatListener();
                Console.WriteLine(String.Format("Server is ready with {0}:{1}", tcpServer.ServerIP, tcpServer.ServerPort));
            }           
            catch (Exception e)
            {
                Console.WriteLine("Exception: {0}", e);
            }
            Console.WriteLine("Press Enter to Exit"); Console.ReadLine();
        }    
    } 

    public abstract class PacketTransferBase
    {
        public PacketTransferBase()
        {            
        }
        private int count = 0;
        /// <summary>
        /// 接收信息
        /// </summary>
        /// <param name="ar"></param>
        protected void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                StateObject so = (StateObject)ar.AsyncState;
                Socket socket = so.workSocket;                

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);


                byte[] actualData = new byte[length];
                Array.Copy(so.buffer, 0, actualData, 0, length);
               
                Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2]));

                bool continueReceive = true;
                Console.WriteLine(Encoding.UTF8.GetString(actualData));
                if (count == 0)
                {
                    socket.Send(Encoding.UTF8.GetBytes("this is a back message from server!"));
                    count++;
                }
                socket.Close();

                if (continueReceive && socket != null && socket.Connected)
                {
                    socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
                }                
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
        }
    }    

    /// <summary>
    /// TCP服务器端
    /// </summary>
    public class TCPServer : PacketTransferBase
    {
        public string ServerIP { get; set; }
        public int ServerPort { get; set; }     
        private static TCPServer _TCPServer = new TCPServer();
        Socket socketServer = null;


       
        private TCPServer()
        {            
        }

        public static PacketTransferBase GetInstance()
        {
            return _TCPServer;
        }

        /// <summary>
        /// 服务器端建立侦听
        /// </summary>
        public void CreatListener()
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将该socket绑定到主机上面的某个端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx           
            socketServer.Bind(new IPEndPoint(IPAddress.Parse(ServerIP), ServerPort));

            //启动监听,并且设置一个最大的队列长度
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
            socketServer.Listen(100);

            //开始接受客户端连接请求
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
            socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
        }

        /// <summary>
        /// 接收客户端
        /// </summary>
        /// <param name="ar"></param>
        public void ClientAccepted(IAsyncResult ar)
        {
            try
            {
                var socketServer = ar.AsyncState as Socket;

                //这就是客户端的Socket实例,我们后续可以将其保存起来
                var socketClient = socketServer.EndAccept(ar);                

                //接收客户端的消息(这个和在客户端实现的方式是一样的)                


                StateObject so = new StateObject();
                so.workSocket = socketClient;
                socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);

                //准备接受下一个客户端请求
                socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
        }
    }

    public class StateObject
    {
        public Socket workSocket = null;
        public EndPoint tempRemoteEP = null;
        public const int BUFFER_SIZE = 1024;
        public byte[] buffer = new byte[BUFFER_SIZE];
    }



客户端代码

    class TestClient
    {
        static void Main()
        {
            try
            {
                TCPClient tcpClient = (TCPClient)TCPClient.GetInstance();
                tcpClient.ServerIP = "127.0.0.1";
                tcpClient.ServerPort = 3721;               

                Socket socket = tcpClient.ConnectToServer();
                socket.Send(Encoding.UTF8.GetBytes("this is a test message from client!"));                
            }            
            catch (Exception e)
            {
                Console.WriteLine("Exception: {0}", e);
            }
            Console.WriteLine("Press Enter to Exit"); Console.ReadLine();


        }         
    }

    public abstract class PacketTransferBase
    {
        public PacketTransferBase()
        {
        }

        private int count = 0;
        /// <summary>
        /// 接收信息
        /// </summary>
        /// <param name="ar"></param>
        protected void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                StateObject so = (StateObject)ar.AsyncState;
                Socket socket = so.workSocket;

                //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
                var length = socket.EndReceive(ar);
                byte[] actualData = new byte[length];
                Array.Copy(so.buffer, 0, actualData, 0, length);

                Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2]));

                bool continueReceive = true;
                Console.WriteLine(Encoding.UTF8.GetString(actualData));
                //把以下代码去掉注释,把服务器端socket.Close()注释,则服务器端产生一样效果。
                //if (count == 0)
                //{
                //    socket.Send(Encoding.UTF8.GetBytes("this is a second message from server!"));
                //    count++;
                //}
                //socket.Close();

                if (continueReceive && socket != null && socket.Connected)


                {
                    socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
        }
    }    

    public class StateObject
    {
        public Socket workSocket = null;
        public EndPoint tempRemoteEP = null;
        public const int BUFFER_SIZE = 1024;
        public byte[] buffer = new byte[BUFFER_SIZE];
    }


    /// <summary>
    /// TCP 客户端
    /// </summary>
    public class TCPClient : PacketTransferBase
    {
        public string ServerIP { get; set; }
        public int ServerPort { get; set; }       

        private static TCPClient _TCPClient = new TCPClient();       

        private TCPClient()
        {            
        }

        public static PacketTransferBase GetInstance()
        {
            return _TCPClient;
        }        

        public Socket ConnectToServer()
        {
            //创建一个Socket
            Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接到指定服务器的指定端口
            //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx           
            socketClient.Connect(ServerIP, ServerPort);

            StateObject so = new StateObject();


            so.workSocket = socketClient;
            socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
            return socketClient;            
        }
    }



服务端端和客户端关闭Socket部分互换后(注释服务器端,开启客户端关闭),服务器端也能长生此同样现象。
[解决办法]
结束时发个消息给另一端手动判断吧
[解决办法]
引用:
引用:这可能算是一个BUG吧。理论上是应该阻塞的。
还有更多的问题。
codeproject有一个开源东东很多也碰到过原子锁会出现负的情况。

使用心跳包吧。
我用的短连接,用完就关,耗费资源少。

这个和短连接长连接又没有关系。。。。
[解决办法]
接收到0字节说明连接已经断开,不再阻塞
所以在处理接收数据时一定要处理这种情况
当收到0字节时,处理连接的关闭事宜

热点排行