当前位置:编程学习 > C#/ASP.NET >>

200RMB解决一个C# socket传输数据网络不丢失数据的问题

C#通过socket 异步传输数据功能已实现,如果网络不好的情况(电信服务器给网通服务器发送数据)偶尔会出现数据丢失 客户端发送数据成功,但服务端接口不到,如何才能避免网络不好的情况下数据正常接收?如果帮我侧地解决掉这个问题,绝对按标题说的给帮我解决问题的朋友打200元,绝不食言!

一下是我的代码--------------------------------------- 

服务端代码  用线程调用 Listen()方法 

private void Listen() 
        { 

            string strIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString(); 
            if (strIP.Length < 9) 
            { 
                strIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[1].ToString(); 
            } 

            IPAddress ip = IPAddress.Parse(strIP); 
            IPEndPoint ipe = new IPEndPoint(ip, 8889); 
            sockets = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
            sockets.Bind(ipe); 
            sockets.Listen(500); 
            while (isRun) 
            { 
                Control.CheckForIllegalCrossThreadCalls = false; 

                try 
                { 
                    allDone.Reset(); 
                    sockets.BeginAccept(new AsyncCallback(AcceptCallback), sockets); 
                    allDone.WaitOne(); 
                } 
                catch 
                { 

                } 
            } 
          
        } 
        private void AcceptCallback(IAsyncResult ar) 
        { 
            try 
            { 
                allDone.Set(); 
                Socket listener = (Socket)ar.AsyncState; 
                Socket handler = listener.EndAccept(ar); 

                StateObject state = new StateObject(); 
                state.workSocket = handler; 
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); 
            } 
            catch 
            { 
                
            } 
        } 
        private void ReadCallback(IAsyncResult ar) 
        { 
            try 
            { 
                string content = string.Empty; 

                StateObject state = (StateObject)ar.AsyncState; 
                Socket handler = state.workSocket; 
                int bytesRead = handler.EndReceive(ar); 

                if (bytesRead > 0) 
                { 
                    state.sb.Append(Encoding.Default.GetString(state.buffer, 0, bytesRead)); 
                  
                    string info = state.sb.ToString(); 

                    } 
                } 
            } 
            catch 
            { } 
        } 

--------------------编程问答-------------------- 负责帮顶 顺便沙发 --------------------编程问答-------------------- 短工 --------------------编程问答-------------------- 小型外包?
不懂通信,要不接定了。 --------------------编程问答-------------------- 希望大家能够帮帮我啊,都郁闷死了 --------------------编程问答-------------------- 你可以做一个应答的机制和超时的机制

假如客户端发送数据成功,服务端必须要回发一个标识,假如超过一定时间,就自动重发 --------------------编程问答-------------------- 首先,你这是使用异步的语法,实际上是同步编程。我从来不使用WaitHandle用于服务器监听。msdn上这类使用信号量阻塞来在异步代码中模拟同步控制的例子,每当我看到我就骂一遍。你要么就是用同步的Accept方法,要么就是用真正的异步多线程多并发控制。 --------------------编程问答-------------------- 其次,你的 allDone.Set();  似乎太早了吧?

不过我所谓了,这类用信号量阻塞方式来冒充异步编程的东西,东西我看到了就不想测试了。 --------------------编程问答--------------------
引用 7 楼 sp1234 的回复:
其次,你的 allDone.Set();  似乎太早了吧?

不过我所谓了,这类用信号量阻塞方式来冒充异步编程的东西,东西我看到了就不想测试了。



老兄  能不能给个演示的代码啊? 其实我对socket 也是一知半解的,谢谢了 --------------------编程问答-------------------- 刚接触C#,socket还没用过 - -
帮顶 --------------------编程问答-------------------- 首先问题还不够细。。
其次个人感觉csdn里面分比钱重要。。

感觉楼主还是胆子小点。你把钱放在猪八戒上面,估计现在任务已经解决了。 --------------------编程问答-------------------- 首先,你是使用ProtocolType.Tcp, 在IP/TCP协议中,应用层是不需要理会丢包问题的,因为底层会有重发机制,面向连接的SOCKET中是没有接收不到包的情况的,也没有丢包的说法,这个基础需要去补补。
好的,现在的问题是如何检测掉线的问题,在 sockets.BeginAccept方法中.NET封装了一些异常捕获,在SocketException中:

        try{
              //...
        }
        catch (SocketException e)   
        {   
            //10035 == WSAEWOULDBLOCK   
            if (e.NativeErrorCode.Equals(10035))   
            {   
                //仍然处于连接状态,但是发送可能被阻塞   
            }   
            else  
            {   
                //连接错误,返回错误代码:e.NativeErrorCode   
                Socket_e.ResCode = e.NativeErrorCode.ToString();   
                SocketEvent(this, Socket_e);   
                return;   
            }   
        }   
    catch (Exception e)   
         {      
             //其他错误   
             Socket_e.ResCode = e.ToString();   
             SocketEvent(this, Socket_e);   
             return;                       
         }        


如果是UDP的连接就要考虑丢包的解决方案。一般是在判断包头的信息,重组,丢弃等做法。 --------------------编程问答-------------------- 在MSDN上,这部分的实例代码确实不实用,在很多地方需要自己修改下,重要的是理解方法的使用,建议弄清楚SOCKET这个类,如果有时间,建议了解下WIN32中的SOCKET编程,对更加深刻的理解网络编程有好处,.NET虽然封装了很多操作,SOCKET的操作更是简单的一塌糊涂,但是,这样对真正的深入编程是没有好处的,没有弄清楚原理,只有沦为代码搬运工。 --------------------编程问答-------------------- TCP是不会丢包的,是不是连接断了,如果是短连接就加个心跳包,要真不是连接的问题,估计楼主用的是UDP了 --------------------编程问答-------------------- 通讯不是很懂 帮你顶 --------------------编程问答-------------------- 楼主 SP1234可以大牛加起私聊一下看他有空没有? --------------------编程问答-------------------- 帮顶 --------------------编程问答-------------------- 建议思路:

1.添加链路测试,即客户端定时给服务端发送测试报告,服务端在显眼的地方显示连接状态,这样可以及时发现网络状况或异常中断。

2.客户端与服务端添加确认收包信号,即客户端每发送一次包带上流水号,服务端收取后,回送流水号给客户端,此时客户端才发送下一个数据包,否则,继续发送前一个包,直到成功接收为止。

3.为了便于准确的把所有数据包,都能成功发送出去,客户端可建立缓冲区,比如存储最近的100个包,先进先出原则,每个包都有对应的流水号,这样即便在网络中断的情况下,客户端能把数据包有序存着,等待链路通了之后,便可将缓冲区的数据包依次发送出去。

祝你好运! --------------------编程问答-------------------- 学习 --------------------编程问答--------------------
引用 6 楼 sp1234 的回复:
首先,你这是使用异步的语法,实际上是同步编程。我从来不使用WaitHandle用于服务器监听。msdn上这类使用信号量阻塞来在异步代码中模拟同步控制的例子,每当我看到我就骂一遍。你要么就是用同步的Accept方法,要么就是用真正的异步多线程多并发控制。

为什么不建议使用 WaitHandle ? --------------------编程问答--------------------
引用 7 楼 sp1234 的回复:
其次,你的 allDone.Set();  似乎太早了吧?

不过我所谓了,这类用信号量阻塞方式来冒充异步编程的东西,东西我看到了就不想测试了。

这是MSDN上的标准写法,没有任何问题。

楼主你的问题是出在catch块中没有做任何处理,因为一旦因网络不稳定导致数据传输失败,catch中肯定会有异常抛出,你的代码中直接将这类异常丢弃了,这显然是不行的。 --------------------编程问答--------------------
引用 20 楼 qldsrx 的回复:
楼主你的问题是出在catch块中没有做任何处理,因为一旦因网络不稳定导致数据传输失败,catch中肯定会有异常抛出,你的代码中直接将这类异常丢弃了,这显然是不行的。


同意,就算网络连接异常所丢失,引发的异常你扔掉了
应该是捕获到异常就重发

你说的客户端发送正常,服务端接受不到数据,其实客户端根本没有正常发送,有异常都吃掉了,所以你才认为客户端发送正常 --------------------编程问答--------------------
引用 21 楼 yangglemu 的回复:
引用 20 楼 qldsrx 的回复:

楼主你的问题是出在catch块中没有做任何处理,因为一旦因网络不稳定导致数据传输失败,catch中肯定会有异常抛出,你的代码中直接将这类异常丢弃了,这显然是不行的。


同意,就算网络连接异常所丢失,引发的异常你扔掉了
应该是捕获到异常就重发

你说的客户端发送正常,服务端接受不到数据,其实客户端根本没有正常发送,有异常都吃掉了,所以你才认为客户端发送正常



客户端确实正常的,因为客户端有异常处理,如果连接不到或者发送不了数据会提示错误的,
服务端做过异常处理,偶尔出现 “远程主机强迫关闭了一个先有的链接”,因为服务器是一直处于监听状态,一出异常就会影响接收数据,又不知道怎么解决这个问题,所以只能把异常处理给去掉,

服务端接收不到数据,是偶尔才会接收不到的,比如,电信服务器的客户端,给网通服务器的服务端发信息(一般发送20个之内字母,数据量不大,但要不定时的发送),网络不稳定的情况才会出现服务端接收不到数据,
电信服务器 对 电信服务器发送数据,基本上不会出现这种情况。
希望有对通讯编程熟悉的朋友提示下怎么解决这个棘手的问题,先谢了 --------------------编程问答-------------------- 学习了 --------------------编程问答--------------------     本来TCP应该是出错会重传的,但是网通和电信之间传输的话,很可能是在中间路由的时候出现了问题,结果连重传都没做,那样的话,你最好增加自己的消息验证,方法如下:
    首先给每次发送的消息编号,使用GUID即可,如果是发送XML更佳。当消息发送到服务器端后,服务器端返回一个确认消息给客户端,确认消息也要带上刚收到的消息的编号,这样客户端得到了这个确认消息就认为服务器端正常接收,否则就有可能接收失败,如果未收到确认消息(也可能是确认消息发送失败),客户端再次发送上次的消息,编号仍旧用上次的,这样服务器端即使重复收到了消息,也可以通过编号来识别是否是上次的消息重发了。 --------------------编程问答-------------------- 17楼的华仔的方法不错,也就是我现在的工业控制系统里使用的控制协议模式,关于17#提供的方法,需要编写相关的通讯协议(应用层协议),来保证数据的完整。 --------------------编程问答-------------------- 用草谷协议封装下吧,呵呵 --------------------编程问答--------------------  路过、 --------------------编程问答-------------------- 问题被猪八戒的网友解决了,也谢谢各位的回答,我把代码公布下,希望有这方面问题的朋友可以参考下:

客户端代码:

public partial class Form1 : Form
    {
        string host = "192.168.1.5";
        int port = 8888;
        Thread thread;
        int rcount;
        int fcount;
        int sendcount = 1;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            thread = new Thread(new ThreadStart(send));
            thread.Start();
            
        }
        private void UpdateUI(object o, System.EventArgs e)
        {
            sendcount += 1;
            label1.Text = o.ToString();
        }
        private void send()
        {
            string msg = "";
            for (int i = 0; i < 10000; i++)
            {
                msg = "第" + i + "次发送数据";
                TcpClient client = new TcpClient();
                client.Connect(host, port);
                NetworkStream ns = client.GetStream();
                ns.ReadTimeout = 1000;
                byte[] bs = Encoding.Default.GetBytes(msg);
                bool isSend = true;
                while (isSend)
                {
                    if (client.Connected == false)
                    {
                        client.Close();
                        client = new TcpClient();
                        client.Connect(host, port);
                        ns = client.GetStream();
                        ns.ReadTimeout = 1000;
                    }
                    ns.Write(bs, 0, bs.Length);
                    try
                    {
                        if (bs[0] == ns.ReadByte())
                        {
                            isSend = false;
                            label1.BeginInvoke(new System.EventHandler(UpdateUI), "send ok" + (rcount++));
                        }
                        else
                        {
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        label1.BeginInvoke(new System.EventHandler(UpdateUI), "send fail" + (fcount++));
                    }
                    Thread.Sleep(1000);
                }
                client.Close();
            }
        }
    } --------------------编程问答--------------------  byte[] reveidata;
        Thread thread;
        TcpListener listener;
        int reqeustcount = 0;

        /// <summary>
        /// 开始监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            thread = new Thread(new ThreadStart(Listen));
            thread.Start();
            Rolling();
        }

        /// <summary>
        /// 停止监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                listener.Stop();
                thread.Abort();
            }
            catch(Exception ex)
            {
                throw ex;
            }
            Rolling();
        }
        private void Rolling()
        {
            richTextBox1.SelectionStart = richTextBox1.Text.Length;
            richTextBox1.ScrollToCaret();
        }


        private void UpdateUI(object o, System.EventArgs e)
        {
            richTextBox1.AppendText("\n" + o.ToString());
        }
        private void Listen()
        {
            try
            {
                string hostname = System.Net.Dns.GetHostName();
                IPHostEntry ipEntry = Dns.GetHostEntry(hostname);
                IPAddress ip = ipEntry.AddressList[0];
                 listener = new TcpListener(IPAddress.Any, 8889);
                listener.Start();
                bool done = false;
                reveidata = new byte[1000];
                while (!done)
                {
                    reqeustcount += 1;
                    Socket client = listener.AcceptSocket();
                    try
                    {
                        byte[] returnNum = new byte[1];
                        client.Receive(reveidata);
                        string str = Encoding.Default.GetString(reveidata);
                        richTextBox1.BeginInvoke(new System.EventHandler(UpdateUI), "第:" + reqeustcount.ToString() + "接收数据  " + str);
                        returnNum[0] = reveidata[0];
                        client.Send(returnNum);
                    }
                    catch (Exception e)
                    {
                        richTextBox1.BeginInvoke(new System.EventHandler(UpdateUI), "端口被占用  ");
                    }
                }
            }
            catch(Exception ex)
            {
                throw ex;
 
            }
        } --------------------编程问答-------------------- 上周刚完成了一个通信的C S 程序,开始做的时候确实很烦锁,明明设计好的程序就是不听使,经过一周的不断调试,现在已经非常稳定,不知是否可帮楼主,主要思路各位都说了,而我的思路之前LZ发的贴我也回复过,如果不行可能是你细节代码的问题,有兴趣了解可加我QQ183003220 --------------------编程问答-------------------- 帮顶~ --------------------编程问答-------------------- 友情帮顶。。。 --------------------编程问答-------------------- 刚才仔细看了一下解决的代码,其实在楼主上次提问时我就告诉你应该这样做,只是那些是VB代码,可能你看不懂吧!哈哈 --------------------编程问答--------------------
引用 22 楼 iccarm 的回复:
引用 21 楼 yangglemu 的回复:
引用 20 楼 qldsrx 的回复:

楼主你的问题是出在catch块中没有做任何处理,因为一旦因网络不稳定导致数据传输失败,catch中肯定会有异常抛出,你的代码中直接将这类异常丢弃了,这显然是不行的。


同意,就算网络连接异常所丢失,引发的异常你扔掉了
应该是捕获到异常就重发

你说的客户端发送正常,服务端接受不到数据,其实客户端根本没有正常发送,有异常都吃掉了,所以你才认为客户端发送正常



客户端确实正常的,因为客户端有异常处理,如果连接不到或者发送不了数据会提示错误的,
服务端做过异常处理,偶尔出现 “远程主机强迫关闭了一个先有的链接”,因为服务器是一直处于监听状态,一出异常就会影响接收数据,又不知道怎么解决这个问题,所以只能把异常处理给去掉,

服务端接收不到数据,是偶尔才会接收不到的,比如,电信服务器的客户端,给网通服务器的服务端发信息(一般发送20个之内字母,数据量不大,但要不定时的发送),网络不稳定的情况才会出现服务端接收不到数据,
电信服务器 对 电信服务器发送数据,基本上不会出现这种情况。
希望有对通讯编程熟悉的朋友提示下怎么解决这个棘手的问题,先谢了


ping XXX.XXX.XXX.XXX -t
发现有TIMEOUT的情况直接回给领导...这种事情你的小罗罗..还解决个毛啊.....电信..网通内部都是走的私网..网络有丢包..说不定是那个交换机设备不好呢...
你要做的是网络不好..比如网络断了..重连多少次..失败了..上报一个告警..就完事了...不要一直重连...这样有的设备会认为是攻击的...会出大事的...
--------------------编程问答-------------------- 要想保证数据不丢失,就得做个握手协议...服务端接到数据,给出相应的应答返回...如果客户端没收到应答返回,则保存数据,等待下次发送 --------------------编程问答-------------------- 可以在客户端自己做一个cash保存所有你发出去的消息形成一个消息队列,然后只要有收到的回复,那么就将这条消息从队列中remove,如果没有回复则一直try发送,然后接收断就要做到同一个消息只接受一次的办法,这样一来就差不多可以了! --------------------编程问答-------------------- 你异步的话分包写 同步就不需要了 异步你定义个协议每次发多少包最后包发完了给个表示读取 了就表示数据传输玩了哈· --------------------编程问答-------------------- 学习了

                  if (client.Connected == false) 
                    { 
                        client.Close(); 
                        client = new TcpClient(); 
                        client.Connect(host, port); 
                        ns = client.GetStream(); 
                        ns.ReadTimeout = 1000; 
                    } 

这个地方做个判断,不理解,如果前一次的连接不成功,你再连一次,如果再连一次不成功呢?
你能直接发送数据?
--------------------编程问答-------------------- Mark --------------------编程问答-------------------- Mark! --------------------编程问答-------------------- !!! --------------------编程问答--------------------
引用 38 楼 lfs09 的回复:
学习了
C# codeif (client.Connected==false) 
                    { 
                        client.Close(); 
                        client=new TcpClient(); 
                        client.Connect(host, port); 
                        ns= client.GetStream(); 
                        ns.ReadTimeout=1000; 
                    }
这个地方做个判断,不理解,如果前一次的连接不成功,你再连一次,如果再连一次不成功呢?
你能直接发送数据?




可以设置下发送数量,如果3次都连接不成功,直接按失败处理 --------------------编程问答-------------------- 有sp回复的帖子,我总能学到东西 --------------------编程问答--------------------
引用 6 楼 sp1234 的回复:
首先,你这是使用异步的语法,实际上是同步编程。我从来不使用WaitHandle用于服务器监听。msdn上这类使用信号量阻塞来在异步代码中模拟同步控制的例子,每当我看到我就骂一遍。你要么就是用同步的Accept方法,要么就是用真正的异步多线程多并发控制。


这位朋友,你每次看到这种例子都说这个例子不好,能不能说点详细的分析? --------------------编程问答-------------------- 留个脚印。。。 --------------------编程问答--------------------
引用 6 楼 sp1234 的回复:
首先,你这是使用异步的语法,实际上是同步编程。我从来不使用WaitHandle用于服务器监听。msdn上这类使用信号量阻塞来在异步代码中模拟同步控制的例子,每当我看到我就骂一遍。你要么就是用同步的Accept方法,要么就是用真正的异步多线程多并发控制。


哈哈,看到你骂WaitHandle不止一次了,确实B4 MSND的这个例子 --------------------编程问答-------------------- 1万元都不能保证掉包,你知道什么叫做互联网吗?它不是实线,而是微波,你学过物理,学过传播吗? --------------------编程问答-------------------- 到底是怎么解决的,我也是服务端生产错误,当客户端非常规断开后
 try
{
  bytesRead = handler.EndReceive(ar);// Read data from the client socket. 
}
catch (Exception ex)
{
  //抛出异常 远程主机强迫关闭一个现在的连接
}
再也接收不到数据了?? --------------------编程问答-------------------- 制定通讯协议
比如CRC16校验什么的 --------------------编程问答--------------------
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,