socket多客户端连接,接收数据或反序列化时偶尔发生异常导致断线,异常是偶尔发生,不是每次都出现。求各位帮忙解答,由于系统只给我送100分,解决实际问题的还可再加分。服务器代码如下:
  public void NetWorkServer()
        {
            IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName())[0];
            //定义socket
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //定义端点
            IPEndPoint ep = new IPEndPoint(ip, 2008);
            //绑定端点
            socket.Bind(ep);
            //最大监听数
            socket.Listen(connectNum);
            //开启线程监听
            listenThread = new Thread(new ThreadStart(listenSocket));
            listenThread.IsBackground = true;
            listenThread.Start();
            
        }
        /// <summary>
        /// 监听
        /// </summary>
        private void listenSocket()
        {
            while (true)//不停的监听客户端连接请求
            {
                try
                {
                    chatSocket = socket.Accept();
                    //开辟线程处理
                    Thread newChatThread = new Thread(new ThreadStart(beginChat));
                    newChatThread.IsBackground = true;
                    newChatThread.Start();
                }
                catch (Exception ex)
                {
                    log.Error("监听失败");                    
                }                
            }
        }
   /// <summary>
        /// 接收数据
        /// 处理
        /// </summary>
        private void beginChat()
        {
            Socket s = chatSocket;
            string ep = s.RemoteEndPoint.ToString();//远程ip+端口
            string[] str = ep.Split(':');
            lock (socketsMap)
            {
                if (!socketsMap.ContainsKey(str[0]))
                {
                    socketsMap.Add(str[0], s);  //保存用户socket
                }
                else
                {
                    socketsMap[str[0]] = s;
                }
            }            
            BinaryFormatter format = new BinaryFormatter();
            MemoryStream mem = new MemoryStream();
            while (true)
            {
                #region   方法一,直接从网络流中读取数据
                /*
                NetworkStream stream = null;
                try
                {
                    stream = new NetworkStream(s);
                }
                catch (IOException)
                {
                    log.Error("客户端关闭");
                    return;
                }
                try
                {                     
                    NetWordMsg msg = (NetWordMsg)format.Deserialize(stream);//这里偶尔出现“在分析完成之前就遇到流结尾”异常
                    //处理信息,引发事件给DoMsg(String msg)
                    ReveiveEvent(s, msg);
                }
                catch (Exception ex)
                {
                    lock (socketsMap)
                    {
                        socketsMap.Remove(str[0]);
                        try
                        {
                            s.Disconnect(false);
                        }
                        catch (Exception)
                        {
                        }
                    }
                    s.Close();
                    s = null;                    
                    log.Error("格式化信息失败",ex);
                    return;
                }
               */

                #endregion   
                                                            
                #region 方法二:发送方法是带长度的方法
                byte[] data = new byte[4];
                try
                {
                    if (s != null && s.Connected)
                    {
                        int revc=0;
                        try
                        {
                            revc = s.Receive(data, 4, SocketFlags.None);//读出数据长度
                        }
                        catch (SocketException ex)
                        {
                            log.Error("连接关闭", ex);
                            s.Disconnect(false);
                            s = null;
                        }
                        int size = BitConverter.ToInt32(data, 0);//得到数据长度
                        int offset = 0;
                        if (size > 0)
                        {
                            data = new byte[size];//接收数据数组
                            revc = s.Receive(data, size, SocketFlags.None);//此方法偶尔出现“连接已关闭”的异常
                            mem.Write(data, offset, revc);
                            offset += revc;
                            size -= revc;
                            //mem.Position = 0;
                            mem.Seek(0, SeekOrigin.Begin);
                            NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
                            //处理信息,引发事件给DoMsg(String msg)
                            ReveiveEvent(s, msg);
                            mem = new MemoryStream();
                        }
                    }
                }
                catch (SocketException ex)
                {
                    lock (socketsMap)
                    {
                        socketsMap.Remove(str[0]);
                        try
                        {
                            s.Disconnect(false);
                        }
                        catch (Exception)
                        {
                        }
                    }
                    s.Close();
                    s = null;
                    log.Error("格式化信息失败", ex);
                    continue;
                }                
                #endregion
            }               
        }
   

解决方案 »

  1.   

     客户端原码:
       /// <summary>
            /// 连接服务器 
            /// </summary>
            private void ConnectToServer()
            {
                
                //定义socket
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //定义端点
                IPEndPoint ep = new IPEndPoint(IPAddress.Parse(sip), 2008);
                //连接
                try
                {
                    socket.Connect(ep);
                }
                catch (Exception)
                {               
                    //Init_DisConnection();
                    isConnected = false;
                }
                if (socket.Connected)
                {
                    isConnected = true;
                    //发送消息,通知服务器
                    NetWordMsg msg = new NetWordMsg();
                    msg.Type = "connect";
                    sendMsg(msg);
                    //开线程负责接收服务器传来的消息
                    Thread recThread = new Thread(new ThreadStart(ReceiveMsg));
                    recThread.IsBackground = true;
                    recThread.Start();
                }
            }
            /// <summary>
            /// 发送信息
            /// </summary>
            /// <param name="msg"></param>
            private void sendMsg(NetWordMsg msg)
            {
                #region  原方法
                /*
                if (socket.Connected)
                {
                    NetworkStream stream = null;
                    try
                    {
                        stream = new NetworkStream(socket);
                        BinaryFormatter format = new BinaryFormatter();
                        
                        format.Serialize(stream, msg);
                        stream.Flush();
                    }
                    catch {
                        Init_DisConnection();
                    }
                    finally
                    {
                        if (stream != null)
                        {
                            stream.Close();
                        }
                    }
                }
                else
                {
                    isConnected = false;
                }
                */

                #endregion            
                NetworkStream ns = null;
                if (socket.Connected)
                {
                    MemoryStream mem = new MemoryStream();
                    BinaryFormatter format = new BinaryFormatter();
                    format.Serialize(mem, msg);
                    byte[] data = mem.GetBuffer();//把内存流转成字节数组
                    int memsize = (int)mem.Length;//数据长度
                    byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
                    try
                    {
                        ns = new NetworkStream(socket);
                        ns.Write(size, 0, 4);
                        ns.Write(data, 0, memsize);
                        ns.Flush();
                    }
                    catch (Exception)
                    {                   
                        Init_DisConnection();
                    }
                    finally
                    {
                        mem.Close();
                    }
                }
                else
                {
                    isConnected = false;
                }  
                
            }
            /// <summary>
            /// 接收信息
            /// </summary>
            private void ReceiveMsg()
            {
                #region 原方法  
                /*
                NetworkStream stream = null;
                try
                {
                    while (isConnected)
                    {
                        stream = new NetworkStream(socket);
                        BinaryFormatter format = new BinaryFormatter();
                        NetWordMsg msg = (NetWordMsg)format.Deserialize(stream);
                        ReveiveEvent(msg); //引发事件给DoMsg(string msg)
                    }
                }
                catch (Exception)       
                {
                    Init_DisConnection();
                }    
              */  

                #endregion            
                #region 带发送长度方法           
                BinaryFormatter format = new BinaryFormatter();
                MemoryStream mem = new MemoryStream();     
                NetworkStream ns =null;
                while (true)
                {                
                    byte[] data = new byte[4];
                    try
                    {
                        int revc=0;
                        try
                        {
                            revc = socket.Receive(data, 4, SocketFlags.None);
                            ns = new NetworkStream(socket);
                        }
                        catch (SocketException ex)
                        {
                            log.Error("连接断开", ex);
                            Init_DisConnection();
                            break;
                        }
                        finally
                        {
                            if (ns != null)
                            {
                                ns.Dispose();
                                ns = null;
                            }
                        }
                        int size = BitConverter.ToInt32(data, 0);
                        int offset = 0;
                        if (size > 0)
                        {
                            data = new byte[size];
                            revc = socket.Receive(data, size, SocketFlags.None);
                            mem.Write(data, offset, revc);
                            offset += revc;
                            size -= revc;                        //mem.Position = 0;
                            mem.Seek(0, SeekOrigin.Begin);
                            NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
                            ReveiveEvent(msg); //引发事件给DoMsg(string msg) 
                            mem = new MemoryStream();
                        }                   
                    }
                    catch (IOException ex)
                    {
                        log.Error("接收信息失败", ex);
                    }                
                }
                 
                #endregion           
            }发送和接收的数据都是用NetWordMsg类进行封装,发送和接收进行序列化和反序列化,由于代码太长,发送数据部分就不贴出来了。异常总是出现在接收数据那部分。主要的异常是“连接已关闭”。请各位帮忙解决。
      

  2.   

    是不是发生超时,断开连接了,通过heartbeat等机制来维护连接,当连接断开了,重新连接
      

  3.   

    socket出现异常很正常,重新连接或者重发就可以了。
    楼主只是网络编程这块的经验少了些
      

  4.   

    我也怀疑是超时导致断线,但是我对socket只是初步了解。实在没办法解决。希望各位能帮我修改这部分代码。先谢过了。
      

  5.   

       if (s != null && s.Connected) 
                        { 
                            int revc=0; 
                            try 
                            { 
                                revc = s.Receive(data, 4, SocketFlags.None);//读出数据长度 
                            } 
                            catch (SocketException ex) 
                            { 
                                log.Error("连接关闭", ex); 
                                s.Disconnect(false); 
                                s = null; 
                            } 
                            int size = BitConverter.ToInt32(data, 0);//得到数据长度 
                            int offset = 0; 
                            if (size > 0) 
                            { 
                                data = new byte[size];//接收数据数组 
                                revc = s.Receive(data, size, SocketFlags.None);//此方法偶尔出现“连接已关闭”的异常 
    //你前面的如果读数据长度的时候出错的话,你这个地方当然是连接已经关闭了,因为你前面在catch中作了关闭处理出错的原因可能是因为数据包粘包/
    /你应该一次将所有数据接收,然后判断接受数据的长度是否大于4,如果大于4,取出前4位,得到数据长度a,然后判断余下的部分长度是否达到a,如果大于a,取出长度a的数据进行处理,再将多余的数据进行保存用于下一次接收,如果数据长度小于4保存用于下一次接收,将多余的数据与下一次接收的数据和并,在重复一遍上面的操作就下楼上前几位说的
    通过heartbeat等机制来维护连接
    心跳包的一个例子
    byte[] inOptionValues = new byte[4 * 3];
                    BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
                    BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, 4);//一回送信間隔
                    BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, 8);//二回送信間隔
    socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    注意必须是在基础socket上设置
      

  6.   

    万分感谢homejiji的耐心解答,我原来一次读出所有数据后进行反序列化会出现
    “在分析完成之前已到流的结尾”异常,这有可能是数据包粘包引起的。现在先按你说的试试先。
      

  7.   

    还有一个问题是,我断开客户端后出现
    在 System.Net.Sockets.SocketException 中第一次偶然出现的“System.dll”类型的异常
    “ServerClient.exe”(托管): 已加载“C:\WINDOWS\assembly\GAC_MSIL\System.resources\2.0.0.0_zh-CHS_b77a5c561934e089\System.resources.dll”
    在 System.IO.IOException 中第一次偶然出现的“System.dll”类型的异常
    在 System.InvalidOperationException 中第一次偶然出现的“mscorlib.dll”类型的异常但是我没办法捕获到异常,CPU占用率非常高。不知道这么去处理这个问题,希望各位帮忙解答。
      

  8.   

    cup高的问题已经解决,但是socket异常还是一样,我的服务器SOCKET设置了超时为2分钟,服务器最多隔1分钟就会想所有客户端发送数据。但是还是在Receive()这个地方出现“远程主机强迫关闭现有连接”.主要是不知道系统为什么会自动关闭连接,而且捕获不到其它异常。等待各位帮忙解决,谢谢!
      

  9.   

    服务器接收数据方法三(代替方法一、方法二)
    这是按照8楼的朋友改进的
     #region 方法三   
                    
                    if (s != null && s.Connected)
                    {
                        byte[] data = new byte[1024 * 10];//每次接收1024字节
                        int revc = 0;//实际接收数据大小
                        try
                        {
                            revc = s.Receive(data, 1024 * 10, SocketFlags.None);//读出数据长度
                        }
                        catch (Exception ex)
                        {
                            //s.Disconnect(false);
                            s.Shutdown(SocketShutdown.Both);
                            //s = null;
                            socketsMap.Remove(str[0]);
                            log.Error(str[0] + "连接断开", ex);
                            break;
                        }
                        for (int i = 0; i < revc; i++)
                        {
                            receBytes.Add(data[i]);
                        }
                        revc = 0;
                        try
                        {
                            if (receBytes.Count > 4)//接收完毕
                            {
                                byte[] bt = new byte[4];
                                receBytes.CopyTo(0, bt, 0, 4);
                                int size = BitConverter.ToInt32(bt, 0);
                                if (receBytes.Count >= size + 4)//数据接收完整
                                {
                                    receBytes.RemoveRange(0, 4);
                                    byte[] mess = new byte[size];
                                    receBytes.CopyTo(0, mess, 0, size);
                                    mem.Write(mess, 0, size);
                                    mem.Seek(0, SeekOrigin.Begin);
                                    NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
                                    //处理信息,引发事件给DoMsg(String msg)
                                    ReveiveEvent(s, msg);
                                    mem = new MemoryStream();
                                    receBytes.RemoveRange(0, size);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            log.Error("格式化信息失败", ex);
                            //s.Disconnect(false);
                            //s = null;
                        }
                    }
                    else {                    
                        socketsMap.Remove(str[0]);
                        log.Error(str[0]+"连接断开");
                        break;
                    }
                    
                    #endregion
      

  10.   

    服务器发送数据的方法
    /// <summary>
            /// 发送信息
            /// </summary>
            /// <param name="ip"></param>
            /// <param name="msg"></param>
            private void sendMsg(string ip, NetWordMsg msg)
            {
                #region 原方法
                /*
                BinaryFormatter format = new BinaryFormatter();
                if (ip != null)
                {
                    Socket s = socketsMap[ip] as Socket;
                    if (s != null)
                    {
                        NetworkStream stream = new NetworkStream(s);
                        try
                        {
                            format.Serialize(stream, msg);
                            stream.Flush();
                        }
                        catch (Exception)
                        {
                            log.Error("序列化信息失败");
                            lock (socketsMap)
                            {
                                socketsMap.Remove(ip);
                            }
                            //RemoFromMachinList(ip);
                            try
                            {
                                s.Disconnect(false);
                                s.Close();
                            }
                            catch (Exception ex)
                            {
                                throw ex;
                            }
                        }
                        finally
                        {
                            stream.Close();
                        }
                    }
                }
                else//向所有客户发送信息
                {
                    if (socketsMap.Count == 0)
                    {
                        return;
                    }
                    lock (socketsMap)
                    {
                        IDictionaryEnumerator enumerator = socketsMap.GetEnumerator();
                        Socket temps;
                        while (enumerator.MoveNext())
                        {
                            temps = enumerator.Value as Socket;
                            if (temps != null)
                            {
                                NetworkStream stream = new NetworkStream(temps);
                                try
                                {
                                    format.Serialize(stream, msg);
                                    stream.Flush();
                                }
                                catch (Exception)
                                {
                                    log.Error("初始化失败");
                                    continue;
                                }
                                finally
                                {
                                    stream.Close();
                                }
                            }
                            Thread.Sleep(10);//线程暂停10毫秒
                        }
                    }
                }
               */

                #endregion
                #region 发送带长度的方法
                BinaryFormatter format = new BinaryFormatter();
                if (ip != null)
                {
                    Socket s = socketsMap[ip] as Socket;
                    MemoryStream mem = new MemoryStream();
                    if (s != null && s.Connected)
                    {
                        NetworkStream ns = null;
                        format.Serialize(mem, msg);
                        byte[] data = mem.GetBuffer();//把内存流转成字节数组
                        int memsize = (int)mem.Length;//数据长度
                        byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
                        try
                        {
                            ns = new NetworkStream(s);
                            ns.Write(size, 0, 4);
                            ns.Write(data, 0, memsize);
                            ns.Flush();
                        }
                        catch (Exception ex)
                        {
                            lock (socketsMap)
                            {
                                socketsMap.Remove(ip);
                            }
                            log.Error("连接关闭,无法发送数据!", ex);
                            s.Disconnect(false);
                            s.Close();
                            //s = null;
                        }
                        finally
                        {
                            mem.Close();
                        }                }
                }
                else//向所有客户发送信息
                {
                    if (socketsMap.Count == 0)
                    {
                        return;
                    }
                    lock (socketsMap)
                    {
                        IDictionaryEnumerator enumerator = socketsMap.GetEnumerator();
                        Socket temps;
                        while (enumerator.MoveNext())
                        {
                            temps = enumerator.Value as Socket;
                            MemoryStream mem = new MemoryStream();
                            if (temps != null)
                            {
                                NetworkStream ns = null;
                                format.Serialize(mem, msg);
                                byte[] data = mem.GetBuffer();//把内存流转成字节数组
                                int memsize = (int)mem.Length;//数据长度
                                byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
                                try
                                {
                                    ns = new NetworkStream(temps);
                                    ns.Write(size, 0, 4);
                                    ns.Write(data, 0, memsize);
                                    ns.Flush();
                                }
                                catch (IOException ex)
                                {
                                    lock (socketsMap)
                                    {
                                        socketsMap.Remove(temps.RemoteEndPoint.ToString().Split(':')[0]);
                                    }
                                    log.Error("连接关闭,无法发送数据!", ex);
                                    temps.Disconnect(false);
                                    temps.Close();
                                    //temps = null;
                                }
                                finally
                                {
                                    mem.Close();
                                }
                            }
                            Thread.Sleep(50);//线程暂停10毫秒
                        }
                    }
                }
                #endregion        }socketsMap 是HashTable,用于保存客户Socket,key是ip,value是socket
      

  11.   

    今天才明白,socket是无法保持长连接的。系统会根据资源关闭连接。心跳机制也是在SOCKET连接断开后重新连接。
    用阻塞模式没必要用心跳。只能在socket异常断开后重新连接。谢谢各位的解答。