C# socket通信出现内存不足的问题

时间:2021-04-20 06:14:43
一个关于即时信息的客户端,和服务端建立通信后,一直进行信息的发送,没有处理,运行一段时间后,会卡住,提示内存不足,断线,哪位做过的可以说说怎么解决,在“this.Invoke(new ThreadStart(delegate()
                    {
                        AlertCustom alert = new AlertCustom(recv);
                        alert.CaptionText = "即时通讯信息";
                        alert.Show(this);会在此处提示内存不足
                        //GC.Collect();

                    }));”
整个代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using Client;
using Maticsoft.Model;
using Balloon;
using DevComponents.DotNetBar;
using log4net;


namespace Client
{
    public partial class Frmain : Form
    {
        #region 私有成员变量
        private static ILog log = LogManager.GetLogger(typeof(Frmain));
        private static int port = 2323;
        private static ManualResetEvent receiveDone = new ManualResetEvent(false);
        private static Thread ReviceThread;
        private static Thread KeepAliveThread;
        private static object obj = new object();
        private static string MessageSN = "0";
        private static IPAddress ipAddress;
        string str = "192.168.11.1";
        string str_ip;
        #endregion
        #region 公有成员变量
        public static DateTime tempDate = DateTime.Now;
        public static Socket client;
        public static bool isNeedAlive = true;
        StateObject state = new StateObject();
        #endregion
        public Frmain()
        {
            InitializeComponent();
            StartClient();
        }
        /// <summary>
        /// 启动客户端
        /// </summary>
        private void StartClient()
        {
            try
            {
                ipAddress = IPAddress.Parse(str);
                ConnnectServer();
                StartSendServer();
                RunReceive();
            //    RunKeepAlive();

            }
            catch (Exception e)
            {

                MessageBox.Show(e.Message );
                log.Error(e.Message.ToString());
                str_ip = string.Empty;

            }
        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        public void ConnnectServer()
        {
            try
            {
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
                client = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                client.BeginConnect(remoteEP,
                    new AsyncCallback(ConnectCallback), client);

            }
            catch (Exception e)
            {
                log.Error(e.Message.ToString());
            }
        }

        public void RunReceive()
        {
            ReviceThread = new Thread(new ThreadStart(Receive));
            ReviceThread.IsBackground = true;
            ReviceThread.Start();
        }

        private void Receive()
        {
            try
            {
                StateObject state = new StateObject();
                state.workSocket = client;
                while (true)
                {
                    receiveDone.Reset();
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                    receiveDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                // Console.WriteLine(e.ToString());
                StateObject state = new StateObject();
                client = state.workSocket;
                client.Shutdown(SocketShutdown.Both);
                client.BeginDisconnect(false, new AsyncCallback(BeginDisconnectCallback), state);
                log.Error(e.Message.ToString());

            }

        }

        /// <summary>
        /// 读取连接回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                //还原原始的TcpClient对象
                Socket client = (Socket)ar.AsyncState;
                MessageBox.Show("与服务器" + client.RemoteEndPoint + "连接成功");
                log.Info("与服务器连接成功");

            }
            catch (Exception e)
            {
                //  MessageBox.Show(e.Message);
                log.Error("读取回调错误", e);
            }
            finally
            {

            }

        }
        /// <summary>
        /// 取本机用户名,域名
        /// </summary>
        private void StartSendServer()
        {
            USER_IP data = new USER_IP();
            //IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
            //foreach (IPAddress ip in ips)
            //{
            //    str_ip += ip.ToString();//将得到的IP地址显示在文本框中
            //}
            // data.TOUSER_IP = str_ip;
            data.USER_DOMAIN = Environment.UserDomainName;
            data.USER_ID = Environment.UserName;
            data.MESSAGESN = MessageSN;
            data.WF_NAME = "0";
            data.USER_STATE = "0";
            SendUpConnectCommand(data);
            str_ip = string.Empty;
        }
        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="data"></param>
        public static void SendUpConnectCommand(USER_IP data)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(data.USER_ID);
            sb.Append("|" + data.USER_DOMAIN);
            sb.Append("|" + data.MESSAGESN);
            sb.Append("|" + data.WF_NAME);
            sb.Append("|" + data.USER_STATE);
            string message = string.Empty;
            message = sb.ToString();
            Send(client, message);
        }
        /// <summary>
        /// 发送信息给服务端
        /// </summary>

        public static void Send(Socket handler, string data)
        {
            byte[] byteData = Encoding.Default.GetBytes(data);

            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0,
              new AsyncCallback(SendCallback), handler);
        }

        /// <summary>
        /// 给服务端端发确认信息
        /// </summary>

        public static void SendCallback(IAsyncResult ar)
        {

            try
            {
                Socket handler = (Socket)ar.AsyncState;
                int bytesSent = handler.EndSend(ar);
            }
            catch (Exception e)
            {
                // Console.WriteLine(e.ToString());
                MessageBox.Show("来自客户端。接收数据失败,请检查您设置的IP地址和端口,以及连接是否正确。\n" + e.Message);
                log.Error("发送确认信息错误", e);
                return;
            }
        }


        /// <summary>
        /// 接受服务回调
        /// </summary>
        private void ReceiveCallback(IAsyncResult ar)
        {
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;
            try
            {

                int bytesRead = client.EndReceive(ar);//接收客户端数据
                if (bytesRead > 0)
                {
                    AnalysisData(Encoding.Default.GetString(state.buffer, 0, bytesRead), handler);
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);
                }
            }
            catch (Exception e)
            {
                // Console.WriteLine(e.ToString());
                //   MessageBox.Show(e.Message);
                if (handler.Connected)
                {
                    handler.Shutdown(SocketShutdown.Both);
                    handler.BeginDisconnect(false, new AsyncCallback(BeginDisconnectCallback), state);
                }
                log.Error(e.Message.ToString());
            }
        }
        /// <summary>
        /// 接受断开回调
        /// </summary>
        private static void BeginDisconnectCallback(IAsyncResult ar)//接收来自服务端的返回确认信息
        {
            try
            {
                StateObject state = (StateObject)ar.AsyncState;
                state.workSocket.EndDisconnect(ar);
            }
            catch
            {
                Socket socket = (Socket)ar.AsyncState;
                socket.EndDisconnect(ar);
            }

        }

        private void AnalysisData(string recv, Socket handler)
        {
            try
            {
                //Control.CheckForIllegalCrossThreadCalls = false;
                //   AlertCustom alert = new AlertCustom(recv);
                //   alert.Show();
                //  Thread secondThread = new Thread(alert.Show);
                //   secondThread.Start();
                this.Invoke(new ThreadStart(delegate()
                    {
                        AlertCustom alert = new AlertCustom(recv);
                        alert.CaptionText = "即时通讯信息";
                        alert.Show(this);
                        //GC.Collect();

                    }));


                // MessageBox.Show(recv);
            }
            catch (Exception e)
            {
                log.Error(e.Message.ToString());
            }
            finally
            {
                // Dispose();
            }

        }
        private void button1_Click(object sender, EventArgs e)
        {
            str = tbServerIP.Text.Trim();
            port = Convert.ToInt32(tbServerPort.Text);
            //   ipAddress = IPAddress.Parse(str);
            StartClient();
            tbServerIP.Text = "";
            tbServerPort.Text = "";
            str = string.Empty;
        }
        private void Frmain_FormClosing(object sender, FormClosingEventArgs e)
        {
            //  Application.DoEvents();
            // thread.Abort();
            //  ReviceThread.Abort();
            Application.ExitThread();
            Application.Exit();
            //   Application.Exit();
        }

11 个解决方案

#1


class AlertCustom {
    void Show(object o) {
        throw new OutOfMemoryException(); //将这行注释掉
    }
}

#2


把你的 while(true)、receiveDone.WaitOne()语句删掉。

既然准确地通过 BeginReceive 监听事件回调,另外再弄一个疯狂地死循环去重复注册就没有必要了,只要有一条 BeginReceive 就足够了。

#3


在没有消息到来时,根本不存在什么“占用线程”的事情,也根本不需要什么 receiveDone.WaitOne() 来阻塞程序。搞明白这个道理,你的程序就简单多了,也能清晰、也能保证执行效率。

#4


另外要说明一下,你的
AnalysisData(Encoding.Default.GetString(state.buffer, 0, bytesRead), handler);
代码这里显然也是有设计问题的。

buffer 里边只有当前一次异步 Receive 收到的数据,但是数据流相对于发送端,是可能“分包、粘包”的,所以并不能假设说 buffer 中 bytesRead 个字节就恰好是一个消息(可能其中还没有消息的结束部分,也可能是包括了下一个消息的开头部分)。

所以除了 state.buffer,你还应该在 state 中保存一个 List<byte> 类型的属性,用来“累积”保存数据,然后在这里将 bytesRead 个字节保存到这个属性,再去判断这个属性中没有一个完整的消息。如果有完整消息,依次取出完整消息,再去分别执行 AnalysisData 操作。而这个属性整下的不完整消息内容,仍然要保存在其中,等着后续 Receive 到的字节加入,才能开始命令解析。

另外,AnalysisData 里边的代码应该使用“异步”操作,不让其执行来阻塞 I/O,不让其阻塞随后的 handler.BeginReceive 语句。而异步线程操作中如果涉及到 UI 控件操作,应该使用 BeginInvoke 而不是 Invoke 去注册。以免阻塞。

最后,不要滥用Encoding.Default.。你应该使用明确的 Encoding.UTF8 或者明确的 Encoding.GetEncoding("gb2312") 之类的代码。因为 Default 是取当前window系统设置的代码,在不同的window版本(例如中文简体版、中文繁体版、英文版、法文版.....)上返回值是不同,而且就算是同样是中文简体版,用户不同(例如一个日本人再用)其设置可能也不同,那么 Encoding.Default 返回的值就不同。那么胡乱写 Encoding.Default 其实就会产生歧义,程序结果完全错乱了。你应该确定使用某一种统一规范的编码,而不是胡乱用什么 Encoding.Default。

#5


引用 4 楼 sp1234 的回复:
另外要说明一下,你的
AnalysisData(Encoding.Default.GetString(state.buffer, 0, bytesRead), handler);
代码这里显然也是有设计问题的。

buffer 里边只有当前一次异步 Receive 收到的数据,但是数据流相对于发送端,是可能“分包、粘包”的,所以并不能假设说 buffer 中 bytesRead 个字节就恰好是一个消息(可能其中还没有消息的结束部分,也可能是包括了下一个消息的开头部分)。

所以除了 state.buffer,你还应该在 state 中保存一个 List<byte> 类型的属性,用来“累积”保存数据,然后在这里将 bytesRead 个字节保存到这个属性,再去判断这个属性中没有一个完整的消息。如果有完整消息,依次取出完整消息,再去分别执行 AnalysisData 操作。而这个属性整下的不完整消息内容,仍然要保存在其中,等着后续 Receive 到的字节加入,才能开始命令解析。

另外,AnalysisData 里边的代码应该使用“异步”操作,不让其执行来阻塞 I/O,不让其阻塞随后的 handler.BeginReceive 语句。而异步线程操作中如果涉及到 UI 控件操作,应该使用 BeginInvoke 而不是 Invoke 去注册。以免阻塞。

最后,不要滥用Encoding.Default.。你应该使用明确的 Encoding.UTF8 或者明确的 Encoding.GetEncoding("gb2312") 之类的代码。因为 Default 是取当前window系统设置的代码,在不同的window版本(例如中文简体版、中文繁体版、英文版、法文版.....)上返回值是不同,而且就算是同样是中文简体版,用户不同(例如一个日本人再用)其设置可能也不同,那么 Encoding.Default 返回的值就不同。那么胡乱写 Encoding.Default 其实就会产生歧义,程序结果完全错乱了。你应该确定使用某一种统一规范的编码,而不是胡乱用什么 Encoding.Default。
您好,按照您说的,删掉“ while(true)、receiveDone.WaitOne()语句”,运行一段时间还是会出现内存不足,在程序运行的时候,发现如果没有处理,应用程序进程是一直在累加。就是想,如果服务端一直发送消息,怎么才能避免出现内存不足这种情况呢?如果可以,希望您能加下我的QQ:1012589162,非常感谢!

#6


我写socket通信时转换数据格式一般用Encoding.UTF8...语句来进行转换,UTF8是一种针对Unicode的可变长度字符编码,又称万国码;so,我建议最好使用UTF8

#7


引用 5 楼 cwt19902010 的回复:
您好,按照您说的,删掉“ while(true)、receiveDone.WaitOne()语句”,运行一段时间还是会出现内存不足,在程序运行的时候,发现如果没有处理,应用程序进程是一直在累加。就是想,如果服务端一直发送消息,怎么才能避免出现内存不足这种情况呢?如果可以,希望您能加下我的QQ:1012589162,非常感谢!


我只是简单看一下一眼就能看出是没有按照基本设计模式来编程的几个问题。你的程序中一定还有至少5、6个严重问题,需要你自己调试,把 bug 修改掉。

例如你用了全局 client 变量,那么你可以自己搜一下都在哪些地方用了这个变量,从并发多线程 “冲突”的角度看看他会不会把你的程序搞乱、产生各种诡异的错误,最终崩溃?!!

自己检查你的程序的 bug。

#8


太乱了,你用了信号量,ReceiveCallback 里为什么不receiveDone.set();

#9


Windows应用程序的栈好像是2MB,你在栈上分配的太多了对象,又不销毁肯定是这样了。

#10


引用 9 楼 wq1234wq 的回复:
Windows应用程序的栈好像是2MB,你在栈上分配的太多了对象,又不销毁肯定是这样了。
是的,关键是信息没有处理,销毁了也就没有了

#11


该回复于2017-03-29 17:33:10被管理员删除

#1


class AlertCustom {
    void Show(object o) {
        throw new OutOfMemoryException(); //将这行注释掉
    }
}

#2


把你的 while(true)、receiveDone.WaitOne()语句删掉。

既然准确地通过 BeginReceive 监听事件回调,另外再弄一个疯狂地死循环去重复注册就没有必要了,只要有一条 BeginReceive 就足够了。

#3


在没有消息到来时,根本不存在什么“占用线程”的事情,也根本不需要什么 receiveDone.WaitOne() 来阻塞程序。搞明白这个道理,你的程序就简单多了,也能清晰、也能保证执行效率。

#4


另外要说明一下,你的
AnalysisData(Encoding.Default.GetString(state.buffer, 0, bytesRead), handler);
代码这里显然也是有设计问题的。

buffer 里边只有当前一次异步 Receive 收到的数据,但是数据流相对于发送端,是可能“分包、粘包”的,所以并不能假设说 buffer 中 bytesRead 个字节就恰好是一个消息(可能其中还没有消息的结束部分,也可能是包括了下一个消息的开头部分)。

所以除了 state.buffer,你还应该在 state 中保存一个 List<byte> 类型的属性,用来“累积”保存数据,然后在这里将 bytesRead 个字节保存到这个属性,再去判断这个属性中没有一个完整的消息。如果有完整消息,依次取出完整消息,再去分别执行 AnalysisData 操作。而这个属性整下的不完整消息内容,仍然要保存在其中,等着后续 Receive 到的字节加入,才能开始命令解析。

另外,AnalysisData 里边的代码应该使用“异步”操作,不让其执行来阻塞 I/O,不让其阻塞随后的 handler.BeginReceive 语句。而异步线程操作中如果涉及到 UI 控件操作,应该使用 BeginInvoke 而不是 Invoke 去注册。以免阻塞。

最后,不要滥用Encoding.Default.。你应该使用明确的 Encoding.UTF8 或者明确的 Encoding.GetEncoding("gb2312") 之类的代码。因为 Default 是取当前window系统设置的代码,在不同的window版本(例如中文简体版、中文繁体版、英文版、法文版.....)上返回值是不同,而且就算是同样是中文简体版,用户不同(例如一个日本人再用)其设置可能也不同,那么 Encoding.Default 返回的值就不同。那么胡乱写 Encoding.Default 其实就会产生歧义,程序结果完全错乱了。你应该确定使用某一种统一规范的编码,而不是胡乱用什么 Encoding.Default。

#5


引用 4 楼 sp1234 的回复:
另外要说明一下,你的
AnalysisData(Encoding.Default.GetString(state.buffer, 0, bytesRead), handler);
代码这里显然也是有设计问题的。

buffer 里边只有当前一次异步 Receive 收到的数据,但是数据流相对于发送端,是可能“分包、粘包”的,所以并不能假设说 buffer 中 bytesRead 个字节就恰好是一个消息(可能其中还没有消息的结束部分,也可能是包括了下一个消息的开头部分)。

所以除了 state.buffer,你还应该在 state 中保存一个 List<byte> 类型的属性,用来“累积”保存数据,然后在这里将 bytesRead 个字节保存到这个属性,再去判断这个属性中没有一个完整的消息。如果有完整消息,依次取出完整消息,再去分别执行 AnalysisData 操作。而这个属性整下的不完整消息内容,仍然要保存在其中,等着后续 Receive 到的字节加入,才能开始命令解析。

另外,AnalysisData 里边的代码应该使用“异步”操作,不让其执行来阻塞 I/O,不让其阻塞随后的 handler.BeginReceive 语句。而异步线程操作中如果涉及到 UI 控件操作,应该使用 BeginInvoke 而不是 Invoke 去注册。以免阻塞。

最后,不要滥用Encoding.Default.。你应该使用明确的 Encoding.UTF8 或者明确的 Encoding.GetEncoding("gb2312") 之类的代码。因为 Default 是取当前window系统设置的代码,在不同的window版本(例如中文简体版、中文繁体版、英文版、法文版.....)上返回值是不同,而且就算是同样是中文简体版,用户不同(例如一个日本人再用)其设置可能也不同,那么 Encoding.Default 返回的值就不同。那么胡乱写 Encoding.Default 其实就会产生歧义,程序结果完全错乱了。你应该确定使用某一种统一规范的编码,而不是胡乱用什么 Encoding.Default。
您好,按照您说的,删掉“ while(true)、receiveDone.WaitOne()语句”,运行一段时间还是会出现内存不足,在程序运行的时候,发现如果没有处理,应用程序进程是一直在累加。就是想,如果服务端一直发送消息,怎么才能避免出现内存不足这种情况呢?如果可以,希望您能加下我的QQ:1012589162,非常感谢!

#6


我写socket通信时转换数据格式一般用Encoding.UTF8...语句来进行转换,UTF8是一种针对Unicode的可变长度字符编码,又称万国码;so,我建议最好使用UTF8

#7


引用 5 楼 cwt19902010 的回复:
您好,按照您说的,删掉“ while(true)、receiveDone.WaitOne()语句”,运行一段时间还是会出现内存不足,在程序运行的时候,发现如果没有处理,应用程序进程是一直在累加。就是想,如果服务端一直发送消息,怎么才能避免出现内存不足这种情况呢?如果可以,希望您能加下我的QQ:1012589162,非常感谢!


我只是简单看一下一眼就能看出是没有按照基本设计模式来编程的几个问题。你的程序中一定还有至少5、6个严重问题,需要你自己调试,把 bug 修改掉。

例如你用了全局 client 变量,那么你可以自己搜一下都在哪些地方用了这个变量,从并发多线程 “冲突”的角度看看他会不会把你的程序搞乱、产生各种诡异的错误,最终崩溃?!!

自己检查你的程序的 bug。

#8


太乱了,你用了信号量,ReceiveCallback 里为什么不receiveDone.set();

#9


Windows应用程序的栈好像是2MB,你在栈上分配的太多了对象,又不销毁肯定是这样了。

#10


引用 9 楼 wq1234wq 的回复:
Windows应用程序的栈好像是2MB,你在栈上分配的太多了对象,又不销毁肯定是这样了。
是的,关键是信息没有处理,销毁了也就没有了

#11


该回复于2017-03-29 17:33:10被管理员删除