C#套接字数据报溢出

时间:2014-02-19 09:26:02

标签: c# sockets udp

我是c#=)的新手

我有一个关于udp socket的问题。 我有一个聊天服务器接收特定结构的数据包(udp数据报)。

为什么程序在套接字缓冲区已满时接收数据?是否所有这些都不应该丢失?可能是数据包碎片?

数据包结构:udp_headers(28字节)| dataIdentifier(4字节)|名称长度(4字节)|消息长度(4字节)|名称(名称长度)|消息(消息长度)

当我发送大于内部缓冲区的数据包时。该程序抛出异常:

ReceiveData错误:数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或者用于接收数据报的缓冲区小于数据报本身

我需要的是在导致此错误之前丢弃此类数据包。有可能吗?

服务器代码:

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.Net.Sockets;
using System.Net;
using System.Collections;

using ChatApplication;

namespace ChatServer
{
    public partial class Server : Form
    {
        #region Private Members

        // Structure to store the client information
        private struct Client
        {
            public EndPoint endPoint;
            public string name;
        }

        // Listing of clients
        private ArrayList clientList;

        // Server socket
        private Socket serverSocket;

        // Data stream
        private byte[] dataStream = new byte[1024];

        // Status delegate
        private delegate void UpdateStatusDelegate(string status);
        private UpdateStatusDelegate updateStatusDelegate = null;

        #endregion

        #region Constructor

        public Server()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        private void Server_Load(object sender, EventArgs e)
        {
            try
            {
                // Initialise the ArrayList of connected clients
                this.clientList = new ArrayList();

                // Initialise the delegate which updates the status
                this.updateStatusDelegate = new UpdateStatusDelegate(this.UpdateStatus);

                // Initialise the socket
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                // Initialise the IPEndPoint for the server and listen on port 30000
                IPEndPoint server = new IPEndPoint(IPAddress.Any, 30000);

                // Associate the socket with this IP address and port
                serverSocket.Bind(server);

                // Initialise the IPEndPoint for the clients
                IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);

                // Initialise the EndPoint for the clients
                EndPoint epSender = (EndPoint)clients;

                // Start listening for incoming data
                serverSocket.BeginReceiveFrom(this.dataStream, 0, 1024, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveData), epSender);

                lblStatus.Text = "Listening";
            }
            catch (Exception ex)
            {
                lblStatus.Text = "Error";
                MessageBox.Show("Load Error: " + ex.Message, "UDP Server", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion

        #region Send And Receive

        public void SendData(IAsyncResult asyncResult)
        {
            try
            {
                serverSocket.EndSend(asyncResult);
            }
            catch (Exception ex)
            {
                MessageBox.Show("SendData Error: " + ex.Message, "UDP Server", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ReceiveData(IAsyncResult asyncResult)
        {
            try
            {
                byte[] data;

                // Initialise a packet object to store the received data
                Packet receivedData = new Packet(this.dataStream);

                // Initialise a packet object to store the data to be sent
                Packet sendData = new Packet();

                // Initialise the IPEndPoint for the clients
                IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);

                // Initialise the EndPoint for the clients
                EndPoint epSender = (EndPoint)clients;

                // Receive all data
                serverSocket.EndReceiveFrom(asyncResult, ref epSender);

                // Start populating the packet to be sent
                sendData.ChatDataIdentifier = receivedData.ChatDataIdentifier;
                sendData.ChatName = receivedData.ChatName;

                switch (receivedData.ChatDataIdentifier)
                {
                    case DataIdentifier.Message:
                        sendData.ChatMessage = string.Format("{0}: {1}", receivedData.ChatName, receivedData.ChatMessage);
                        break;

                    case DataIdentifier.LogIn:
                        // Populate client object
                        Client client = new Client();
                        client.endPoint = epSender;
                        client.name = receivedData.ChatName;

                        // Add client to list
                        this.clientList.Add(client);

                        sendData.ChatMessage = string.Format("-- {0} is online --", receivedData.ChatName);
                        break;

                    case DataIdentifier.LogOut:
                        // Remove current client from list
                        foreach (Client c in this.clientList)
                        {
                            if (c.endPoint.Equals(epSender))
                            {
                                this.clientList.Remove(c);
                                break;
                            }
                        }

                        sendData.ChatMessage = string.Format("-- {0} has gone offline --", receivedData.ChatName);
                        break;
                }

                // Get packet as byte array
                data = sendData.GetDataStream();

                foreach (Client client in this.clientList)
                {
                    if (client.endPoint != epSender || sendData.ChatDataIdentifier != DataIdentifier.LogIn)
                    {
                        // Broadcast to all logged on users
                        serverSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, client.endPoint, new AsyncCallback(this.SendData), client.endPoint);
                    }
                }

                // Listen for more connections again...
                serverSocket.BeginReceiveFrom(this.dataStream, 0, 1024, SocketFlags.None, ref epSender, new AsyncCallback(this.ReceiveData), epSender);

                // Update status through a delegate
                this.Invoke(this.updateStatusDelegate, new object[] { sendData.ChatMessage });
            }
            catch (Exception ex)
            {
                MessageBox.Show("ReceiveData Error: " + ex.Message, "UDP Server", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Other Methods

        private void UpdateStatus(string status)
        {
            rtxtStatus.Text += status + Environment.NewLine;
        }

        #endregion
    }
}

这是udp发送者和reciver在一个。如果你想听,请使用/ s

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace UDPTestClient
{
    class Program
    {
        static void RecvCompleted(object sender, SocketAsyncEventArgs e)
        {
            string Data = Encoding.ASCII.GetString(e.Buffer, e.Offset, e.BytesTransferred);
            e.Dispose();
            ReceiveUdp((Socket)e.UserToken);
            Console.WriteLine(Data);
        }

        static void SendCompleted(object sender, SocketAsyncEventArgs e)
        {
            int i = e.Count;
            e.Dispose();
            Console.WriteLine("{0} bytes send", i);
        }

        static void ReceiveUdp(Socket s)
        {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            //set buffer to 100 .Now it's work fine.
            e.SetBuffer(new byte[100], 0, 100);
            e.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            e.UserToken = s;
            e.Completed += new EventHandler<SocketAsyncEventArgs>(RecvCompleted);
            s.ReceiveFromAsync(e);
        }

        static void SendUdp(Socket s, string Data)
        {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            byte[] buf = Encoding.ASCII.GetBytes(Data);
            e.SetBuffer(buf, 0, buf.Length);
            e.RemoteEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3333);
            e.UserToken = s;
            e.Completed += new EventHandler<SocketAsyncEventArgs>(SendCompleted);
            s.SendToAsync(e);
        }

        static void Main(string[] args)
        {
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            if (args.Length != 0 && args[0] == "/s")
            {
                IPEndPoint ip = new IPEndPoint(IPAddress.Any, 3333);
                s.Bind(ip);
                ReceiveUdp(s);
            }
            else
            {
                SendUdp(s, "Hello!");
            }
            Console.ReadKey();
            s.Close();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

UDP是一种愚蠢的协议,因为它实际上并不知道大小或如何使用它。如果要在发生异常之前处理此问题,则需要为通信创建不同的结构。我建议您执行以下操作之一:

  1. 使用轮询机制,客户端从服务器请求具有给定大小的单个更新,然后确保缓冲区足够大以容纳此消息。
  2. 只需将缓冲区增加到正确的大小即可。
  3. 抓住异常并简单地丢弃它,当缓冲区溢出时,然后尝试重新建立。
  4. 使用RecieveAsync一次只能获取部分数据报。看到 http://msdn.microsoft.com/en-us/library/dxkwh6zw(v=vs.110).aspx