0

I am working on a basic networking assignment for a class of mine. The objective is to send an image from one program to another via UDP. Both programs are running on the same PC and can successfully send string messages between each other.

In order to send an image, I convert it from a .bmp into a byte array called ImageBytes, and then break that array down into smaller "packets" and send them to the server application after sending the number of packets to the server application:

public void sendImage(ImageProcessor img)
    {
        int pSize = (Int16.MaxValue / 2);

        int numPackets = img.ImageBytes.Length / pSize;
        int sizeLastPacket = img.ImageBytes.Length % pSize;

        byte[][] packetArr = new byte[numPackets + 1][];

        for (int i = 0; i < numPackets; i++)
        {
            packetArr[i] = new byte[pSize];
        }

        packetArr[numPackets] = new byte[sizeLastPacket];

        for (int i = 0; i < numPackets; i++)
        {
            for (int j = 0; j < pSize; j++)
            {
                packetArr[i][j] = img.ImageBytes[j + pSize * i];
            }
        }

        for (int i = 0; i < sizeLastPacket; i++)
        {
            packetArr[numPackets][i] = img.ImageBytes[numPackets * pSize + i];
        }

        sendDataAsString(numPackets + 1 + "");

        for (int i = 0; i < numPackets; i++)
        {
            sendDataAsBytes(packetArr[i]);
        }

        sendDataAsBytes(packetArr[numPackets]);
    }`

For context, ImageProcessor is a class I made that contains a member for the Image I want to transfer and the byte representation of that image in an array, ImageBytes.

sendDataAsBytes() is a method I wrote that simply calls UdpClient.Send(byte[] dgram, int bytes)

sendDataAsString() is similar, but converts from string to byte[] before sending.

Here is my code for receiving the packets:

public byte[] receiveImage(IPEndPoint sourcePoint)
    {
        List<byte> received = new List<byte>();
        byte[] inBuffer;
        int pCount;`

        int.TryParse(receiveDataAsString(sourcePoint), out pCount);

        Console.WriteLine("Number of packets expected: " + pCount);

        for (int i = 0; i < pCount; i++)
        {
            inBuffer = receiveDataAsBytes(sourcePoint);

            Console.WriteLine("Received packet " + (i + 1) + " of " + pCount);
            Console.WriteLine("Waiting for the next one...");

            foreach (var val in inBuffer)
            {
                received.Add(val);
            }
        }

        return received.ToArray();
    }`

First, I receive the number of packets I'm expecting, and then I call the UdpClient.Receive() function (wrapped in my own simple receiveDataAsBytes()) function in a for loop.

The particular image I'm sending is split into 28 packets, however, my server application only receives 6 packets, at which point it is blocked by the UdpClient.Receive() function as if it is waiting for more data to be sent. No exception is being thrown, and I can't quite figure out why my server application stops receiving at that point.

I tried disabling the Windows firewall to no avail, and have also tested the same code by sending multiple packets of strings. I sent 10 packets of strings and my server application received them all. This problem occurs on both my desktop and my laptop.

Stepping through the sendImage() code shows that all 28 packets are at least being sent out, however, I have no way of telling if they're actually being received by receiveImage() or if something else is the issue.

EDIT: Here is all the code I have for the Client application and Server application, respectively:

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace NetDesignUDPClient
{
    class MyUDPClient
    {
    #region Members    
    public byte[] IpAddr { get; set; }
    public int RecPort { get; set; }
    public int SendPort { get; set; }
    public IPEndPoint RecEndPoint { get; set; }
    public IPEndPoint SendEndPoint { get; set; }
    public UdpClient MyClient { get; set; }
    #endregion

    #region Constructors
    public MyUDPClient()
    {
        IpAddr = new byte[] { 127, 0, 0, 1 };
        RecPort = 1101;
        SendPort = 1100;
        RecEndPoint = new IPEndPoint(new IPAddress(IpAddr), RecPort);
        SendEndPoint = new IPEndPoint(new IPAddress(IpAddr), SendPort);
        MyClient = new UdpClient(RecEndPoint);

        connectToSendPort();
    }
    #endregion

    #region Methods
    public bool connectToSendPort()
    {
        try
        {
            MyClient.Connect(SendEndPoint);
            return true;
        }
        catch (Exception ex)
        {
            // do something
            return false;
        }
    }
    public void sendDataAsString(string usrMsg)
    {
        byte[] sendMsg = Encoding.ASCII.GetBytes(usrMsg);
        sendDataAsBytes(sendMsg);
    }
    public void sendDataAsBytes(byte[] msg)
    {
        MyClient.Send(msg, msg.Length);
    }

    public void sendImage(ImageProcessor img)
    {
        int pSize = (Int16.MaxValue / 2);

        int numPackets = img.ImageBytes.Length / pSize;
        int sizeLastPacket = img.ImageBytes.Length % pSize;

        byte[][] packetArr = new byte[numPackets + 1][];

        for (int i = 0; i < numPackets; i++)
        {
            packetArr[i] = new byte[pSize + 1];
        }

        packetArr[numPackets] = new byte[sizeLastPacket + 1];

        for (int i = 0; i < numPackets; i++)
        {
            packetArr[i][0] = (byte)(i + 1);    // Send packet number

            for (int j = 1; j <= pSize; j++)
            {
                packetArr[i][j] = img.ImageBytes[j + pSize * i];
            }
        }

        for (int i = 0; i < sizeLastPacket; i++)
        {
            packetArr[numPackets][i] = img.ImageBytes[numPackets * pSize + i];
        }

        sendDataAsString(numPackets + 1 + "");

        for (int i = 0; i < numPackets; i++)
        {
            sendDataAsBytes(packetArr[i]);
        }

        sendDataAsBytes(packetArr[numPackets]);
    }

    public string receiveDataAsString(IPEndPoint sourcePoint)
    {
        return Encoding.ASCII.GetString(receiveDataAsBytes(sourcePoint));
    }

    public byte[] receiveDataAsBytes(IPEndPoint sourcePoint)
    {
        return MyClient.Receive(ref sourcePoint);
    }
    #endregion
}
}

Server:

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

namespace NetDesignUDPServer
{
class UDPServer
{
    #region Class members    
    public byte[] IpAddr { get; set; }
    public int RecPort { get; set; }
    public int SendPort { get; set; }
    public IPEndPoint RecEndPoint { get; set; }
    public IPEndPoint SendEndPoint { get; set; }
    public UdpClient MyClient { get; set; }
    #endregion

    #region Constructors
    public UDPServer()
    {
        IpAddr = new byte[] { 127, 0, 0, 1 };
        RecPort = 1100;
        SendPort = 1101;
        RecEndPoint = new IPEndPoint(new IPAddress(IpAddr), RecPort);
        SendEndPoint = new IPEndPoint(new IPAddress(IpAddr), SendPort);
        MyClient = new UdpClient(RecEndPoint);


        connectToSendPort();
    }
    #endregion

    #region Methods
    public bool connectToSendPort()
    {
        try
        {
            MyClient.Connect(SendEndPoint);
            return true;
        }
        catch (Exception ex)
        {
            // do something
            return false;
        }
    }

    public string receiveDataAsString(IPEndPoint sourcePoint)
    {
        return Encoding.ASCII.GetString(receiveDataAsBytes(sourcePoint));
    }

    public byte[] receiveDataAsBytes(IPEndPoint sourcePoint)
    {
        return MyClient.Receive(ref sourcePoint);
    }

    public byte[] receiveImage(IPEndPoint sourcePoint)
    {
        List<byte> received = new List<byte>();
        byte[] inBuffer;
        int pCount;

        int.TryParse(receiveDataAsString(sourcePoint), out pCount);

        Console.WriteLine("Number of packets expected: " + pCount);

        for (int i = 0; i < pCount; i++)
        {
            inBuffer = receiveDataAsBytes(sourcePoint);

            Console.WriteLine("Received packet " + inBuffer[0] + " of " + pCount);
            Console.WriteLine("Waiting for the next one...");

            foreach (var val in inBuffer)
            {
                received.Add(val);
            }
        }

        return received.ToArray();
    }

    public void sendDataAsString(string msg)
    {
        byte[] sendMsg = Encoding.ASCII.GetBytes(msg);
        sendDataAsBytes(sendMsg);
    }

    public void sendDataAsBytes(byte[] msg)
    {
        MyClient.Send(msg, msg.Length);
    }

    public void sendImage(ImageProcessor img)
    {
        img.ConvertImageToBytes();

        byte[] val = new byte[1];

        for (int i = 0; i < img.ImageBytes.Length; i++)
        {
            val[0] = img.ImageBytes[i];
            sendDataAsBytes(val);
        }

        val[0] = (byte)' ';
        sendDataAsBytes(val);
    }
    #endregion
}

}

EDIT2: It turns out my problem was datagrams getting dropped. Only 6 of the expected 28 datagrams were received properly. I found this thread and decided to try adding Thread.Sleep() calls in my for loop. This ended up solving my dropped datagram issue, at the cost of 100 ms delays between sends. I did not experiment with shorter sleeps as speed is not a factor in this assignment.

DCaruso
  • 48
  • 6
  • You need to implement a decent protocol. Mind that UDP is not reliable. Packets _may_ or may not arrive _out of order_ or _not at all_. You have to consider all of those cases. Which means, that you need to make sure, the server is able to detect out-of-order packets and to reorder them, as well as be able to detect missing packets and requery them. – Fildor Feb 03 '20 at 18:30
  • I added the packet number to each packet as it is being sent. When I ran it, the server application output the following: `Server instance created. Listening... Number of packets expected: 28 Received packet 1 of 28 Waiting for the next one... Received packet 2 of 28 Waiting for the next one... Received packet 3 of 28 Waiting for the next one... Received packet 4 of 28 Waiting for the next one... Received packet 8 of 28 Waiting for the next one... Received packet 24 of 28 Waiting for the next one...` Is this an indication of dropped packets? – DCaruso Feb 03 '20 at 18:37
  • Absolutely it is. At least for out-of-order. But if the other ones won't arrive later, they are being dropped somewhere. Which is kind of odd, given both programs run on the same machine. – Fildor Feb 03 '20 at 18:41
  • Could that be caused by a timing issue? As in, are the packets being sent out faster than they can be received by the server application? It consistently receives packet numbers 1, 2, 3, and 4, and then either packet 7 or 8 and 23 or 24. I can upload more of the code if needed to paint a clearer idea of what is going on – DCaruso Feb 03 '20 at 18:43
  • Not that kind of timing. If that were a problem, UDP would be useless in the internet, generally. What the exact problem is, is hard to tell without further details of code. – Fildor Feb 03 '20 at 18:46
  • 1
    I will edit my original post with more of the code – DCaruso Feb 03 '20 at 18:47

0 Answers0