3

I am working on a project that uses a python script to send an image taken with a webcam to a c# webserver using sockets. I am currently sending strings to the server from the python client using code like:

info = bytearray("Text to send", "UTF-8")
socket.send(info)

to send data to the c# server and it functions perfectly for text and numbers. I run into an issue when trying to encode the data read from the .bmp into "UTF-8", as trying this returns an error of not being able to encode certain characters into UTF-8.

I was wondering if anyone had any idea of a way to encode this that c# will be able to recognize, or, if there is a better way of trying to implement this process, I am all ears.

A couple of options I have come up with would be to 1 - use something like google drive to save the image to, or an FTP server and then have the c# server retrieve it from there or 2 - create a packet system containing the RGB values and recreating the image from those pixel values on the server.

Thanks for your help.

EDIT: I have tried sending the file this way

 data = bytearray("123456789","UTF-8")
 file = open("image.bmp", "rb")
 data += file.read()
 socket.send(data)

and was able to successfully retreive the string "123456789", but the data after this is garbage. I have also implemented sending the size of the file before sending the data and that size number is retrieved fine, but the img data saves as a black bmp.

Edit 2 :

Here is the server and client code I am using to try and recreate the image using a memory stream. I the client code is using the process mentioned by hcalves.

Client

if __name__ == "__main__":
sock = socket.socket()
sock.connect(("localhost", 50839))

with open("image.bmp", "rb") as fd:
    buf = fd.read(1024)
    while (buf):
        sock.send(buf)
        buf = fd.read(1024)
sock.close()

Server

Socket client = server.AcceptSocket();
NetworkStream stream = new NetworkStream(client);
byte[] imgData = new byte[1024];
MemoryStream memstream = new MemoryStream();
stream.Read(imgData,0, 1024);
int counter = 0;
while (stream.DataAvailable)
{
    memstream.Write(imgData, 0, 1024);
    stream.Read(imgData, 0, 1024);
    counter = counter + 1024;
}

memstream.Seek(0, SeekOrigin.Begin);

using (Stream file = File.OpenWrite("img.bmp"))
{
    byte[] buffer = new byte[8*1024];
    int len;
    while ((len = memstream.Read(buffer, 0, buffer.Length)) > 0)
    {
        file.Write(buffer,0,len);
    }
}
Chris
  • 600
  • 7
  • 15
  • 6
    Why are you trying to encode a binary image into a text format? Get the bytearray representing the image data, then send THAT array. – EricLaw Apr 04 '13 at 19:09
  • 1
    If for some reason you have something against just sending the raw bytes as is, you could base64-encode it. I'm not really sure what you would gain from this, and it would slow the transport down a bit. – lc. Apr 04 '13 at 19:10
  • Will the c# image class be able to interpret base64 byte arrays into images? – Chris Apr 04 '13 at 19:13
  • I have tried reading the data as binary and just sending that data, but I have to send string data before that so the data object is of type bytearray and wanted it to append to it correctly. EDIT: Also have been having trouble getting this binary data interpreted correctly on the c# side, which is why I am trying all this. I orginially was just sending the binary array – Chris Apr 04 '13 at 19:14
  • If you're saying: "I need to make a proper HTTP request, and that means sending text headers before the binary body", then the way to do that is FIRST get the headers as text, convert that to a byte array, send those bytes, THEN send the bytes of the image. But trying to implement HTTP yourself is borderline crazy. Use an existing framework/object. – EricLaw Apr 04 '13 at 19:19
  • Really it is just a serial number corresponding to a deviceID, so that I know which device this image is coming from. If you know of a more efficient way I would love to hear it, this is all a learning process for me. – Chris Apr 04 '13 at 19:22
  • When you say the data is "garbage"-- what EXACTLY is it? (Screenshot from NetMon or Wireshark would be helpful). Have you verified that file.read() does what you're hoping it does? – EricLaw Apr 04 '13 at 19:25
  • The data gets saved as a bmp which displays a black bmp. The api for Python's built in function open says the first character states which mode r - for read and then a b can be appended to the r to read this in binary mode. then the fileobject method read() returns an string array. I was trying to use bytearray to convert this string array into bytes. Are you getting at there is a better way of reading this file in? – Chris Apr 04 '13 at 19:35
  • I was gonna suggest bytes or b64 ... but looks like i was slow to the draw – Joran Beasley Apr 04 '13 at 19:36
  • The moment you go to text, your file is hosed. See http://stackoverflow.com/questions/3943149/reading-and-interpreting-data-from-a-binary-file-in-python for how to load a bytearray from a file without mangling it into text first. – EricLaw Apr 04 '13 at 19:47
  • I know this, that is why I was trying to encode the text from the image data into a bytearray using a certain type of encoding that c# would understand. It understood the UTF-8 encoding, so I was wondering if there was a type of encoding that works with the Image class inside of c#. – Chris Apr 04 '13 at 20:32

1 Answers1

2

You shouldn't need more than this recipe:

import socket

if __name__ == "__main__":
    sock = socket.socket()
    sock.connect(("localhost", 50839))

    with open("data.bin", "rb") as fd:
        buf = fd.read(1024)
        while (buf):
            sock.send(buf)
            buf = fd.read(1024)
    sock.close()

For practical reasons, you can treat str objects (the result of fd.read) as raw data, you don't need any further crazy encoding. Just iterate the file and send over the socket. Test by running this server which just echoes to stdout with python server.py > data2.bin:

import socket
import sys

if __name__ == "__main__":
    sock = socket.socket()
    sock.bind(("localhost", 50839))
    sock.listen(1)

    client, address = sock.accept()

    buf = client.recv(1024)
    while (buf):
        sys.stdout.write(buf)
        buf = client.recv(1024)

    client.close()
    sock.close()

A checksum shows the binary data is sent correctly:

% md5 data.bin data2.bin      
MD5 (data.bin) = 8b3280072275badf3e53a6f7aae0b8be
MD5 (data2.bin) = 8b3280072275badf3e53a6f7aae0b8be

Your C# server should be able to accept this data as is. If it doesn't work, it's because your server is expecting something in particular, not just raw data.

hcalves
  • 2,268
  • 1
  • 21
  • 17
  • I am trying your method of sending the data, I do have to have a number sent right before I send the data, but I took that part out just to get this data sending. I have the c# server that stores the image data into a memorystream right before receiving the next set of 1024 bytes. But I am still getting a format error in the image class, my whole reason for finding a different way of encoding the data. – Chris Apr 04 '13 at 20:36
  • It's impossible to have an answer without access to the server code. It can be expecting anything. – hcalves Apr 04 '13 at 20:43
  • Whats the purpose of "counter" in the C# server code? It's not used anywhere. All in all, the code seems analogous to the Python server I posted, so it's not immediately obvious what's wrong. Maybe there's a bug on the way you write the file? Have you tried writing to stdout instead and testing like I did to confirm the right data is sent over the wire? – hcalves Apr 04 '13 at 22:30