I would like to send a small texture (16x12px) over the network. I am using the Unity 5 Low-Level Networking API.
Connecting works fine. My problem is that I can’t figure out how to send the data.
Before serialization, I convert the sent texture into a base64 string.
How do I correctly send a texture as a base64 between websockets using Unity’s UNET Low-Level API?
Sending results in an error:
SerializationException: serializationStream supports seeking, but its length is 0
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:155)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:136)
networkReceiver.Update () (at Assets/networkReceiver.cs:95)
I have two scripts: sender and receiver.
Sender script:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;
using System;
using System.Xml;
using System.Xml.Serialization;
public class networkSender : MonoBehaviour {
//network variables
int myReliableChannelId;
int socketId;
int socketPort = 8888;
int connectionId;
Camera thisCam; // for screenshot taking
public GameObject textureObjectOriginal;
void Awake ()
{
thisCam = this.GetComponent<Camera> ();
}
void Start () {
Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
//OPEN SOCKET
//initialize on the NetworkTransport class
NetworkTransport.Init();
//For most situations using default config is fine
ConnectionConfig config = new ConnectionConfig();
//keep the channelId so we can use it later for sending/receiving
myReliableChannelId = config.AddChannel(QosType.Unreliable);
//just sets the max number of connections allowed on your soon to exist socket
int maxConnections = 5;
HostTopology topology = new HostTopology(config, maxConnections);
//finally open the socket
socketId = NetworkTransport.AddHost(topology, socketPort);
Debug.Log("Sender SocketId is: " + socketId);
// the AddHost() method returns an int which is the “HostID”
// but screw that name: it is a SocketId.
}
public void Connect()
{
//connects us to another server
byte error;
//the ip for the remote device
//the socketPort of the remote machine
//Connect(int hostId, string address, int port, int exeptionConnectionId, out byte error);
connectionId = NetworkTransport.Connect(socketId, "127.0.0.1", socketPort, 0, out error);
//if there is a problem we get an error out
Debug.Log("Connected to receiver. ConnectionId: " + connectionId);
}
void OnGUI()
{
if (GUI.Button (new Rect (10, 10, 100, 30), "Connect"))
{
Connect() ;
}
if (GUI.Button (new Rect (10, 60, 100, 80), "Send Socket Message"))
{
SendSocketMessage() ;
}
}
//turn a texture into a stream of bytes then send those bytes out our socket connection.
public void SendSocketMessage()
{
//create texture container
Texture2D tex = new Texture2D(16, 12, TextureFormat.RGB24, false);
// Initialize rendertexture and render screenshot of cam
RenderTexture rt = new RenderTexture(16, 12, 24);
thisCam.targetTexture = rt;
thisCam.Render();
RenderTexture.active = rt;
// Read pixels of rendertexture into texture2d container
tex.ReadPixels(new Rect (0,0,16,12), 0,0);
//Clean up
thisCam.targetTexture = null;
RenderTexture.active = null; // to avoid errors
DestroyImmediate(rt);
//prepare texture for sending over network
byte[] imgByteArray = tex.EncodeToJPG ();
//for test: before sending, display texture on another object in the scene
Texture2D receivedTexture = new Texture2D(16, 12, TextureFormat.RGB24, false);
receivedTexture.LoadImage(imgByteArray);
textureObjectOriginal.GetComponent<Renderer>().material.SetTexture("_MainTex", receivedTexture);
//convert byte array to a base64 string
string imgBase64;
imgBase64 = Convert.ToBase64String (imgByteArray);
// //--- Serialize ---
// UnitySerializer srlzr = new UnitySerializer();
// srlzr.Serialize(imgBase64);
// byte[] byteArray = srlzr.ByteArray;
byte[] buffer = new byte[1024]; //this is the max length of our message when it is in byte form
int sizeToSend = 1024; //the size of our buffer
byte error;
//serialize image
Stream stream = new MemoryStream();
stream.Write(buffer, 0, sizeToSend);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize (stream, imgBase64);
//send over network
NetworkTransport.Send(socketId, connectionId, myReliableChannelId, buffer, sizeToSend, out error);
}
}
Receiver script:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;
using System.Collections.Generic;
using System;
using System.Text;
public class networkReceiver : MonoBehaviour {
//network vars
int myReliableChannelId; //use it for sending/receiving
int socketId;
int socketPort = 8000;
int connectionId;
//testobject to apply received texture to
public GameObject textureObject;
void Start ()
{
Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
//OPEN SOCKET
//initialize on the NetworkTransport class
NetworkTransport.Init();
//For most situations using default config is fine
ConnectionConfig config = new ConnectionConfig();
//keep the channelId so we can use it later for sending/receiving
myReliableChannelId = config.AddChannel(QosType.Unreliable);
//just sets the max number of connections allowed on your soon to exist socket
int maxConnections = 4;
HostTopology topology = new HostTopology(config, maxConnections);
//finally open the socket
socketId = NetworkTransport.AddHost(topology, socketPort);
Debug.Log("Receiver SocketId is: " + socketId);
// the AddHost() method returns an int which is the “HostID”
// but screw that name: it is a SocketId.
}
void Update () {
//always check for new messages coming in
//declare all the variables needed for receiving
int recHostId;
int recConnectionId;
int recChannelId;
byte[] recBuffer = new byte[1024];
int bufferSize = 1024;
int dataSize;
byte error;
// recBuffer contains the byte array message that was received
NetworkEventType recNetworkEvent = NetworkTransport.Receive(out recHostId,
out recConnectionId,
out recChannelId,
recBuffer,
bufferSize,
out dataSize,
out error);
switch (recNetworkEvent)
{
case NetworkEventType.Nothing:
break;
case NetworkEventType.ConnectEvent:
Debug.Log("Receiver: incoming connection event received");
break;
case NetworkEventType.DataEvent:
//handle received string
//Stream stream = new MemoryStream(recBuffer);
Stream stream = new MemoryStream();
stream.Read(recBuffer, 0, bufferSize);
//deserialize
BinaryFormatter formatter = new BinaryFormatter();
string receivedImgBase64 = formatter.Deserialize(stream) as string;
//convert received base64 string to bytearray for texture display
byte[] imgByteArray = Encoding.ASCII.GetBytes(receivedImgBase64);
//--- Deserialize ---
//UnitySerializer dsrlzr = new UnitySerializer(receivedImg);
//create texture to display received image
Texture2D receivedTexture = new Texture2D(32, 24, TextureFormat.RGB24, false);
//read received pixels to texture
receivedTexture.LoadImage(imgByteArray);
//apply texture to object
textureObject.GetComponent<Renderer>().material.SetTexture("_MainTex", receivedTexture);
break;
case NetworkEventType.DisconnectEvent:
//display a disconnect message on screen.
Debug.Log("Receiver: disconnected");
break;
}
}//update
}
Something is wrong here, but what?
Thank you & regards,
bm
