TCP c# Getting specific protocol information from a byte array - c#

When i receive a connection, i get sent a sort of "Connect Request" message with that connection and in that byte array I have 2 Headers and then the data, 1 header that is 16 bytes and one header that is 48 bytes.
But it seems that i am doing something wrong here. I can read the data i receive just fine. But when trying to translate the headers over something seems to be wrong.
I have been giving the documentation that in the first header the lenght of the request is saved in "byte index 2 and with the byte length 2" So byte 2 and 3 from the array. I then know how each bit should function, we know that with Bit 10-15: Each bit will be set to 0. And that Bit 0-9: Contains the actual lenght count.
In my example i receive the data, split it up so i have my 2 headers as their own arrays and try to look at the data, tried to convert the bytes into int for the lenght but that made no sense so i even split up the 2 bytes to try and see but they return data that does not corrospond with what im told. "Byte nr 2 returns 00000000" and "Byte nr 3 returns 00010101"
Heres my code, i hope someone can tell me where ive gone wrong, im certainly getting some data. since i can read the data part of the message without issue.
public static void StartData(TcpListener listener)
{
while (true)
{
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted." + listener.Pending());
NetworkStream stream = client.GetStream();
StreamWriter sw = new StreamWriter(client.GetStream());
byte[] buffer = new byte[client.ReceiveBufferSize];
int bytesRead = stream.Read(buffer, 0, client.ReceiveBufferSize);
byte[] header = new byte[16];
byte[] encHeader = new byte[48];
for (int i = 0; i < 63; i++)
{
if (i <= 15)
{
Console.WriteLine("added to header " + i);
header[i] = buffer[i];
}
else
{
Console.WriteLine("added to headerEnc " + i);
encHeader[i - 15] = buffer[i - 15];
}
}
Console.WriteLine("Byte nr 2 " + Convert.ToString(header[2], 2).PadLeft(8, '0') + " Byte nr 3 " + Convert.ToString(header[3], 2).PadLeft(8, '0'));
//Byte nr 2 00000000 Byte nr 3 00010101
int dataLength = BitConverter.ToInt32(header, 2);
Console.WriteLine("Data lenght int is " + dataLength);
//result for datalenght is 790959360
}
}

As Jereon van Langen commented, it was indeed because it was big endian

Related

Getting error: Protocol message contained an invalid tag (zero) despite bytes being exactly the same

I'm trying to deserialize my protobuf data from a byte[] in C#. The data is being from another program in c++ over a named pipe. I'm printing the byte (or unsigned char) arrays on both ends and the bytes and lengths of arrays are exactly the same. Despite that i'm getting the invalit tag(zero) error when trying to deserialize the data. The relevant code parts are:
Sending data from C++:
bool writeOutputMessage(HANDLE hPipe, const CubesExample::OutputMessage& outputMessage)
{
int messageBytes = outputMessage.ByteSizeLong();
char buffer[messageBytes + 4];
outputMessage.SerializeToArray(&buffer + 4, messageBytes);
memcpy(buffer, &messageBytes, 4);
messageBytes += 4;
DWORD bytesWritten = 0;
std::cout << std::string("Writing ") + std::to_string(messageBytes) + " bytes" << std::endl;
std::cout << "Sending bytes";
for(char c : buffer) std::cout << " " << +static_cast<unsigned char>(c);
std::cout << std::endl;
BOOL fSuccess = WriteFile(
hPipe, // handle to pipe
buffer, // buffer to write from
messageBytes, // number of bytes to write
&bytesWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || bytesWritten != messageBytes)
{
std::cout << "InstanceThread WriteFile failed, GLE=" << GetLastError() << std::endl;
return false;
}
return true;
}
Receiving data in C#:
public byte[] readOutputMessage()
{
int readBytes = 0;
byte[] messageSizeBuf = new byte[4];
readBytes = fromagent_pipe.Read(messageSizeBuf, 0, 4);
if(readBytes != 4)
{
Debug.Log("Failed to read message size!");
return null;
}
int messageSize = BitConverter.ToInt32(messageSizeBuf, 0);
Debug.Log("Attempting to read message of size: " + messageSize);
byte[] buffer = new byte[messageSize];
readBytes = 0;
readBytes = fromagent_pipe.Read(buffer, 0, messageSize);
if(readBytes != messageSize)
{
Debug.Log("Read " + readBytes + " bytes but expected " + messageSize);
return null;
}
return buffer;
}
Parsing bytes in C#:
byte[] buffer = inputTask.Result;
string bytes = "";
foreach(byte b in buffer) bytes += " " + (uint)b;
Debug.Log("Got bytes: " + bytes + " Length: " + buffer.Length);
if(buffer != null)
{
OutputMessage outputMessage = OutputMessage.Parser.ParseFrom(buffer);
inputMotion = new UnityEngine.Vector2(outputMessage.MovementDirection.XPos, outputMessage.MovementDirection.YPos);
}
Additionaly here is the output from from the C++ process:
Writing 11 bytes
Sending bytes 7 0 0 0 227 0 0 0 72 57 201
And the output from the C# process:
Attempting to read message of size: 7
Got bytes: 227 0 0 0 72 57 201 Length: 7
The writing process writes 4 bytes which indicate the size of the message followed by the message bytes. In this case message size was 7 so writing process wrote 11 bytes total.
As you can see the bytes are exactly the same but the call to OutputMessage.Parser.ParseFrom(buffer) still fails with invalid tag(zero).
What am i missing?
Thanks to anyone reading in advance.
EDIT:
The message that is sent is being created as follows:
CubesExample::OutputMessage outputMessage;
CubesExample::Vector2* moveDir = outputMessage.mutable_movementdirection();
moveDir->set_xpos(1.0f);
moveDir->set_ypos(0.0f);
Typo in SerializeToArray(). Instead of:
outputMessage.SerializeToArray(&buffer + 4, messageBytes);
Should be:
outputMessage.SerializeToArray(buffer + 4, messageBytes);
:D

Why is the endian reversed after sending over TCP

I have a client written in C#, and a server written in python. The messages that I send over the socket are 8 bytes followed by the data, the 8 bytes are the data length.
In C# before sending, I convert the 8-byte data length too big endian as shown:
public void Send(SSLMsg m)
{
string json = m.Serialize();
byte[] data = Encoding.ASCII.GetBytes(json);
ulong dataLen = (ulong)data.Length;
byte[] dataLenPacked = packIt(dataLen);
Log("Sending " + dataLen + " " + json);
sslStream.Write(dataLenPacked);
sslStream.Write(data);
sslStream.Flush();
}
private byte[] packIt(ulong n)
{
byte[] bArr = BitConverter.GetBytes(n);
if (BitConverter.IsLittleEndian)
Array.Reverse(bArr, 0, 8);
return bArr;
}
The message is sent successfully and I am getting tied up in the python server code since the unpack format should be correct here shouldn't it?
(length,) = unpack('>Q', data)
# len(data) is 8 here
# length is 1658170187863248538
Isn't the big-endian character '>'? Why is my length so long?
UPDATE:
There was a bug where I was unpacking the wrong 8 bytes, that has been fixed, now that I am unpacking the correct data I still have the same question.
(length,) = unpack('>Q', data)
# len(data) is 8 here
# length is 13330654897016668160L
The correct length is given only if I unpack using little endian even though I sent the bytes to the server using big-endian... so I am expecting >Q to work, but instead
(length,) = unpack('<Q', data)
# len(data) is 8 here
# length is 185
Here is how I am receiving the bytes in python:
while (True):
r,w,e = select.select(...)
for c in r:
if (c == socket):
connection_accept(c)
else
# c is SSL wrapped at this point
read = 0
data = []
while (read != 8):
bytes = c.recv(min(8-read, 8))
read += len(bytes)
data.append(bytes)
joinedData = ''.join(data)
# the below length is 13330654897016668160L
# I am expecting it to be 185
(length,) = unpack('>Q', joinedData)
# the below length is 185, it should not be however
# since the bytes were sent in big-endian
(length,) = unpack('<Q', joinedData)
Something is wrong with your code:
length is 1658170187863248538
This is in hex 1703010020BB4E9A. This has nothing to do with a length of 8, no matter which endianess is involved. Instead it looks suspiciously like a TLS record:
17 - record type application data (decimal 23)
03 01 - protocol version TLS 1.0 (aka SSL 3.1)
00 20 - length of the following encrypted data (32 byte)
..
Since according to your code your are doing SSL there is probably something wrong in your receiver. My guess is that you read from the plain socket instead of the SSL socket and thus read the encrypted data instead of the decrypted ones.
On client side, when you write data to stream, you're doing two Write calls:
sslStream.Write(dataLenPacked);
sslStream.Write(data);
sslStream.Flush();
MSDN says about NetworkStream.Write: The Write method blocks until the requested number of bytes are sent or a SocketException is thrown. On the server side, there is no guarantee that you will receive all bytes in one receive call - it depends on OS, eth driver/config and etc. So, you have to handle this scenario. As I can see in you're handling it by reading 8 or less bytes, but socket.recv says, it's better to receive by bigger portions. Here is my implementation of the server on Python. It creates binary file in the current folder with received bytes - might be helpful to analyze what's wrong. To set listening port need to use -p/--port argument:
#!/usr/bin/env python
import sys, socket, io
import argparse
import struct
CHUNK_SIZE = 4096
def read_payload(connection, payload_len):
recv_bytes = 0
total_data = ""
while (recv_bytes < payload_len):
data = connection.recv(CHUNK_SIZE)
if not data:
break
total_data += data
recv_bytes += len(data)
if len(total_data) != payload_len:
print >> sys.stderr, "-ERROR. Expected to read {0} bytes, but have read {0} bytes\n".format(payload_len, len(total_data))
return total_data
def handle_connection(connection, addr):
total_received = 0
addrAsStr = "{0}:{1}".format(addr[0], addr[1])
# write receved bytes to file for analyzis
filename = "{0}_{1}.bin".format(addr[0], addr[1])
file = io.FileIO(filename, "w")
print "Connection from {0}".format(addrAsStr)
try:
# loop for handling data transfering for particular connection
while True:
header = connection.recv(CHUNK_SIZE)
header_len = len(header)
total_received += header_len
if header_len == 0:
break
if header_len < 8:
print >> sys.stderr, "-ERROR. Received header with len {0} less than 8 bytes!\n".format(header_len)
break
print("Header len is {0} bytes".format(len(header)))
# extract payload length - it's first 8 bytes
real_header = header[0:8]
file.write(real_header)
# more about unpack - https://docs.python.org/3/library/struct.html#module-struct
# Byte order - network (= big-endian), type - unsigned long long (8 bytes)
payload_len = struct.unpack("!Q", real_header)[0]
print("Payload len is {0} bytes".format(payload_len))
# extract payload from header
payload_in_header = header[8:] if header_len > 8 else ""
if len(payload_in_header) > 0:
print "Payload len in header is {0} bytes".format(len(payload_in_header))
file.write(payload_in_header)
# calculate remains
remains_payload_len = payload_len - len(payload_in_header)
remains_payload = read_payload(connection, remains_payload_len)
payload = payload_in_header + remains_payload
print("Payload is '{0}'".format(payload))
if remains_payload:
file.write(remains_payload)
else:
break
total_received += len(remains_payload)
finally:
file.close()
return total_received
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--port', required=True)
args = parser.parse_args()
# listen tcp socket on all interfaces
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("0.0.0.0", int(args.port)))
s.listen(1)
# loop for handling incoming connection
while True:
print "Waiting for a connection..."
(connection, addr) = s.accept()
addrAsStr = "{0}:{1}".format(addr[0], addr[1])
try:
total_received = handle_connection(connection, addr)
print "Handled connection from {0}. Received: {1} bytes\n".format(addrAsStr, total_received)
finally:
# Clean up the connection
connection.close()
if __name__ == "__main__":
main()
To make this example full, here is C# client. It uses one external library - Newtonsoft.Json for serialization:
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SimpleTcpClient
{
class SimpleTcpClient : IDisposable
{
readonly TcpClient _client;
public SimpleTcpClient(string host, int port)
{
_client = new TcpClient(host, port);
}
public void Send(byte[] payload)
{
// Get network order of array length
ulong length = (ulong)IPAddress.HostToNetworkOrder(payload.LongLength);
var stream = _client.GetStream();
// Write length
stream.Write(BitConverter.GetBytes(length), 0, sizeof(long));
// Write payload
stream.Write(payload, 0, payload.Length);
stream.Flush();
Console.WriteLine("Have sent {0} bytes", sizeof(long) + payload.Length);
}
public void Dispose()
{
try { _client.Close(); }
catch { }
}
}
class Program
{
class DTO
{
public string Name { get; set; }
public int Age { get; set; }
public double Weight { get; set; }
public double Height { get; set; }
public string RawBase64 { get; set; }
}
static void Main(string[] args)
{
// Set server name/ip-address
string server = "192.168.1.101";
// Set server port
int port = 8080;
string[] someNames = new string[]
{
"James", "David", "Christopher", "George", "Ronald",
"John", "Richard", "Daniel", "Kennet", "Anthony",
"Robert","Charles", "Paul", "Steven", "Kevin",
"Michae", "Joseph", "Mark", "Edward", "Jason",
"Willia", "Thomas", "Donald", "Brian", "Jeff"
};
// Init random generator
Random rnd = new Random(Environment.TickCount);
int i = 1;
while (true) {
try {
using (var c = new SimpleTcpClient(server, port)) {
byte[] rawData = new byte[rnd.Next(16, 129)];
rnd.NextBytes(rawData);
// Create random data transfer object
var d = new DTO() {
Name = someNames[rnd.Next(0, someNames.Length)],
Age = rnd.Next(10, 101),
Weight = rnd.Next(70, 101),
Height = rnd.Next(165, 200),
RawBase64 = Convert.ToBase64String(rawData)
};
// UTF-8 doesn't have endianness - so we can convert it to byte array and send it
// More about it - https://stackoverflow.com/questions/3833693/isn-t-on-big-endian-machines-utf-8s-byte-order-different-than-on-little-endian
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(d));
c.Send(bytes);
}
}
catch (Exception ex) {
Console.WriteLine("Get exception when send: {0}\n", ex);
}
Thread.Sleep(200);
i++;
}
}
}
}
According to https://msdn.microsoft.com/en-us/library/z78xtwts(v=vs.110).aspx you are reversing 9 bytes when you invoke:
if (BitConverter.IsLittleEndian)
Array.Reverse(bArr, 0, 8);
and according to https://www.displayfusion.com/Discussions/View/converting-c-data-types-to-c/?ID=38db6001-45e5-41a3-ab39-8004450204b3 a ulong in C# is only 8 bytes.
I don't think that this is necessarily an answer, but maybe it's a clue?

Socket Programming: How can I read a specified number of bytes from buffer?

TCP is stream-based protocol. To convert that stream into my messages, I send the size of each message with the message itself. At server side, I first read the first two bytes of message, which have the size. Then I create a byte array, of size equal to the size which was just read. Then I read the bytes into that array. But for some reason, more bytes are being read than specified. How can I read exactly the same number of bytes as I specify?
Here is my code:
while (true)
{
data = null;
length = null;
size = new byte[2];
handler.Receive(size);
length += Encoding.ASCII.GetString(size, 0, 2);
System.Console.WriteLine("Size: " + Int32.Parse(length));
bufferSize = Int32.Parse(length) + 2;
bytes = new byte[bufferSize];
handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bufferSize);
System.Console.WriteLine("Data: " + data);
}
This is my server running in Windows PC, written in C#. My client is running in android phone, written in Java.
It's unclear why you're adding two to the size that's been transmitted - you've already accounted for the two additional bytes for storing the length during your previous receive. So I'd get rid of the +2.
You also need to respect the fact already stated in your question - TCP is a sequence of bytes, not messages. As such, you're never guaranteed whether a call to Receive is going to retrieve an entire "message" or just part of one (or, possible, parts of multiple messages). As such, you need to make sure that you respect the return value from Receive.
We can probably re-write your code as:
while (true)
{
data = null;
length = null;
size = ReceiveExactly(handler,2);
length = Encoding.ASCII.GetString(size, 0, 2); //Why +=?
bufferSize = Int32.Parse(length); //Why + 2?
System.Console.WriteLine("Size: " + bufferSize);
bytes = ReceiveExactly(handler,bufferSize);
data += Encoding.ASCII.GetString(bytes, 0, bufferSize);
System.Console.WriteLine("Data: " + data);
}
Where ReceiveExactly is defined something like this:
private byte[] ReceiveExactly(Socket handler, int length)
{
var buffer = new byte[length];
var receivedLength = 0;
while(receivedLength < length)
{
var nextLength = handler.Receive(buffer,receivedLength,length-receivedLength);
if(nextLength==0)
{
//Throw an exception? Something else?
//The socket's never going to receive more data
}
receivedLength += nextLength;
}
return buffer;
}
to receive a specific amount of bytes use the method
Socket.Receive(Byte[], Int32, Int32, SocketFlags)
rather than Socket.Receive(Byte[]). see spec here
I suspect you want something like
int len = Socket.Receive(bytes, 0, bufferSize, SocketFlags.None);
data += Encoding.ASCII.GetString(bytes, 0, len);
System.Console.WriteLine("Data: " + data);

C# NetworkStream data loss

I am currently working on a networking project where I worked out a binary protocol. My packets look like this:
[1 byte TYPE][2 bytes INDEX][2 bytes LENGTH][LENGTH bytes DATA]
And here's the code where I am receiving the packets:
NetworkStream clientStream= Client.GetStream();
while (Client.Connected)
{
Thread.Sleep(10);
try
{
if (clientStream.DataAvailable)
{
byte[] infobuffer = new byte[5];
int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }
byte[] rawclient = new byte[2];
Array.Copy(infobuffer, 1, rawclient, 0, 2);
PacketType type = (PacketType)Convert.ToSByte(infobuffer[0]);
int clientIndex = BitConverter.ToInt16(rawclient, 0);
int readLength = BitConverter.ToInt16(infobuffer, 3);
byte[] readbuffer = new byte[readLength];
int count_read = clientStream.Read(readbuffer, 0, readLength);
byte[] read_data = new byte[count_read];
Array.Copy(readbuffer, read_data, count_read);
HandleData(read_data, type, clientIndex);
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[E] " + ex.GetType().ToString());
Console.ResetColor();
break;
}
}
Well, and everything works fine... as long as I run it on 127.0.0.1. As soon as I try testing it over long distance, packets somehow get lost, and I am getting an overflow-exception on the line where I convert the first byte to PacketType. Also, if I try to convert the other values to int16, I get very strange values.
I assume the stream somehow looses some bytes on its way to the server, but can this be? Or is it just a little mistake of mine somewhere in the code?
edit:
I now edited the code, now it reads till it gets its 5 bytes. But I still get the same exception over long distance...
NetworkStream clientStream = Client.GetStream();
while (Client.Connected)
{
Thread.Sleep(10);
try
{
if (clientStream.DataAvailable)
{
int totalread = 0;
byte[] infobuffer = new byte[5];
while (totalread < 5)
{
int inforead = clientStream.Read(infobuffer, totalread, 5 - totalread);
if (inforead == 0)
{ break; }
totalread += inforead;
}
byte[] rawclient = new byte[2];
Array.Copy(infobuffer, 1, rawclient, 0, 2);
PacketType type = (PacketType)Convert.ToSByte(infobuffer[0]);
int clientIndex = BitConverter.ToInt16(rawclient, 0);
int readLength = BitConverter.ToInt16(infobuffer, 3);
byte[] readbuffer = new byte[readLength];
int count_read = clientStream.Read(readbuffer, 0, readLength);
byte[] read_data = new byte[count_read];
Array.Copy(readbuffer, read_data, count_read);
HandleData(read_data, type, clientIndex);
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[E] " + ex.GetType().ToString());
Console.ResetColor();
break;
}
}
PacketType is an enum:
public enum PacketType
{
AddressSocks5 = 0,
Status = 1,
Data = 2,
Disconnect = 3,
AddressSocks4 = 4
}
So many things you're doing wrong here... so many bugs... where to even start...
First Network polling? Really? That's just a naïve way of doing network activity in this day and age.. but I won't harp on that.
Second, with this type of protocol, it's pretty easy to get "out of sync" and once you do, you have no way to get back in sync. This is typically accomplished with some kind of "framing protocol" which provides a unique sequence of bytes that you can use to indicate the start and end of a frame, so that if you ever find yourself out of sync you can read data until you get back in sync. Yes, you will lose data, but you've already lost it if you're out of sync.
Third, you're not really doing anything huge here, so I shamelessly stole the "ReadWholeArray" code from here, it's not the most efficient, but it works and there is other code there that might help:
http://www.yoda.arachsys.com/csharp/readbinary.html
Note: you don't mention how you are serializing the length, type and index values on the other side. So using the BitConverter may be the wrong thing depending on how that was done.
if (clientStream.DataAvailable)
{
byte[] data = new byte[5];
// if it can't read all 5 bytes, it throws an exception
ReadWholeArray(clientStream, data);
PacketType type = (PacketType)Convert.ToSByte(data[0]);
int clientIndex = BitConverter.ToInt16(data, 1);
int readLength = BitConverter.ToInt16(data, 3);
byte[] rawdata = new byte[readLength];
ReadWholeArray(clientStream, rawdata);
HandleData(rawdata, type, clientIndex);
}
/// <summary>
/// Reads data into a complete array, throwing an EndOfStreamException
/// if the stream runs out of data first, or if an IOException
/// naturally occurs.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="data">The array to read bytes into. The array
/// will be completely filled from the stream, so an appropriate
/// size must be given.</param>
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
I think the problem is in these lines
int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }
what happen to your previously read data if the length is under 5 byte? you should save the bytes you have read so far and append next bytes so you can have the header completely
You Read 5 - totalRead.
let totalRead equal 5 or more. When that happens you read nothing, and in cases of 1 - 4 you read that many arbitrary bytes. Not 5. You also then discard any result of less then 5.
You also copy at a offset 1 or another offset without really knowing the offset.
BitConverter.ToInt16(infobuffer, 3);
Is an example of this, what is at off 2?
So if it's not that (decoding error) and and not the structure of your data then unless you change the structure of your loop its you who's losing the bytes not the NetworkStream.
Calculate totalRead by increments of justRead when you recieve so you can handle any size of data as well as receiving it at the correct offset.

File transfer issue in c#

I am transferring files using c#. I have used this code. The problem is small files like .txt files are transferred correctly but not big files like images, documents, pdf, ppt. Sometimes code works fine but most of the times it transfers less amount of data.
Server Code:
Socket clientSock = sock.Accept();
byte[] clientData = new byte[1024 * 50000];
int receivedBytesLen = clientSock.Receive(clientData);
int fileNameLen = BitConverter.ToInt32(clientData, 0);
string fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);
BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + "/" + fileName, FileMode.Append));
bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
bWrite.Close();
clientSock.Close();
Client Code:
byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName);
byte[] fileData = File.ReadAllBytes(filePath + fileName);
byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];
byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);
fileNameLen.CopyTo(clientData, 0);
fileNameByte.CopyTo(clientData, 4);
fileData.CopyTo(clientData, 4 + fileNameByte.Length);
clientSock.Connect(ipEnd);
clientSock.Send(clientData);
clientSock.Close();
Complete code is given in the above link. I have also seen this post but this is not helpful.
Sometimes code works fine but most of the times it transfers less amount of data.
That's the nature of Socket.Receive(), it doesn't always return all data that gets sent to it.
You'll have to do a Receive(clientData, 4, 0) first to receive the bytes that indicate the size , then call Receive(clientData) in a loop until you've received size bytes. But beware that the Receive(buffer[], length, offset) overload just as easily as any other overload can return less than the expected amount of bytes. So you'll also have to call that in a loop:
Something like this:
// First receive the size
int sizeSize = 4; // Size of Int32 in bytes
int sizeOffset = 0;
var sizeBytes = new byte[sizeSize];
while (sizeOffset < sizeSize)
{
sizeOffset += clientSocket.Receive(sizeBytes, sizeSize - sizeOffset, sizeOffset);
}
var size = BitConverter.ToInt32(sizeBytes, 0);
// Then receive the data
byte[] fileData = new byte[size];
byte[] clientData = new byte[8192];
int totalBytes = 0;
while (totalBytes < size)
{
// This may return anything between 0 and 8192, even if not all sent data has been received yet. It may be in a buffer somewhere, waiting to be picked up. Check for 0, since that's when the client disconnects.
int bytesReceived = clientSocket.Receive(clientData);
// You now have received a chunk of data of bytesReceived length. Append it into the fileData array.
Buffer.BlockCopy(clientData, 0, fileData, totalBytes, bytesReceived);
totalBytes += bytesReceived;
}
As CodeCaster's answered that Socket.Receive(), it doesn't always return all data that gets sent to it. This is 100% correct and tested but the next step of sending the file size is not working, I found an easy and correct solution.
Socket.Receive() method takes the byte array in which received data will be copied and returns the number of bytes received. So we can easily loop it till bytes received are 0.
byte[] tempData = new byte[1024 * 5000];
BinaryWriter bWrite = null;
int bytes_received;
int fileNamelength = 0;
bool isFirstPacket = true;
do
{
bytes_received = clientSock.Receive(tempData);
if(isFirstPacket)
{
isFirstPacket = false;
int fileNameLen = BitConverter.ToInt32(tempData, 0);
string fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);
bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Append))
bWrite.Write(tempData, 4 + fileNameLength, bytes_received - 4 - fileNamelength);
}
else
bWrite.Write(tempData, 0, bytes_received);
}
while (bytes_received != 0);

Categories