c# TCP NetworkStream is not receiving data - c#

I'm trying to send files after sending their's information via TCP connection. At the end of receiver host, the data packets are received. I used Wireshark to confirm it. However the data couldn't be received in NetworkStream.
public class FileTransporter
{
public void ReceiveFiles(IPAddress IP, int port)
{
TcpListener tcpListener = new TcpListener(IP, port);
tcpListener.Start();
using (TcpClient tcpClient = tcpListener.AcceptTcpClient())
{
if (tcpClient.Connected)
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
int pointer = 0;
byte[] fileNameLengthBytes = new byte[sizeof(int)];
networkStream.Read(fileNameLengthBytes, pointer, fileNameLengthBytes.Length);
int fileNameLength = BitConverter.ToInt32(fileNameLengthBytes, pointer);
// code to read fileName and it's size
networkStream.Close();
}
}
tcpClient.Close();
}
tcpListener.Stop();
}
public void SendFiles(IPAddress IP, int port, string[] paths)
{
for(int i=0; i<paths.Length; i++)
{
FilePackage filePackage = new FilePackage(paths[i]);
byte[] infoBytes = filePackage.EncodeInfoToByte();
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.Connect(IP, port);
using (NetworkStream networkStream = tcpClient.GetStream())
{
networkStream.Write(infoBytes, 0, infoBytes.Length);
networkStream.Close();
}
tcpClient.Close();
}
}
}
}
public class FilePackage
{
public FilePackage(string fileName)
{
this.Info = new FileInfo(fileName);
}
public byte[] EncodeInfoToByte()
{
List<byte> infoByte = new List<byte>();
infoByte.AddRange(BitConverter.GetBytes(this.Info.Name.Length));
infoByte.AddRange(Encoding.UTF8.GetBytes(this.Info.Name));
infoByte.AddRange(BitConverter.GetBytes(this.Info.Length));
return infoByte.ToArray();
}

I am going to Assume that ReadFiles is invoked somehow / somewhere else.
I have a very similar implementation, however my "Read" of the stream is much smaller:
// Read the first batch of the TcpServer response bytes.
var bytes = new byte[512];
// Loop to receive all the data sent by the Server.
var sb = new StringBuilder();
do
{
var i = stream.Read(bytes, 0, bytes.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(bytes, 0, i));
} while (stream.DataAvailable);
Perhaps using "stream.DataAvailable" will allow you to test for data existence and that way you can make sure that you only process when data is available.
Also I don't see where "pointer" is being initialized in your code.
If it is not set to 0 each time, you will not be processing from the beginning of the data packet.

Your problem is probably being caused by the nagle algorithm, prevent small amounts of data being sent to reduce congestion. You could try disabling it by setting the following properties on your tcpClient
tcpClient.NoDelay = true;
tcpClient.Client.NoDelay = true

Related

C# - Sockets not receiving all bytes

I have been struggling to fix my mistake. My client/server seems to work fine when on the same machine. When the code is executed on a different PC, for some reason only part of the bytes are reaching the server. I cannot seem to get the bytes to go through to matter how much I try. It seems the cutoff point is 367 bytes and it does not want to transfer anymore. If anyone knows what I am doing wrong, a solution would be much appreciated.
Thanks in advance!
Server:
using System;
using System.Data.SQLite;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
namespace DMAssist
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
// Current URL string
}
public class asyncserver
{
public asyncserver()
{
}
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("Local IP Address Not Found!");
}
public void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 25599);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public byte[] ObjectToByteArray(object obj)
{
if (obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
private Object ByteArrayToObject(byte[] arrBytes)
{
MemoryStream memStream = new MemoryStream();
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
Object obj = (Object)binForm.Deserialize(memStream);
return obj;
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
String connectedAddress = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
connectedAddress = remoteIpEndPoint.Address.ToString();
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
Console.WriteLine("Bytes read: " + bytesRead);
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
object rawbytes = ByteArrayToObject(state.buffer);
netObject recvobject = (netObject)rawbytes;
Send(handler, (object)recvobject);
}
}
private void Send(Socket handler, object data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = ObjectToByteArray(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
Client:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections;
namespace DMAssist
{
public class asyncsclient {
public asyncsclient()
{
}
public byte[] ObjectToByteArray(object obj)
{
if (obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
private Object ByteArrayToObject(byte[] arrBytes)
{
MemoryStream memStream = new MemoryStream();
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
Object obj = (Object)binForm.Deserialize(memStream);
return obj;
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
static string GetString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
public object sendObject(object outgoingObject, bool expectRecieve)
{
try
{
Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("x.x.x.x");
IPEndPoint remoteEP = new IPEndPoint(ip, 25599);
soc.Connect(remoteEP);
byte[] byData = ObjectToByteArray(outgoingObject);
//Console.WriteLine("Array length:" + byData.Length);
soc.Send(byData);
if (expectRecieve)
{
byte[] buffer = new byte[1024];
int iRx = soc.Receive(buffer);
object rawbytes = ByteArrayToObject(buffer);
netObject recvobject = (netObject)rawbytes;
Console.WriteLine("Writing stopped");
soc.Close();
soc.Dispose();
return recvobject;
}
else
{
soc.Close();
soc.Dispose();
return "";
}
}
catch (Exception)
{
MessageBox.Show("Server unreachable. Make sure server is broadcasting on port 25599 successfully.", "DMAssist Error 0xC1");
}
return null;
}
}
}
There is a big difference between UDP sockets and TCP sockets.
What you are trying would work better with UDP because this protocol is more packet oriented. This means that if a server sends a certain number of bytes, the client 'might' receive the same certain number of bytes or receive nothing at all.
For TCP it is different because it is a streaming protocol which means bytes are just sent sequentially anyway the server sees fit. Usually it will try to fill up to the mtu and when you receive you will very likely receive the data in multiple chunks that you have to append yourself. TCP is a also connection oriented and reliable. Bytes are acked from the client to the server and vice versa, and retransmitted if an ack is not received in due time. This means even that packets can at the TCP level be received out of order. TCP will fit them together. You at the application level will receive everything in order.
The advice you got so far is correct, you have to keep calling receive. And this until there is nothing to receive anymore. And how do you know that there is nothing to receive anymore ? Usually that is detected by receiving a TCP message in receive with 0 length. This means that the peer has closed its end of the socket and will not send anymore. This in turn means that you can close your end of the socket.
This is by design. And it's a very common mistake developers make with regards to sockets.
To receive data correctly, you need to keep receiving data in a loop until you receive the number of bytes you need. Keep appending to your receive buffer until you get the number of bytes you need.
Some socket implementations have a the concept of a "wait for all" flag (e.g. MSG_WAITALL), but even that will return partial data if the remote closed the connection after sending.
Code defensively. Code as if the sender is only going to send 1 byte at a time. You'll be in good shape.
As you mentioned in your code, There might be more data, so you store the data received so far. But you don't have the code get the rest of the data.
MSDN has an example on how to do this correctly:
https://msdn.microsoft.com/en-us/library/bew39x2a.aspx?f=255&MSPPError=-2147217396
From winsock2.h:
#define MSG_WAITALL 0x8 /* do not complete until packet is completely filled */
You can add this option to your .net stream socket receive like this:
socket.Receive(buffer, (SocketFlags)0x8)
This should preserve message boundaries much like you're accustomed with dgram sockets.

How to make sure that stream is read completely?

I am using Network Stream, TcpListener and Sockets.
I want to make sure that all the data from sender is received by receiver.
I have below code for receiver
private void StartReciever()
{
util.LoadSettings();
string tcpIpAddress = util.svrSettings["IpAddress"];
string port = util.svrSettings["Port"];
string outDir = util.svrSettings["isOutput"];
new Thread(
() =>
{
if (!File.Exists(util.settingFile))
Logger("Please setup the services first.");
else
{
try
{
IPAddress ipAddress = IPAddress.Parse(tcpIpAddress);
TcpListener tcpListener = new TcpListener(ipAddress, Convert.ToInt32(port));
tcpListener.Start();
Logger("\nWaiting for a client to connect...");
//blocks until a client connects
Socket socketForClient = tcpListener.AcceptSocket();
Logger("\nClient connected");
//Read data sent from client
NetworkStream networkStream = new NetworkStream(socketForClient);
int bytesReceived, totalReceived = 0;
string fileName = "testing.txt";
byte[] receivedData = new byte[10000];
do
{
bytesReceived = networkStream.Read
(receivedData, 0, receivedData.Length);
totalReceived += bytesReceived;
Logger("Progress of bytes recieved: " + totalReceived.ToString());
if (!File.Exists(fileName))
{
using (File.Create(fileName)) { };
}
using (var stream = new FileStream(fileName, FileMode.Append))
{
stream.Write(receivedData, 0, bytesReceived);
}
}
while (bytesReceived != 0);
Logger("Total bytes read: " + totalReceived.ToString());
socketForClient.Close();
Logger("Client disconnected...");
tcpListener.Stop();
}
catch (Exception ex)
{
// Error : "Only one usage of each socket address (protocol/network address/port) is normally permitted"
Logger("There is some error: " + ex.Message);
}
}
}).Start();
}
How can I make sure that my code after do-while loop executes ?
Sender Code:
private static void SendData(string tcpIpAddress, string port, string filename)
{
new Thread(
() =>
{
TcpClient tcpClient = new TcpClient(tcpIpAddress, Convert.ToInt32(port));
//const int bufsize = 8192;
const int bufsize = 10000;
var buffer = new byte[bufsize];
NetworkStream networkStream = tcpClient.GetStream();
using (var readFile = File.OpenRead(filename))
{
int actuallyRead;
while ((actuallyRead = readFile.Read(buffer, 0, bufsize)) > 0)
{
networkStream.Write(buffer, 0, actuallyRead);
}
}
}).Start();
}
How can I make sure that my code after do-while loop executes?
Here, it's easy because you can close the connection on the sender which will cause the receiver to read zero bytes and terminate the loop.
In fact you forgot to clean up the sender. Dispose of all resources. This fixes the problem.
Your code would benefit from the Code Review SE. I see about a dozen issues immaterial to this question. Would be a great way for you to improve yourself.
For example you can replace the copy loops with Stream.Copy.
The usual way I've seen this done to prepend the data being sent with the number of bytes about to be sent. That way the receiver knows that the packet has been read. Even if the network gets interrupted or the sender sends something else without a break.
Note that doing this you can also read the first 8 bytes (or 4 if an Int32 will do the job) before allocating the read buffer which can help optimise the buffer size
Another common way of doing it is with a specific terminator, but then you have to guarantee that whatever you're sending cannot contain that terminator

Android retrieve bytes from inputstream

I am trying to retrieve the byte that transferred by the server that has been programmed in c# as follow:
static void Main(String[] args){
Socket sListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress IP = IPAddress.Parse("10.0.0.92");
IPEndPoint IPE = new IPEndPoint(IP, 4321);
sListen.Bind(IPE);
Console.WriteLine("Service is listening ...");
sListen.Listen(2);
while (true){
Socket clientSocket;
try{
clientSocket = sListen.Accept();
}
catch{
throw;
}
byte[] buffer = ReadImageFile("path to image");
clientSocket.Send(buffer, buffer.Length, SocketFlags.None);
Console.WriteLine("Send success!");
}
}
private static byte[] ReadImageFile(String img){
FileInfo fileinfo = new FileInfo(img);
byte[] buf = new byte[fileinfo.Length];
FileStream fs = new FileStream(img, FileMode.Open, FileAccess.Read);
fs.Read(buf, 0, buf.Length);
fs.Close();
//fileInfo.Delete ();
GC.ReRegisterForFinalize(fileinfo);
GC.ReRegisterForFinalize(fs);
return buf;
}
Above codes works fine when I write a client in the c# and run it in the pc. However I want to retrieve the bytes transferred by the server in the android device.
The android connected to the server successfully but it will not finish its job, basically it will not pass the ‘while loop in the bellow code’. I think there is something wrong with the byte length because it’s never get to ‘-1’.
Android java code (Client):
Socket socket = new Socket("ip", 4321);
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int read = is.read(buffer);
while(read != -1){
read = is.read(buffer);
}
is.close();
socket.close();
I do appreciate any help in advance,
Thanks,
The client read() method will never return -1 until the server closes the connection. You're not doing that so it never happens.
I don't get the point of your Java code. The read(byte[]) method you are using writes 1024 bytes (or less) in the buffer again and again. You are overwriting your previous buffer content. You probably would like to write the downloaded data to somewhere, like an OutputStream. Example:
Socket socket = new Socket("ip", 4321);
InputStream is = socket.getInputStream();
OutputStream os = ...; // Where to save data, for example, new ByteArrayOutputStream();
copyStreams(is, os);
is.close();
socket.close();
public static void copyStreams(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
int numBytesRead;
for (;;) {
bytesRead = is.read(buffer);
if (bytesRead <= 0)
break;
os.write(buffer, 0, bytesRead);
}
}

How can i get more than one jpg. or txt file from any folder?

i have two Application to listen network Stream : Server.cs on the other hand; send file Client.cs. But i want to send more files on a stream from any folder. For example. i have C:/folder whish has got 3 jpg files. My client must run. Also My server.cs get files on stream:
Client.cs:
private void btn_send2_Click(object sender, EventArgs e)
{
string[] paths= null;
paths= System.IO.Directory.GetFiles(#"C:\folder" + #"\", "*.jpg", System.IO.SearchOption.AllDirectories);
byte[] Dizi;
TcpClient Gonder = new TcpClient("127.0.0.1", 51124);
FileStream Dosya;
FileInfo Dos;
NetworkStream Akis;
foreach (string path in paths)
{
Dosya = new FileStream(path , FileMode.OpenOrCreate);
Dos = new FileInfo(path );
Dizi = new byte[(int)Dos.Length];
Dosya.Read(Dizi, 0, (int)Dos.Length);
Akis = Gonder.GetStream();
Akis.Write(Dizi, 0, (int)Dosya.Length);
Gonder.Close();
Akis.Flush();
Dosya.Close();
}
}
Also i have Server.cs
void Dinle()
{
TcpListener server = null;
try
{
Int32 port = 51124;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
server = new TcpListener(localAddr, port);
server.Start();
Byte[] bytes = new Byte[1024 * 250000];
// string ReceivedPath = "C:/recieved";
while (true)
{
MessageBox.Show("Waiting for a connection... ");
TcpClient client = server.AcceptTcpClient();
MessageBox.Show("Connected!");
NetworkStream stream = client.GetStream();
if (stream.CanRead)
{
saveFileDialog1.ShowDialog();
// burası degişecek
string pathfolder = saveFileDialog1.FileName;
StreamWriter yaz = new StreamWriter(pathfolder);
string satir;
StreamReader oku = new StreamReader(stream);
while ((satir = oku.ReadLine()) != null)
{
satir = satir + (char)13 + (char)10;
yaz.WriteLine(satir);
}
oku.Close();
yaz.Close();
client.Close();
}
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
Please look Client.cs: icollected all files from "c:\folder"
paths= System.IO.Directory.GetFiles(#"C:\folder" + #"\", "*.jpg", System.IO.SearchOption.AllDirectories);
My Server.cs how to get all files from stream?
It's not a typical way of doing things.
Usually you would write a single file in a single stream and repeat the process for multiple files. You risk data corruption this way as you'll have to place some kind of marker in the stream to know where to split it.
The easiest way is to use multiple sockets, one for commands and one (or more) for sending files.
The below code have NOT been tested, just wrote it up to show what I mean. It's multithreaded and can receive multiple files from the same client, just call sendfiles multiple times in the client.
You might want to add error/exception handling if you decide to finish the code.
Server.cs
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Test2
{
public class Server
{
private readonly TcpListener _listener = new TcpListener(1234);
public void Start()
{
_listener.BeginAcceptTcpClient(OnClient, null);
}
private void OnClient(IAsyncResult ar)
{
// End async accept and start wait for a new connection again
TcpClient client = _listener.EndAcceptTcpClient(ar);
_listener.BeginAcceptTcpClient(OnClient, null);
// Let's start receiving files from the accepted client.
var context = new Context {Client = client, Buffer = new byte[8196]};
client.GetStream().BeginRead(context.Buffer, 0, context.Buffer.Length, OnReceive, context);
}
/// <summary>
/// Got some stuff from a client
/// </summary>
/// <param name="ar"></param>
private void OnReceive(IAsyncResult ar)
{
// got a file command
var context = (Context) ar.AsyncState;
int bytesRead = context.Client.GetStream().EndRead(ar);
string cmd = Encoding.UTF8.GetString(context.Buffer, 0, bytesRead);
string[] parts = cmd.Split(';');
string command = parts[0];
// want to send another file
if (command == "sendfile")
{
// context info for receiving files
var client = new FileClient();
client.FileName = parts[1];
client.Size = long.Parse(parts[2]);
client.FileStream = new FileStream("C:\\" + client.FileName, FileMode.CreateNew, FileAccess.Write);
// startup listener where we are going to receive the file.
var listener = new TcpListener(IPAddress.Any, 0); // get a kernelassigned number
client.Listener = listener;
listener.Start();
listener.BeginAcceptTcpClient(OnFileSocket, client);
// send reply
var ep = (IPEndPoint) listener.LocalEndpoint;
byte[] reply = Encoding.UTF8.GetBytes(ep.Port.ToString());
context.Client.GetStream().Write(reply, 0, reply.Length);
}
}
// Receiving the actual files.
private void OnFileSocket(IAsyncResult ar)
{
var client = (FileClient) ar.AsyncState;
client.Socket = client.Listener.EndAcceptTcpClient(ar);
var buffer = new byte[8192];
client.Buffer = buffer;
client.Socket.GetStream().BeginRead(buffer, 0, buffer.Length, OnReceiveFile, client);
}
private void OnReceiveFile(IAsyncResult ar)
{
var client = (FileClient) ar.AsyncState;
int bytesRead = client.Socket.GetStream().EndRead(ar);
client.Received += bytesRead;
client.FileStream.Write(client.Buffer, 0, bytesRead);
// recieved complete file, disconnect and exit.
if (client.Received == client.Size)
{
client.FileStream.Close();
client.Socket.Close();
return;
}
client.Socket.GetStream().BeginRead(client.Buffer, 0, client.Buffer.Length, OnReceiveFile, client);
}
#region Nested type: Context
private class Context
{
public byte[] Buffer;
public TcpClient Client;
}
#endregion
#region Nested type: FileClient
private class FileClient
{
public byte[] Buffer;
public string FileName;
public FileStream FileStream;
public TcpListener Listener;
public long Received;
public long Size;
public TcpClient Socket;
}
#endregion
}
}
Client.cs
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Test2
{
internal class Client
{
private readonly IPAddress _server;
private readonly TcpClient _tcpClient = new TcpClient();
public Client(IPAddress server)
{
_server = server;
_tcpClient = new TcpClient();
}
public void Connect()
{
_tcpClient.Connect(new IPEndPoint(_server, 1234));
}
// asks server on which port the file should be sent.
private int RequestPort(string fileName, long length)
{
// lock tpcClient for each request, so we dont mix up the responses.
lock (_tcpClient)
{
// send request
byte[] bytes = Encoding.UTF8.GetBytes("sendfile;" + fileName + ";" + length);
_tcpClient.GetStream().Write(bytes, 0, bytes.Length);
// get reply
var buffer = new byte[1024];
int bytesRead = _tcpClient.GetStream().Read(buffer, 0, buffer.Length);
string reply = Encoding.UTF8.GetString(buffer, 0, bytesRead);
// error message or port?
int port;
if (!int.TryParse(reply, out port))
throw new InvalidOperationException("Server sent an error:" + reply);
return port;
}
}
public void SendFiles(string[] fileNames)
{
// Use a buffer to not preserve memory (instead of reading whole file into memory)
var buffer = new byte[8192];
foreach (string fileName in fileNames)
{
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Send on commandchannel that we want to send a file.
int filePort = RequestPort(Path.GetFileName(fileName), fileStream.Length);
var client = new TcpClient(new IPEndPoint(_server, filePort));
NetworkStream stream = client.GetStream();
// repeat until there are no more bytes to read.
int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
stream.Write(buffer, 0, bytesRead);
bytesRead = fileStream.Read(buffer, 0, buffer.Length);
}
stream.Close();
client.Close();
}
}
}
}
}

Deserializing data sent via TCP

I have problem with sending objects via TCPClient. At first, I serialize them into byte array and then I send them. TCPListener recieves some data, but deserializer is throwing exception "Unexpected end of stream".
Here is reciever code:
public void start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 8090);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
processClient(client);
}
}
public void processClient(TcpClient client)
{
NetworkStream net = client.GetStream();
ReadData(net);
byte[] response = Encoding.UTF8.GetBytes("Hello from the server.");
net.Write(response, 0, response.Length);
net.Close();
client.Close();
}
void ReadData(NetworkStream netstream)
{
byte[] buffer = new byte[2048];
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
netstream.ReadTimeout = 5000;
int bytes = -1;
while ((bytes = netstream.ReadByte()) != -1)
{
memStream.WriteByte((byte)bytes);
}
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bform = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
Packet packet = (Packet)bform.Deserialize(memStream);
OnMessageArrived(this, new MessageEventArgs(packet.From.ToString(), packet.Data.ToString()));
memStream.Close();
netstream.Close();
}
And here is sender code:
public void sendData(string to, Packet data)
{
TcpClient client = new TcpClient();
MemoryStream mstream = new MemoryStream();
client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8090));
if (client.Connected)
{
NetworkStream stream = client.GetStream();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bform = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bform.Serialize(mstream, data);
byte[] buffer = new byte[2048];
mstream.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, buffer.Length);
stream.Flush();
stream.Close();
client.Close();
}
}
Sender main method:
static void Main(string[] args)
{
SimplestTCPIP.Client client = new SimplestTCPIP.Client();
Packet packet = new Packet("client", "server", IPAddress.Parse("127.0.0.1"));
client.sendData("server", packet);
Console.WriteLine("IP: " + GetIP().ToString());
Console.Read();
}
Reciever main method:
static void Main(string[] args)
{
SimplestTCPIP.Server server = new SimplestTCPIP.Server();
server.OnMessageArrived += new SimplestTCPIP.Server.MessageArrived(server_OnMessageArrived);
Thread thread = new Thread(server.start);
thread.Start();
}
static void server_OnMessageArrived(object sender, SimplestTCPIP.Server.MessageEventArgs m)
{
Console.WriteLine(m.From + " : " + m.Text);
}
In your sendData method you serialize the object to a memory stream and then read it back into a buffer of 2048 bytes before writing it to the network stream. If the serialzed object is > 2048 bytes you would have a problem. I would try just serializing directly to the network stream or at least using the same kind of code as in your ReadData method where you write byte by byte.
Edit:
The size of the data is probably not the problem. You should still avoid the hard coded buffer size and stick with the code change you mention in the comment. Given your comment the problem lies elsewhere.
In both your sender and your receiver you write to a memory stream and then read from it. You can't do that unless you set the current position in the stream back to zero between the write and read.
So in your sendData method on the client add the line
mstream.Seek(0, SeekOrigin.Begin);
right after
bform.Serialize(mstream, data);
And in the ReadData method of your server add the line
memStream.Seek(0, SeekOrigin.Begin);
right before
Packet packet = (Packet)bform.Deserialize(memStream);
That way the memory stream is set to the beginning before you try and read from it.
I think you could just skip the memory stream all together and just read and write to the network stream, but you might have other reasons for it.

Categories