3

This is my first question posted on this forum, and I'm a beginner in c# world , so this is kind of exciting for me, but I'm facing some issues with sending a large amount of data through sockets so this is more details about my problem:

I'm sending a binary image of 5 Mo through a TCP socket, at the receiving part I'm saving the result(data received ) and getting only 1.5 Mo ==> data has been lost (I compared the original and the resulting file and it showed me the missed parts) this is the code I use:

private void senduimage_Click(object sender, EventArgs e)
{
    if (!user.clientSocket_NewSocket.Connected)
    {
        Socket clientSocket_NewSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        user.clientSocket_NewSocket = clientSocket_NewSocket;
        System.IAsyncResult _NewSocket = user.clientSocket_NewSocket.BeginConnect(ip_address, NewSocket.Transceiver_TCP_Port, null, null);
        bool successNewSocket = _NewSocket.AsyncWaitHandle.WaitOne(2000, true);
     }
     byte[] outStream = System.Text.Encoding.ASCII.GetBytes(Uimage_Data);
     user.clientSocket_NewSocket.Send(outStream);
 }

In forums they say to divide data into chunks, is this a solution, if so how can I do this, I've tried but it didn't work!

Toon Krijthe
  • 52,876
  • 38
  • 145
  • 202
user1767701
  • 29
  • 1
  • 1
  • 3

4 Answers4

2

There are lots of different solutions but chunking is usually a good solution, you can either do this blindly where you keep filling your temporary buffer and then putting it into some stateful buffer until you hit some arbitrary token or the buffer is not completely full, or you can adhere to some sort of contract per tcp message (a message being the overall data to recieve).

If you were to look at doing some sort of contract then do something like the first N bytes of a message is the descriptor, which you could make as big or as small as you want, but your temp buffer will ONLY read this size up front from the stream.

A typical header could be something like:

public struct StreamHeader // 5 bytes
{
   public byte MessageType {get;set;} // 1 byte
   public int MessageSize {get;set;}  // 4 bytes
}

So you would read that in then if its small enough allocate the full message size to the temp buffer and read it all in, or if you deem it too big chunk it into sections and keep reading until the total bytes you have received match the MessageSize portion of your header structure.

Grofit
  • 17,693
  • 24
  • 96
  • 176
  • 2
    Your general explanation is correct, however I must point out that you indicate that the above struct is "5 bytes". This is only correct if you apply the [StructLayoutAttribute](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx) with the [LayoutKind.Sequential](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.layoutkind.aspx) parameter combined with the [Pack](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.pack.aspx) property set to 1. – Francois Nel Oct 23 '12 at 09:46
  • yeah I did prefix it with "something like" as I was making it up as I was typing, good point you raise though. – Grofit Oct 23 '12 at 09:59
0

Probably you haven't read the documentation on socket usage in C#. (http://msdn.microsoft.com/en-us/library/ms145160.aspx)

The internal buffer can not store all the data you provided to send methode. A possible solution to your problem can be is like you said to divide your data into chunks.

int totalBytesToSend = outstream.length;     int bytesSend = 0;
while(bytesSend < totalBytesToSend )
    bytesSend+= user.clientSocket_NewSocket.Send(outStream, bytesSend, totalBytesToSend - bytesSend,...);
K Roobroeck
  • 1,378
  • 2
  • 10
  • 13
  • 1
    This only applies to the `Send (buffer, flags)` overload. The `Send (buffer)` does not call this limitation out. – Skizz Oct 23 '12 at 09:02
  • Indeed, Send(byte[] buffer,int offset,int size, SocketFlags socketFlags) – K Roobroeck Oct 23 '12 at 09:09
  • thank you very much for the quick responses , i didn't expect having answer as quickly as you did :) i've tried almost of the methods but i still have the same problem some of the data is still missing, i thought it would have a relation with my file type: it is a binary file, so i tried sending file.dat and it worked fine. My question is: Why a binary file can not be sent without loosing some data and how can i process to resolve this issue?? Thanks ;) – user1767701 Oct 23 '12 at 13:33
  • I would suggest to post your client code. Similar to your server code you need to wait untill your Receive methode returns 0 to make sure all data is received. – K Roobroeck Oct 24 '12 at 06:53
0

I suspect that one of your problems is that you are not calling EndConnect. From the MSDN documentation:

The asynchronous BeginConnect operation must be completed by calling the EndConnect method.

Also, the wait:-

bool successNewSocket = _NewSocket.AsyncWaitHandle.WaitOne(2000, true);

is probably always false as there is nothing setting the event to the signaled state. Usually, you would specify a callback function to the BeginConnect function and in the callback you'd call EndConnect and set the state of the event to signaled. See the example code on this MSDN page.

UPDATE

I think I see another problem:-

byte[] outStream = System.Text.Encoding.ASCII.GetBytes(Uimage_Data);

I don't know what type Uimage_Data but I really don't think you want to convert it to ASCII. A zero in the data may signal an end of data byte (or maybe a 26 or someother ASCII code). The point is, the encoding process is likely to be changing the data.

Can you provide the type for the Uimage_Data object?

Skizz
  • 69,698
  • 10
  • 71
  • 108
  • Or just use the synchronous [Connect](http://msdn.microsoft.com/en-us/library/d7ew360f.aspx) method, since that is what he is doing anyway... – Francois Nel Oct 23 '12 at 09:52
  • @FrancoisNel: The synchronous version has no timeout that can be specified although the underlying provider may timeout at some point. – Skizz Oct 23 '12 at 11:21
  • thank you very much for the quick responses , i didn't expect having answer as quickly as you did :) i've tried almost of the methods but i still have the same problem some of the data is still missing, i thought it would have a relation with my file type: it is a binary file, so i tried sending file.dat and it worked fine. My question is: Why a binary file can not be sent without loosing some data and how can i process to resolve this issue?? Thanks ;) – user1767701 Oct 23 '12 at 12:55
  • @user1767701: Made an update to my answer with another potential problem with the code. – Skizz Oct 23 '12 at 21:22
  • @Skizz that is why we have the [SendTimeout](http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.sendtimeout.aspx) property – Francois Nel Oct 24 '12 at 06:37
  • @FrancoisNel: The SendTimeout seems to be only applicable to the Send method. I was referring to the Connect method which doesn't have anything similar (although I'm not ruling out that possibility, the .Net framework is a large beast after all). The only way I know of to timeout the connect is to do it asynchronously and use the WaitOne method with the timeout. – Skizz Oct 24 '12 at 08:13
  • Hi all,,@ skizz the Uimage_Data is a binary file, i still have the same problem (data loss) is it related to: byte[] outStream = System.Text.Encoding.ASCII.GetBytes(Uimage_Data); should i use something more appropriate to this data type (binary) – user1767701 Oct 24 '12 at 09:04
  • Hello every body! I am still stuck in the binary file problem :((( any help please?? – user1767701 Oct 29 '12 at 12:59
0

Most likely the problem is that you are closing the client-side socket before all the data has been transmitted to the server, and it is therefore getting discarded.

By default when you close a socket, all untransmitted data (sitting in the operating system buffers) is discarded. There are a few solutions:

[1] Set SO_LINGER (see http://developerweb.net/viewtopic.php?id=2982) [2] Get the server to send an acknowledgement to the client, and don't close the client-side socket until you receive it. [3] Wait until the output buffer is empty on the client side before closing the socket (test using getsocketopt SO_SND_BUF - I'm not sure of the syntax on c#).

Also you really should be testing the return value of Send(). Although in theory it should block until it sends all the data, I would want to actually verify that and at least print an error message if there is a mismatch.

CpnCrunch
  • 4,831
  • 1
  • 33
  • 31