New to UDP Sockets "barebone" need a keen eye!

I have only done UDP/TCP work using the TcpClient and UdpClient in the past, and never really dove down into barebone sockets programming.
In windows 10, these two libraries are no longer part of System.Net.Sockets, so I figured I’d try to get some help with the following code:

My question is this.
Using UdpClient, getting a response from the device that I want to manage is a breese:


            var sendClient = new UdpClient(SEND_PORT);

            var remoteIp = IPAddress.Parse(IP_ADDRESS);
            var remoteHost = new IPEndPoint(remoteIp, RECEIVE_PORT);

            var buffer = new byte[] { 0xff, 0x03, 0x00, 0x00, 0x0E, 0x11, 0xFF };

            sendClient.Connect(remoteHost);
            sendClient.Send(buffer, 7);

            var response = sendClient.Receive(ref remoteHost);
            sendClient.Close();

The above code gives me back the data that I am looking for.

Now, fast forward to sockets in Windows 10, and I am confused. I never reach past the initial ProcessSend() method.
I am also noting that there is nowhere where I can enter both the sending port AND the receiving port.

Could someone help me understand these inner workings of Socket programming, and what should I be doing that the UdpClient class masks away from me?


        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            var remoteIp = IPAddress.Parse(SERVER_IP);
            var remoteHost = new IPEndPoint(remoteIp, SERVER_PORT);

            // Same bytes in VersionRequest that are on the previous example
            var buffer = new byte[7];
            Buffer.BlockCopy(Commands.VersionRequest, 0, buffer, 0, 7);

            SocketAsyncEventArgs clientArgs = new SocketAsyncEventArgs();

            clientArgs.SetBuffer(buffer, 0, 7);            
            clientArgs.UserToken      = socket;
            clientArgs.RemoteEndPoint = remoteHost;
            clientArgs.Completed += OnOperationComplete;

            socket.SendToAsync(clientArgs);
        }

        private void OnOperationComplete(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                return;

            switch (e.LastOperation)
            {
                case SocketAsyncOperation.SendTo:
                    ProcessSend(e);
                    break;

                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;

                case SocketAsyncOperation.ReceiveFrom:
                    ProcessReceive(e);
                    break;
            }       
        }

        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                return;

            var socket = e.UserToken as Socket;
            bool receiveIsPending = socket.ReceiveAsync(e);
            if (receiveIsPending)
                ProcessReceive(e);
        }

        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                throw new SocketException((Int32)e.SocketError);

            var message = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);

            var socket = e.UserToken as Socket;
            socket.Shutdown(SocketShutdown.Send);
                       
        }

What is missing in your code is
socket.Bind(endpoint).

Then simply use the Send and Receive methods offered by the Socket type.
You can use the Begin…/End…Versions to do asynchronous send and receive, where I dant see this usefull for send if you just send a couple of Bytes.
The Begin…/End… methods follows the normal .NET Asynchronous pattern

You can also use the SendTO and ReceiveFrom methods. Withe These you don’t Need bind if I remember correctly.

Thank you! That did it for me - Binding to my port solved it.
Another issue arose (I had to re-set the buffer for receive before receiving in order to get the larger response back, but that was easy to detect.

I haven’t seen the Begin/End methods that you mention anywhere. They may not be part of UAP, or perhaps not yet implemented? :slight_smile:

Maybe the Begin/End async pattern is a bit outdated, and was dropped in favor of async/await in UAP.
So everything theat Returns an Task can be used with await.
If I rememver right These methods usually ending on …Async()
You can also store thge Task result in an local variable and wait for it later in the method.

Here’s my final UdpConnector class, in case anyone needs one.
Exception handling is done outside, of course :slight_smile:


    public class UdpConnector
    {
        private readonly EndPoint _localEndpoit;
        private readonly EndPoint _remoteEndpoint;
        private AutoResetEvent _autoResetEvent;
        private byte[] _lastResult;
        private TimeSpan _timeout;


        /// <summary>
        /// Fired whenever a data recieve operation completes. The eventdata is the byte[] received
        /// </summary>
        public event EventHandler<byte[]> BytesReceived;
        

        /// <summary>
        /// The maximum amount of time to wait for a response. Defaults to 5 seconds
        /// </summary>
        public TimeSpan Timeout
        {
            get { return _timeout; }
            set
            {
                if (_timeout == value)
                    return;

                _timeout = value;
            }
        }


        public UdpConnector(string localIp, int localUdpPort, string remoteIp, int remoteUdpPort)
        {
            _localEndpoit   = CreateEndpoint(localIp, localUdpPort);
            _remoteEndpoint = CreateEndpoint(remoteIp, remoteUdpPort);
            _timeout        = TimeSpan.FromSeconds(5);
        }


        public async Task<byte[]> GetDataAsync(byte[] commandBytes, int expectedRecieveBufferSize)
        {
            return await Task.Run(() =>
            {
                _autoResetEvent          = new AutoResetEvent(false);
                var socket               = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.ReceiveBufferSize = expectedRecieveBufferSize;
                socket.Bind(_localEndpoit);

                var e = new SocketAsyncEventArgs();

                e.RemoteEndPoint         = _remoteEndpoint;
                e.UserToken              = socket;

                e.Completed += OnOperationComplete;
                e.SetBuffer(commandBytes, 0, commandBytes.Length);

                socket.SendToAsync(e);

                _autoResetEvent.WaitOne(TimeSpan.FromSeconds(5));

                return _lastResult;
            });
        }


        #region Private helper methods

        private void OnOperationComplete(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                return;

            switch (e.LastOperation)
            {
                case SocketAsyncOperation.SendTo:
                    ProcessSend(e);
                    break;

                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;

                case SocketAsyncOperation.ReceiveFrom:
                    ProcessReceive(e);
                    break;
            }
        }


        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                return;

            var socket = e.UserToken as Socket;
            var receiveBuffer = new byte[socket.ReceiveBufferSize];
            e.SetBuffer(receiveBuffer, 0, socket.ReceiveBufferSize);

            bool receiveIsPending = socket.ReceiveFromAsync(e);

            if (!receiveIsPending)
                ProcessReceive(e);
        }


        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
                throw new SocketException((Int32)e.SocketError);

            UpdateLastResultWithNewBytes(e);

            AnnounceBytesArrived(e);

            ShutDownAndDisposeSocket(e);

            _autoResetEvent.Set();
        }


        private void UpdateLastResultWithNewBytes(SocketAsyncEventArgs e)
        {
            _lastResult = new byte[e.BytesTransferred];
            Buffer.BlockCopy(e.Buffer, 0, _lastResult, 0, e.BytesTransferred);
        }


        private void AnnounceBytesArrived(SocketAsyncEventArgs e)
        {
            if (BytesReceived != null)
            {

                BytesReceived(this, _lastResult);

                _autoResetEvent.Set();
            }
        }


        private static void ShutDownAndDisposeSocket(SocketAsyncEventArgs e)
        {
            var socket = e.UserToken as Socket;
            socket.Shutdown(SocketShutdown.Both);
            socket.Dispose();
        }


        private static EndPoint CreateEndpoint(string ipAddress, int udpPort)
        {
            var ip = IPAddress.Parse(ipAddress);
            return new IPEndPoint(ip, udpPort);
        }

        #endregion
    }

1 Like