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.