情况很简单,我将.NET服务端Socket异步程序封装为类,并抛出一个事件,在外部调用了此事件,将收到服务端的TCP数据。于是,我在UI线程中(一个WinForm窗体类代码中)实例化此接收类,并订阅事件,实时接收TCP数据,并且将一些数据提取于界面上,现在的问题是:我的数据常常丢包,客户端可能发过来10条数据,但UI线程上只显示3、4条。这是为什么?
废话不多说,先上关键代码:这是我的异步Socket处理,其中SocketHelper封装一个byte[] 和Socket,用于异步间的状态保存。AsyncEventArgs是自定义的事件参数,e.AsyncResult为byte[],可得到一次TCP数据报。    /// <summary>
    /// 消息抵达处理方法委托
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e">信息参数</param>
    public delegate void MessageArrivalHandler(object sender,AsyncEventArgs e);    public class FluxSocketManager
    {        private Socket socket;        /// <summary>
        /// 消息抵达事件
        /// </summary>
        public event MessageArrivalHandler MessageArrivalEvent;        private Socket sendSocket;        #endregion        public void BeginReceive()
        {
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                IPEndPoint end = new IPEndPoint(IPAddress.Any, Int32.Parse(ConfigInfo.GetInstance().DevicePort));
                socket.Bind(end);                socket.Listen(100);
                //开始异步监听
                socket.BeginAccept(new AsyncCallback(Accept), socket);
            }
            catch (Exception ex)
            {            }
        }        void Accept(IAsyncResult ia)
        {
            Socket soc = ia.AsyncState as Socket;            try
            {
                Socket worker = soc.EndAccept(ia);                soc.BeginAccept(new AsyncCallback(Accept), soc);                byte[] buf = new byte[1];
                EntityInfo.SocketHelper helper = new EntityInfo.SocketHelper(buf, worker);                //开始异步接收
                worker.BeginReceive(buf, 0, 1, SocketFlags.None, new AsyncCallback(Receive), helper);
            }
            catch (Exception ex)
            {             }
        }        void Receive(IAsyncResult ia)
        {
            try
            {
                EntityInfo.SocketHelper helper = ia.AsyncState as EntityInfo.SocketHelper;
                Socket worker = helper.s;
                worker.EndReceive(ia);                byte[] buffer = new byte[worker.Available + 1];
                worker.Receive(buffer, 1, buffer.Length - 1, SocketFlags.None);
                buffer[0] = helper.b[0];                this.sendSocket = worker;                //调用事件绑定的方法
                OnMessageArrival(new AsyncEventArgs(buffer));                worker.BeginReceive(helper.b, 0, 1, SocketFlags.None, new AsyncCallback(Receive), helper);
            }
            catch (Exception ex)
            {            }
        }        public void SendBack(byte[] buffer)
        {
            try
            {
                this.sendSocket.Send(buffer);
            }
            catch (Exception ex)
            {            }
        }        private void OnMessageArrival(AsyncEventArgs e)
        {
            if (this.MessageArrivalEvent != null)
            {
                this.MessageArrivalEvent(this, e);
            }
        }
    }至于UI线程就比较简单,在Form.cs中代码为:    public partial class MainForm : Form
    {
        private class FluxSocketManager mySocket = new FluxSocketManager();        public MainForm()
        {
            InitializeComponent();
            mySocket.MessageArrivalEvent += new FluxSocketManager.MessageArrivalEvent(mySocket_MessageArrivalEvent);
            Control.CheckForIllegalCrossThreadCalls = false;
        }        /// <summary>
        /// 当收到客户端的数据时触发此方法,UI线程丢失包的情况就在这里!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void mySocket_MessageArrivalEvent(object sender, AsyncEventArgs e)
        {
            byte[] message = e.AsynsBytes; //e.AsynsBytes即为收到的字节数组            //将收到的message用于处理UI
        }
    }
有一个问题要注意,就是客户端数量较少(一般1个客户端),而且发送频率快,但是事件中的消息处理花费时间比较长,例如保存一个3m大小的图片……
那么为什么UI线程上异步会丢包呢?如何解决这个问题?
深夜发帖,确实因为这个问题困扰已久,请sp1234或者其他高手协助!谢谢!

解决方案 »

  1.   

    http://topic.csdn.net/u/20080623/08/4bbd2475-45f1-42e3-a613-16b094759ade.html
      

  2.   


    谢谢楼上,这个页面的几个Demo我也有看过,但就事论事的说,不太适合我的问题。
      

  3.   

    楼主写的这个问题有点大了,特别是Accpet和Receive里面。1."worker.BeginReceive(buf, 0, 1, SocketFlags.None, new AsyncCallback(Receive), helper);" 你的缓冲区只有一个字节?你想整死服务器吗2.“byte[] buffer = new byte[worker.Available + 1];
                    worker.Receive(buffer, 1, buffer.Length - 1, SocketFlags.None);
                    buffer[0] = helper.b[0];
                 this.sendSocket = worker;
                    //调用事件绑定的方法
                    OnMessageArrival(new AsyncEventArgs(buffer));
                    worker.BeginReceive(helper.b, 0, 1, SocketFlags.None, new AsyncCallback(Receive), helper);”
    这一段代码写的一团糟,什么乱七八糟的,居然还有个worker.Receive,难怪你会发现数据丢失。你发现数据丢失实际上是数据覆盖了
      

  4.   

    可能是因为没有处理边界问题造成的。
    粘包了。LZ这样处理比较巧妙,利用了异步的特性,同时又避免数据量大IO唤醒线程消耗的CPU
      

  5.   


    为什么我这里调用worker.Receive(),会将数据覆盖掉呢?
      

  6.   

    有一点问题:我的UI线程正在努力处理某次数据时(大概需要3秒钟),下次数据已经到达,那么此时,UI线程如何做?是抛弃上次没处理完的数据,直接处理本次吗?
      

  7.   

    因为你有两个,一个worker.BeginReceive,一个worker.Receive,本来你用worker.BeginReceive已经接收到数据了,然后你又用worker.Receive再接一次数据将前面的数据覆盖了,覆盖之后你才启动事件通知UI……
      

  8.   


    其实我说的数据丢失,是指一个很简单的东西:每条数据到来时,UI上必然要新增一个记录,问题是客户端确实发了10条以上的记录,但UI上仅显示了3、4条,所以,我对异步通讯与UI线程上的交互处理一直很疑惑,不知道为什么会产生这样的现象!
    也谢谢你对本贴的关心!
      

  9.   

    你还是没说清楚,比如客户端发了10条“abcd”,UI上面显示的是“abcdabcdabcd”(这里表示收到3条),是这样的吗?
      

  10.   

    http://topic.csdn.net/u/20080623/08/4bbd2475-45f1-42e3-a613-16b094759ade.html
      

  11.   


    因为开始你进入beginRecive的时候已经去TCP自带的缓冲区里取了一次数据,这次取的数据你是放在buff 里的,而你在void Receive(IAsyncResult ia)这个里面又去取了TCP自带的缓冲区里取了一次数据,而你却把第二次取得数据通知给UI了,那你第一次的数据哪去了?你有没有去检查一下你接受到的数据,如果我说的没错的话,那边第一次发的数据你应该永远都收不到
      

  12.   


    不是这样的,每次数据到来时都提取字段写一行ListView,有10条自然能生成10行,现在只有3行。而且,如果是多次数据重叠在一起,那肯定会提取失败,并且在日志中报错,现在不报错。
      

  13.   

    很有可能是因为这个Control.CheckForIllegalCrossThreadCalls = false;
    你不要把它弄成false
    使用委托BeginInvoke 出现这样的错误。需要在接收后就记录日志来查。把问题分开去找。
    而且边界没处理。会出现粘包的现象。一般会先把包的长度发过来,然后根据包的长度来申请bufferworker.Receive(buffer, 1, buffer.Length - 1, SocketFlags.None);
    这个若是buffer大的话,还需要判断是否已经接收完了。否则继续接收。
      

  14.   

    Socket不是很熟,不过从LZ的代码上分析可能是如下情况,请LZ自己测试一下:
    (UDP协议是数据报协议,所以接收数据都是以数据包的形式进行的,理论上不会存在粘包)
    从LZ贴出的代码来看~ 是不是可以解析成下面的过程?开始 Accept
    --- 第一个包 ---
    首先 BeginReceive 1字节
    然后 EndReceive (实际只接收了第一个字节)
    --- 第一个包接收完成 ---
    --- 第二个包 ---
    接着 Receive (从第二个字节起接收)
    --- 第二个包接收完成 ---
    ……如果上述过程成立的话,那么就是LZ的异步接收机制直接丢弃了一半的数据包。
      

  15.   

    异步 socket 的问题,  send() 不一定等于 recv()