简体   繁体   中英

problem with transfering image from java to c# using socket connection

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM