I am currently working on a task to transfer a large byte array with wcf.
I tried following code and it works:
On the server side:
[OperationContract]
public System.IO.Stream GetData()
{
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
return fs;
}
On the client side:
public void GetFile()
{
IByteService byteService = channelFactory.CreateChannel();
Stream serverStream = byteService.GetData();
byte[] buffer = new byte[2048];
int bytesRead;
FileStream fs = new FileStream(filePath, FileMode.CreateNew);
while ((bytesRead = serverStream.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, bytesRead);
}
}
But the other way around it does not work:
On the server side:
[OperationContract]
public void WriteToStream(Stream clientStream)
{
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
fs.CopyTo(clientStream);
}
On the client side:
public void SendStream()
{
FileStream bytesReceivedFromTheServerAsFile = new FileStream(filePath,FileMode.OpenOrCreate);
IByteService byteService = channelFactory.CreateChannel();
byteService.WriteToStream(bytesReceivedFromTheServerAsFile);
}
fs.CopyTo(stream) throws NotSupportedException on the server side.
Would like to know why?
I would like to do the download other way around because on the server side I do not have a stream. I will receive bytes from a third party lib. So i was thinking to do it like this:
On the server side:
[OperationContract]
public void WriteToStream(Stream clientStream)
{
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead = 100;
while ((ThirdPartyClass.GetNextBytes(buffer))
{
clientStream.Write(buffer, 0, bytesRead);
}
}
On the client side:
public void SendStream()
{
FileStream bytesReceivedFromTheServerAsFile = new FileStream(filePath, FileMode.OpenOrCreate);
IByteService byteService = channelFactory.CreateChannel();
byteService.WriteToStream(bytesReceivedFromTheServerAsFile);
}
Because i dont have a stream on the server side. I was thinking the other way around to push the data to the client will be a nice approach.
However, other soulution concepts would be great.
WCF does not support pushing stream data like that. Remember that client and server are in separate memory spaces. The server can never access objects that the client has. When you return a stream WCF constructs the illusion for the client that the stream the client reads from is what the server returned. WCF cannot do this the other way around.
Write yourself a custom stream that reads from the 3rd party class. Return that stream from the server.
Related
Currently I'm trying to develop a multiplayer game with Unity. I checked the Unity's built-in Network and NetworkManager but I need the server dedicated. Hence I developed a java server and implements the protolbuf which is provided by Google. It works with my cocos project but not with this Unity one.
Two problems, first, no matter what did I send to server, the server side has a InvalidProtolBufferException :
While parsing a protocol message, the input ended unexpectedly in the middle > of a field. This could mean either than the input has been truncated or that > embedded message misreported its own length.
second, the stream.read method always makes my unity not responding. Here is my client-side code:
public class SocketClient : MonoBehaviour {
public Text send;
public Text read;
const string ipAddress = "192.168.0.233";
const int port = 8080;
TcpClient socket;
NetworkStream stream;
BinaryWriter bw;
BinaryReader br;
// Use this for initialization
void Start () {
SetupSocket();
}
void SetupSocket() {
socket = new TcpClient(ipAddress, port);
stream = socket.GetStream();
bw = new BinaryWriter(socket.GetStream());
br = new BinaryReader(socket.GetStream());
}
// Update is called once per frame
void Update () {
ReadMessage();
}
public void SendMessage() {
//NetworkStream stream = new NetworkStream(socket.Client);
MessagesProto msg = new MessagesProto();
msg.id = int.Parse(send.text);
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<MessagesProto>(ms, msg);
ms.Position = 0;
byte[] data = ms.ToArray();
ms.Position = 0;
//foreach (byte d in data) {
// Debug.Log(d);
//}
stream.Write(data, 0 , data.Length);
}
}
public void ReadMessage() {
if (stream.CanRead) {
//byte[] receiveData = new byte[socket.ReceiveBufferSize];
byte[] receiveData = new byte[4];
socket.GetStream().Read(receiveData, 0, 4);
Debug.Log("Loading...");
using (MemoryStream ms = new MemoryStream()) {
ms.Write(receiveData, 0, receiveData.Length);
ms.Position = 0;
var msg = Serializer.Deserialize<MessagesProto>(ms);
read.text = msg.data + "";
}
}
}
}
I tried to set the stream.CanRead to stream.DataAvailable, no more crash but not reading anything either, these .Net sockets problems drive me crazy, anyone can help me with this please?
It is not responding because your socket code is synchronous(blocking). You can solve this by either using asynchronous version of the socket functions or use Thread to do all your receiving stuff. This has been answered many times and I will just link to the answers.
TCP Server in Unity
Just port the code to Client.
UDP.
I'm trying to pass a custom class from one tcp client to another (using TcpListener, TcpClient and XmlSerializer).
I got 1 problem that breaks into 2 problems.
when i'm using this code, the client hangs while Serilization is being done
(i think the other side doesnt know when the data is fully sent):
public void SendData(myMessage messageClass)
{
NetworkStream serverStream = tcpServer.GetStream();
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
xmlSrlz.Serialize(serverStream, messageClass);
serverStream.Flush();
}
public myMessage ReadData()
{
while (true)
{
NetworkStream clientStream = client.GetStream();
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
int bytesToread;
byte[] inStream = new byte[10025];
myMessage msg;
do
{
msg = (myMessage)xmlSrlz.Deserialize(clientStream);
clientStream.Flush();
}
while (((bytesToread = clientStream.Read(inStream, 0, (int)client.ReceiveBufferSize)) > 0));
return msg;
}
}
So, when i searched around i noticed i can dispose the stream (or use using) and that way let the other side know that the Data has fully sent and theres no more Data to send:
public void SendData(myMessage messageClass)
{
using (NetworkStream serverStream = tcpServer.GetStream())
{
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
xmlSrlz.Serialize(serverStream, messageClass);
serverStream.Flush();
}
}
public myMessage ReadData()
{
while (true)
{
using (NetworkStream clientStream = client.GetStream())
{
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
int bytesToread;
byte[] inStream = new byte[10025];
myMessage msg;
do
{
msg = (myMessage)xmlSrlz.Deserialize(clientStream);
clientStream.Flush();
}
while (((bytesToread = clientStream.Read(inStream, 0, (int)client.ReceiveBufferSize)) > 0));
return msg;
}
}
}
But now i lost the connection of them 2 clients.
when i'm trying to send another Message i get the exception:
The operation is not allowed on non-connected sockets.
So how can i send the Serilized object without breaking the connection?
First off, using a using like that is closing your socket by disposing the underlying stream. Bad idea.
Second, consider serializing to and from a string, instead of serializing directly into the stream. I'm not sure if there are side-effects of doing that, but there certainly could be.
You can use this question to serialize/deserialize from string: Deserialize from string instead TextReader
My company run an application that have to archive many kinds of files into some distants servers. The application works well but can't handle files larger than 1GB.
Here is the current function use to load the files to be uploaded to the distant server :
FileStream fs = File.OpenRead(fileToUploadPath);
byte[] fileArray = new byte[fs.Length];
fs.Read(fileArray, 0, fs.Length);
The byte array (when loaded successfully) was then splited into 100Mb bytes arrays and sent to the local server (using some WSDL web services) with the following function :
localServerWebService.SendData(subFileArray, filename);
I changed the function responsible for the file reading to use BufferendStream and I also wanted to improve the Webservice part so that it doesn't have to create a new stream at each call. I thought of somethings like this :
FileInfo source = new FileInfo(fileName);
using (FileStream reader = File.OpenRead(fileName))
{
using (FileStream distantWriter = localServerWebService.CreateWriteFileStream(fileName))
{
using (BufferedStream buffReader = new BufferedStream(reader))
{
using (BufferedStream buffWriter = new BufferedStream(distantWriter))
{
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = 0;
long bytesToRead = source.Length;
while (bytesToRead > 0)
{
int nbBytesRead = buffReader.Read(buffer, 0, buffer.Length);
buffWriter.Write(buffer, 0, nbBytesRead);
bytesRead += nbBytesRead;
bytesToRead -= nbBytesRead;
}
}
}
}
}
But this code can't compile and always give me the error Cannot convert MyNameSpace.FileStream into System.IO.FileStream at line using (FileStream distantWriter = localServerWebService.CreateWriteFileStream(fileName)). I can't cast MyNameSpace.FileStream into System.IO.FileStream either.
The web service method :
[WebMethod]
public FileStream CreateWriteFileStream(String fileName)
{
String RepVaultUP =
System.Configuration.ConfigurationSettings.AppSettings.Get("SAS_Upload");
String desFile = Path.Combine(RepVaultUP, fileName);
return File.Open(desFile, FileMode.Create, FileAccess.Write);
}
So can you guys please explain to me why is this not working?
P.S.: English is not my mothertong so I hope what i wrote is clearly undestandable.
I've made a simple proxy server. Works fine, but not the gzip decompressing:
The magic number is not correct. Be sure .
No error, but the gzipstream won't decompress.
It works as follows:
private void HandleTraffic()
{
while ((bytesRead = realServer.Receive(buffer, 0, BODYBUFFERSIZE, SocketFlags.None)) > 0)
{
decompressedBuffer = new byte[BODYBUFFERSIZE];
if (t == "gzip")
{
MemoryStream outputStream = new MemoryStream(), inputStream = new MemoryStream();
inputStream.Write(buffer, 0, bytesRead);
inputStream.Position = 0;
inputStream.Flush();
using (GZipStream gz = new GZipStream(inputStream, CompressionMode.Decompress))
{
gz.CopyTo(outputStream);
}
decompressedBuffer = outputStream.ToArray();
MessageBox.Show(ASCIIEncoding.ASCII.GetString(decompressedBuffer));
}
}
}
InputStream is filled, but outputStream isn't decompressed or gives me the error 'The magic number is not correct'.
In this job, sockets has been required.
You don't need to process the data in a proxy server in any way whatsoever, unzipping, deflating, whatsoever. Just copy the bytes. The only data you need to even look at is the first line from the client, the HTTP CONNECT command.
The solution to this for myself was to turn off Fiddler.
I am wanting to write a WCF web service that can send files over the wire to the client. So I have one setup that sends a Stream response. Here is my code on the client:
private void button1_Click(object sender, EventArgs e)
{
string filename = System.Environment.CurrentDirectory + "\\Picture.jpg";
if (File.Exists(filename))
File.Delete(filename);
StreamServiceClient client = new StreamServiceClient();
int length = 256;
byte[] buffer = new byte[length];
FileStream sink = new FileStream(filename, FileMode.CreateNew, FileAccess.Write);
Stream source = client.GetData();
int bytesRead;
while ((bytesRead = source.Read(buffer,0,length))> 0)
{
sink.Write(buffer,0,length);
}
source.Close();
sink.Close();
MessageBox.Show("All done");
}
Everything processes fine with no errors or exceptions. The problem is that the .jpg file that is getting transferred is reported as being "corrupted or too large" when I open it.
What am I doing wrong?
On the server side, here is the method that is sending the file.
public Stream GetData()
{
string filename = Environment.CurrentDirectory+"\\Chrysanthemum.jpg";
FileStream myfile = File.OpenRead(filename);
return myfile;
}
I have the server configured with basicHttp binding with Transfermode.StreamedResponse.
I think the problem is this:
while ((bytesRead = source.Read(buffer,0,length))> 0)
{
sink.Write(buffer,0,length);
}
Imagine you're reading the last bit of your file - maybe it's not 256 bytes anymore, but only 10.
You read those last 10 bytes, bytesRead will be 10, but on the sink.Write operation, you're still using the fixed value length (256). So when you read the last block of data, you're writing out a block that might be too large.
You need to change the line for the sink to:
sink.Write(buffer, 0, bytesRead);
and then it should work.