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

C# Socket 服务端接受数据偶尔丢失

C# Socket 发送消息和接受消息。
开始只是发送文字,定义100*1024字节数组,一次性发送不会丢失,现在需求变了,要发送的有图片,如果内容过多会出现 "一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小"
现在我分批发送 在后一次发送的时候标识一下.

我测试 10次基本有5次接不到信息,原因是客户端发送10或者100次,而服务段老是接受4次,如果发消息和图片发送成功了, 服务端接受的次数和客服端发送的次数一样.什么情况会出现这样问题呢?

我在虚拟机里面断电调试服务端,在自己的电脑调试客户端。  Sock --------------------编程问答-------------------- 一共4个方法,对应代码如下:
实现类
 /// <summary>
    /// 消息传输
    /// </summary>
    public class TransferMessage
    {

        public TransferMessage()
        {

        }
        /// <summary>
        /// 给某具体一个IP发送
        /// </summary>
        /// <param name="info">发送消息内容</param>
        /// <param name="remoteIp">要发送的IP</param>
        public static void Send(MessageCell info, IPEndPoint remoteIP)
        {
            //只能用UDP协议发送广播,所以ProtocolType设置为UDP
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //将发送内容转换为字节数组
            byte[] bytes = SerializeHelper.ObjToByte(info);
            //向子网发送信息
            socket.SendTo(bytes, remoteIP);
            socket.Close();
        }

        /// <summary>
        /// 接受消息
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static MessageCell Receive(Socket s)
        {
            byte[] bytes = new byte[GlobalInfo.PacketSize * 10];
            EndPoint remoteIP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
            // s.ReceiveFrom(bytes, ref remoteIP);
            s.Receive(bytes, 0, bytes.Length, SocketFlags.None);
            MessageCell cell = SerializeHelper.BytesToObject<MessageCell>(bytes);
            return cell;
        } 
    }


客户端发送:

 /// <summary>
        /// 给某具体一个IP发送
        /// </summary>
        /// <param name="info">发送消息内容</param>
        /// <param name="remoteIp">要发送的IP</param>
        public static void SendMeaasge(MessageInfo info, string remoteIp)
        {
            //只能用UDP协议发送广播,所以ProtocolType设置为UDP
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //让其自动提供子网中的IP地址
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(remoteIp), GlobalInfo.Port);
            //socket.Connect(iep);

            //将发送内容转换为字节数组
            byte[] bytes = SerializeHelper.ObjToByte(info); //自己改写

            MemoryStream ms = new MemoryStream();
            ms.Write(bytes, 0, bytes.Length);
            int PacketSize = GlobalInfo.PacketSize;
            int PacketCount = bytes.Length / PacketSize;
            //最后一个包的大小   
            int LastDataPacket = (int)(ms.Length - ((long)(PacketSize * PacketCount)));
            //数据包   
            byte[] data = new byte[PacketSize];
            int totalCell = PacketCount;
            if (LastDataPacket != 0)
            {
                totalCell++;
            }

            //开始循环发送数据包   
            for (int i = 0; i < PacketCount; i++)
            {
                ms.Position = i * PacketSize;
                //从文件流读取数据并填充数据包   
                ms.Read(data, 0, data.Length);
                MessageCell cell = new MessageCell(i, data);
                cell.TotalCellCount = totalCell;
                cell.IsLastMessage = (i == PacketCount - 1 && LastDataPacket == 0);
                TransferMessage.Send(cell, iep);
            }

            //如果还有多余的数据包,则应该发送完毕!   
            if (LastDataPacket != 0)
            {
                if (PacketCount > 0)
                {
                    ms.Position = PacketSize * PacketCount;
                }
                else
                {
                    ms.Position = 0;
                }
                data = new byte[LastDataPacket];
                ms.Read(data, 0, data.Length);
                MessageCell cell = new MessageCell(PacketCount, data);
                cell.TotalCellCount = totalCell;
                cell.IsLastMessage = true;
                TransferMessage.Send(cell, iep);
            }
            socket.Close();
        }

服务端接受:
  private void ReceiveMessage3()
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, GlobalInfo.Port);
            try
            {
                socket.Bind(iep);
                EndPoint ep = (EndPoint)iep;
                MemoryStream ms = new MemoryStream();
                int TotalCellCount = 0;
                while (true)
                {
                    MessageCell cell = TransferMessage.Receive(socket);
                    TotalCellCount++;
                    if (cell.IsLastMessage)
                    {
                        ms.Position = cell.Index * cell.PacketSize;
                        ms.Write(cell.Data, 0, cell.Data.Length);
                        AcceptMessage(ms.GetBuffer());
                        ms.Flush();
                        ms.Close();
                        ms = new MemoryStream();
                    }
                    else
                    {
                        ms.Position = cell.Index * cell.Data.Length;
                        ms.Write(cell.Data, 0, cell.Data.Length);
                    }

                    //if (cell.TotalCellCount != TotalCellCount)
                    //{
                    //    MessageBox.Show(string.Format("包有丢失,包的总个数是{0},当前最后的包{1}",
                    //        cell.TotalCellCount, cell.Index));
                    //}
                }
            }

            catch (Exception ex)
            {
                if (ex.Message.Contains("通常每个套接字地址(协议/网络地址/端口)只允许使用一次"))
                {
                    DialogResult result = MessageBox.Show("端口被占用了", "是否关闭之前程序",
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (result == DialogResult.Yes)
                    {
                        SystemTool.KillSamePort();
                        // AcceptMessage();
                    }
                }
                else
                {
                    MessageBox.Show(ex.Message + "错误方法:" + ex.StackTrace);
                }
            }
            socket.Close();
        }
    }

--------------------编程问答-------------------- 用的UDP还是TCP??

--------------------编程问答-------------------- 看了下,,,你用的UDP协议,UDP丢包正常现象,,,,
如果数据量不大,改用TCP吧.

如果不能改协议,在接收端做一个验证. --------------------编程问答-------------------- udp丢包也不至于丢包率这么高吧 --------------------编程问答-------------------- Udp最好要这样做

定义一个发送列表
发送第一个->对方接收->收到已收到回馈包->再发送第二个->。。。。这样一有个交互过程。 --------------------编程问答--------------------
引用 4 楼 juyangjia 的回复:
udp丢包也不至于丢包率这么高吧

丢50%不算什么。 --------------------编程问答--------------------
引用 5 楼 wyd1520 的回复:
Udp最好要这样做

定义一个发送列表
发送第一个->对方接收->收到已收到回馈包->再发送第二个->。。。。这样一有个交互过程。

显然,这里缺少“重发”。而是发呆在那里干等。

实际上,这种UDP很可能并不比TCP快。 --------------------编程问答-------------------- 还不知道怎么解决的好,关键的不是每一次丢失,有时候发送很正常,有时候不正常,找不到关系。 --------------------编程问答-------------------- 为什么不用tcp,如果你执意用udp又不想验包只能在每次发送后  TransferMessage.Send(cell, iep);
作一短暂休息,时间越长越好最短随你网络环境定。效率可想是多么低。还有udp(在多线程并发时更严重)要注意你分的包不一定依次按你发的次序收到 --------------------编程问答--------------------
引用 8 楼 qq183670101 的回复:
还不知道怎么解决的好,关键的不是每一次丢失,有时候发送很正常,有时候不正常,找不到关系。

我没有看你的代码。

虽然完全可能是你的代码造成了一些错误,但是我看到你认为udp不会丢包,也就懒得看你的代码了。就这块通讯编程而言,如果你计较丢包,那么你就应该先使用tcp。既然你应该使用tcp才能改变你的概念,那么我也就懒得看你的代码到底有什么问题了。 --------------------编程问答-------------------- 我还是看了一下你的“服务端接受”的那几行,不用仔细看,它的概念问题就很严重了。udp接收哪里可能再“分包”呢?根本不保证udp消息消息接收次序,那么根本不会去存在什么 TotalCellCount、IsLastMessage 这种概念。 --------------------编程问答--------------------
引用 11 楼 sp1234 的回复:
我还是看了一下你的“服务端接受”的那几行,不用仔细看,它的概念问题就很严重了。udp接收哪里可能再“分包”呢?根本不保证udp消息消息接收次序,那么根本不会去存在什么 TotalCellCount、IsLastMessage 这种概念。


我是把要发送的消息对象MessageInfo变成byte[] 由于这个很大 ,所以我分开发送, 分开发送的时候 我又构建一个对象MessageCell 对象里面包括TotalCellCount、IsLastMessage。
在接收端,分别 反发序列化成MessageCell对象,然后根据MessageCell里面的数据byte[]构建成原始的btye[] ,然后在反序列化成真正的MessageInfo。 --------------------编程问答-------------------- 另外不管什么方式 ,传输的时候能保证数据包不丢失就行。

我看了下丢的数据 没有接受到5,6.结果反序列化报错。
比如我在客户端发生100次,在服务端能接受到100次, 因为我不清楚TCP和Udp的特点,只知道Upd传输速度快,就选择了, 总之 只要能保证数据不丢失就行,不然反序列化就会报异常。什么传输方式无所谓,就怕换成TCP也出现这样的问题。 


--------------------编程问答-------------------- 现在 还真不是偶尔的了 , 10次居然能达到8次, 有时候10次一次,与网络路由器还有关系。  --------------------编程问答-------------------- 没有太仔细看代码(眼睛疼),但是看你给别人的留言了。
你是把一次发送的较大数据分组发送出去,然后接收的时候再组合好。那么这里就有问题里,你怎么分组,你怎又么组合?比如你一幅图片是 AA BB CC DD EE FF,你分成AA BB CC 某某标志位,DD EE FF 某某标志位,那么当连续发送这些图片时,你服务器接收时怎么组合呢,你接收上来的包可以无序的?别告诉我去找头标志位和尾标位置然后去拼接成一个整图,那你如何去找同一幅图片的头包和尾包呢?如果你真的是问题出在这里,个人建议标志位要设定的唯一且有规律,比如CRC校验,每一副图片的CRC一定不同(相同你就中奖了),然后把CRC在放头包和尾包,这样就找到了。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,