I have a java
program (server) on my raspberry pi and c#
program on my pc (client). I want to continuously transfer images from camera on raspberry to my c#
program. I have found somewhere code how to connect java
and c#
via socket connection. My java code:
public static void Start()
{
new Thread(() -> {
while (true)
{
try
{
System.out.println("waiting for connection");
Socket socket = server.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
System.out.println("got connection");
SetOutput("Hello", os);
String fromClient = GetInput(is);
System.out.println(fromClient);
if(fromClient.equals("Hello"))
{
System.out.println("beginning");
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
while(run)
{
SetOutput("camera:cam1", os);
SetOutput(Base64.getEncoder().encodeToString(MatValue()), os);
}
System.out.println("closed");
SetOutput("-1", os); // close client
fromClient = GetInput(is);
if (fromClient.equals("Closed"))
socket.close();
}
} catch (IOException|ArrayIndexOutOfBoundsException exception) {
exception.printStackTrace();
}
}
}).start();
}
public static byte[] MatValue()
{
Mat mat = new Mat();
camera.read(mat);
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(".png", mat, matOfByte);
byte[] bytes1 = matOfByte.toArray();
return bytes1;
}
private static String GetInput(InputStream is) throws IOException
{
byte[] lenBytes = new byte[4];
is.read(lenBytes, 0, 4);
int len = (((lenBytes[3] & 0xff) << 24) | ((lenBytes[2] & 0xff) << 16) |
((lenBytes[1] & 0xff) << 8) | (lenBytes[0] & 0xff));
byte[] receivedBytes = new byte[len];
is.read(receivedBytes, 0, len);
String received = new String(receivedBytes, 0, len);
return received;
}
private static void SetOutput(String data, OutputStream os) throws IOException
{
byte[] toSendBytes = data.getBytes("ASCII");
int toSendLen = toSendBytes.length;
byte[] toSendLenBytes = new byte[4];
toSendLenBytes[0] = (byte)(toSendLen & 0xff);
toSendLenBytes[1] = (byte)((toSendLen >> 8) & 0xff);
toSendLenBytes[2] = (byte)((toSendLen >> 16) & 0xff);
toSendLenBytes[3] = (byte)((toSendLen >> 24) & 0xff);
os.write(toSendLenBytes);
os.write(toSendBytes, 0, toSendLen);
}
And I should say that I'm using OpenCV
to get an image from camera. Here is c#
code:
internal class Client
{
public string outString = "";
private bool stopThread = false;
private IPEndPoint ipPoint;
private Socket sct;
private Thread thread;
internal Client(IPEndPoint ipPoint)
{
this.ipPoint = ipPoint;
sct = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void StartListening()
{
thread = new Thread(() => { Listening(); });
thread.IsBackground = true;
thread.Start();
}
private void Listening()
{
sct.Connect(ipPoint);
Thread.Sleep(1000);
outString = GetInput(sct);
SetOutput("Hello", sct);
try
{
while (!this.stopThread)
{
outString = GetInput(sct);
string[] subs = outString.Split(':');
switch (subs[0])
{
case "camera":
System.Diagnostics.Debug.WriteLine("camera");
string stringImg = ValidateBase64EncodedString(GetInput(sct));
byte[] data = Convert.FromBase64String(stringImg);
File.WriteAllBytes(Directory.GetCurrentDirectory() + @"\testt.png", data);
break;
}
}
sct.Shutdown(SocketShutdown.Both);
sct.Close();
}
catch (SocketException e)
{
StopListening();
}
}
private static string ValidateBase64EncodedString(string inputText)
{
string stringToValidate = inputText;
stringToValidate = stringToValidate.Replace('-', '+'); // 62nd char of encoding
stringToValidate = stringToValidate.Replace('_', '/'); // 63rd char of encoding
stringToValidate = stringToValidate.Replace("\\", "");
stringToValidate = stringToValidate.Replace("\0", "");
switch (stringToValidate.Length % 4) // Pad with trailing '='s
{
case 0: break; // No pad chars in this case
case 2: stringToValidate += "=="; break; // Two pad chars
case 3: stringToValidate += "="; break; // One pad char
default:
throw new System.Exception(
"Illegal base64url string!");
}
return stringToValidate;
}
private static void SetOutput(string data, Socket clientSocket)
{
int toSendLen = System.Text.Encoding.ASCII.GetByteCount(data);
byte[] toSendBytes = System.Text.Encoding.ASCII.GetBytes(data);
byte[] toSendLenBytes = System.BitConverter.GetBytes(toSendLen);
clientSocket.Send(toSendLenBytes);
clientSocket.Send(toSendBytes);
}
private static string GetInput(Socket clientSocket)
{
byte[] rcvLenBytes = new byte[4];
clientSocket.Receive(rcvLenBytes);
UInt32 rcvLen = BytesToInt(rcvLenBytes);
byte[] rcvBytes = new byte[rcvLen];
clientSocket.Receive(rcvBytes, (int)rcvLen, SocketFlags.None);
String rcv = System.Text.Encoding.ASCII.GetString(rcvBytes);
return rcv;
}
private static UInt32 BytesToInt(byte[] arr)
{
UInt32 wd = ((UInt32)arr[3] << 24) | ((UInt32)arr[2] << 16) | ((UInt32)arr[1] << 8) | (UInt32)arr[0];
return wd;
}
public void StopListening()
{
this.stopThread = true;
thread.Abort();
try
{
sct.Shutdown(SocketShutdown.Both);
sct.Close();
}
catch (Exception e)
{
}
}
}
So I read somewhere - when I want to transfer an image I should encode it into Base64
, so I encode it into Base64
when I transfer it. And about the problem. When I run my programs, they work normally. But when I connect to the java
server from c#
client and they start the connection I get only a part of an image from camera on the client side: .
And after the first iteration of the while loop inside client code I get an Overflow
error here String rcv = System.Text.Encoding.ASCII.GetString(rcvBytes);
.
So the question is - why do client side receives only a part of an image even when I know the size of it (usually it is about 290000 bytes)? Or is there any other ways to transfer an image from java
to c#
? Or maybe problem with OpenCV
on this line Imgcodecs.imencode(".png", mat, matOfByte);
?
Edit 1: I debuged my code a bit and saw a strange thing. When I send and image from java
code, I print byte array size and it gives me 388772
. So when I receive this size from c#
code I get the same number. So I create an byte[]
with this size and then set bytes to this array. Every thing is quite ok on the first iteration. But on the second iteration when java code sends "camera:cam1"
string with size 11
, my c#
code receives a very big number like 11324982
, but it has to be 11
. All this leads me to the thoughts - may be java
sends a lot more bytes than the size of the byte array itself? Or may be c#
receives a lot less bytes than it needs?
I spent several hours trying to understand and fix this problem. As it turned out, the java
server was sending all the correct data, but the c#
client could not receive all the data at once. I have found this helpful questions: File transfer issue in c# and C# - Sockets not receiving all bytes . And the solution to my problem turned out to be a portionwise data sending from the server and portionwise data receiving on the client side. I changed both programs and they are look like:
private static void SetOutput(String data, OutputStream os) throws IOException
{
byte[] toSendBytes = data.getBytes("ASCII");
int toSendLen = toSendBytes.length;
// System.out.println(toSendLen);
byte[] toSendLenBytes = new byte[4];
toSendLenBytes[0] = (byte)(toSendLen & 0xff);
toSendLenBytes[1] = (byte)((toSendLen >> 8) & 0xff);
toSendLenBytes[2] = (byte)((toSendLen >> 16) & 0xff);
toSendLenBytes[3] = (byte)((toSendLen >> 24) & 0xff);
os.write(toSendLenBytes);
if (toSendLen > 10240)
{
for (int i = 0; i < toSendLen; i += 10240)
{
byte[] toSend;
if (i + 10240 > toSendLen)
{
toSend = Arrays.copyOfRange(toSendBytes, i, toSendLen);
os.write(toSend, 0, toSendLen - i);
}
else
{
toSend = Arrays.copyOfRange(toSendBytes, i, i + 10240);
os.write(toSend, 0, 10240);
}
}
}
else
{
os.write(toSendBytes, 0, toSendLen);
}
// os.write(toSendBytes, 0, toSendLen);
}
And c#
:
private static string GetInput(Socket clientSocket)
{
byte[] rcvLenBytes = new byte[4];
clientSocket.Receive(rcvLenBytes);
UInt32 rcvLen = BytesToInt(rcvLenBytes);
// System.Diagnostics.Debug.WriteLine(rcvLen);
byte[] rcvBytes;
if (rcvLen > 10240)
{
byte[] clientData;
List<byte> rcvBytesList = new List<byte>();
int totalBytes = 0;
while (totalBytes < rcvLen)
{
if (rcvLen - totalBytes < 10240)
{
clientData = new byte[rcvLen - totalBytes];
}
else
{
clientData = new byte[10240];
}
int bytesReceived = clientSocket.Receive(clientData);
rcvBytesList.AddRange(clientData);
totalBytes += bytesReceived;
}
rcvBytes = rcvBytesList.ToArray();
}
else
{
rcvBytes = new byte[rcvLen];
clientSocket.Receive(rcvBytes, (int)rcvLen, SocketFlags.None);
}
//System.Diagnostics.Debug.WriteLine(rcvBytes.Count(n => n == '\0'));
String rcv = System.Text.Encoding.ASCII.GetString(rcvBytes);
return rcv;
}
Thanks for trying to help me :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.