0

Client app creates a List<Person> data from an ObservableCollection<Person>, then converts the data into a byte[] array and sends the Length of the array to the server before sending the actual array like this:

void SendData(object o)
{
    var data = new List<Person>(ListOfPerson);
    var array = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
    socket.Send(BitConverter.GetBytes(array.Length));
    socket.Send(array);
    ListOfPerson.Clear();
}

and server first reads the length from e.Buffer which initially was set to 4, then resets e.Buffer and receives whatever client sends in a single Receive call like this:

void Receive(object sender, SocketAsyncEventArgs e)
{
    int length = BitConverter.ToInt32(e.Buffer, 0);
    SetBuffer(e, length);
    int read = (sender as Socket).Receive(e.Buffer);
    var json = Encoding.UTF8.GetString(e.Buffer);
    SetBuffer(e, 4);
    (sender as Socket).ReceiveAsync(e);
}

void SetBuffer(SocketAsyncEventArgs e, int length) => e.SetBuffer(new byte[length], 0, length);

It doesn't matter how many bytes client sends, server always receives whole thing in one Receive call and read shows that it's received all that! In this particular case I've sent a list of 5 Person that takes a Name and Age

public class Person
{
    public string Name { get; set; }
    public Nullable<int> Age { get; set; }
    public Person(string name, Nullable<int> age)
    {
        Name = name;
        Age = age;
    }
    public override string ToString() => Name + " " + Age;
}

I've copied 9999 words from Lorem ipsum generator and pasted that as Name of all 5 Person and 10 as Age. So the length client sent was 341706 bytes and server received all those in that single call and converted it to json string! Will I always get all data client sends in a single call regardless of the number of bytes in TCP communication?

  • 2
    Not unless you use a NetworkStream will you get everything in one chunck. TCP sends data in chunks of approximately 1500 bytes (a datagram). Most of the time you may get everything in one chunk because the speed of the Ethernet is very fast. But there are a lot of factors that affect Ethernet timing and packing and I would want to develop code that works 95% of the time. Do not rely on always getting data in one chunk. – jdweng Oct 28 '19 at 09:34
  • @jdweng, I faced another problem when sending fragmented array! It's easy to set the `offset` of `byte[]` but it's hard to determine the `size` to be read in another overload of `Receive` which takes `array, offset, size and socketflags` as arguments! –  Oct 28 '19 at 09:40
  • 1
    You are missing an application layer over the transfer layer. A client is a master and a server is a slave. So when you send commands/message from a client to a server you usually just do not send the data. You usually preceded the data with a command. So you need to send the three things 1) The type of data 2) The Size of data 3) The data. – jdweng Oct 28 '19 at 09:49
  • @jdweng, could you please give an example of issuing command and packing data with those three things in answer section if you don't mind? –  Oct 28 '19 at 10:22
  • 1
    The command can simply be the three items I mentioned in previous posting. There are 7 Network layers and you are using the Application Layer and the Transport Layer (TCP). You are not always sending the same type data so you need a prefix before the data indicating type and size. Applications do not need to be complicated. Most people working with TCP just think the need to send the data and do not consider the packing and unpacking of the data. You are sending different type data so you need to indicate the type along with the data. – jdweng Oct 28 '19 at 10:35
  • @jdweng, in TCP I should receive all data in a single call as per my understanding! I watched one/two series of lectures of some Universities where the professor/lecturer mentioned that in TCP, Client and Server keep exchanging `ACK` until the whole transaction completes! So when I call `send` in the client and `receive` in the server, none of the client and server should stop sending and receiving theoratically until it finishes! One thing I've forgotten for sure, whether the `ACK` is like "I'm alive, I'm alive .." or "I sent this much, that much ..." –  Oct 28 '19 at 10:50
  • 1
    TCP max datagram size is 1500 bytes with an ACK for each datagram. There is also a IP sequence number which increments for each datagram. And there is a continuation bit in the IP header. See WIKI : https://en.wikipedia.org/wiki/IPv4 and https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure – jdweng Oct 28 '19 at 11:19
  • @jdweng, is `Available` the solution? For example, first 4 bytes of the packet includes size of data and the remainder of the packet is actual data. First there'll be a loop `while (socket.Available < 4) Task.Delay(100)` and then `buffSize = readFourBytes` and again `while (socket.Available < buffSize) Task.Delay(100)` and then `data = readRemainingBytes`. –  Nov 01 '19 at 15:58
  • 1
    I do not use delays. Hate them. The end of the data is when you receive all the bytes. Just append the receive data together and wait until all the data is received before processing by counting bytes. – jdweng Nov 01 '19 at 16:21
  • @jdweng, great, `Socket.Available` is the solution. Look at [another issue](https://stackoverflow.com/questions/58638734/socketasynceventargs-sendasync-callback-doesnt-work), when I try to use same `SocketAsyncEventArgs` for both `SendAsync` and `ReceiveAsync`, I get an error! So in both client and server, I've creaded 2 `SocketAsyncEventArgs` per `Socket` BUT the `sendArgs.Completed` event doesn't work automatically! –  Nov 01 '19 at 16:42
  • 1
    TCP in Net Library you cannot use Synchronous Send with Asynchronous Receive. You must use both Asynchronous with send and receive. I do not know what changes you made and believe the "another issue" has nothing to do with your issue. – jdweng Nov 01 '19 at 17:11
  • @jdweng, both `Send` and `SendAsync` work on same `SAEA.AcceptSocket`, on which I call `ReceiveAsync`, if I pass the `SAEA.Buffer` instead of `SAEA` BUT if I pass the `SAEA` then I get an error which says the `SAEA` is being used in another asynchronous operation. That's why I've created two `SAEA`, one for `ReceiveAsync` and the other for `SendAsync`. Problem is `Completed` event of the `SAEA`, used for `SendAsync`, isn't fired automatically! I've to have `bool a = SAEA.SendAsync(SAEA)` and then `if(!a) CallBack(SAEA)`. –  Nov 01 '19 at 17:40
  • 1
    This statement is SYNCHRONOUS : socket.Send. Will not work with : int read = (sender as Socket).Receive(e.Buffer); – jdweng Nov 02 '19 at 02:06
  • @jdweng, it doesn't work even if I remove `Socket.Receive(e.Buffer)`! On a connected socket `SendAsync` is always done immediately and synchronously. If I, however, call `SendAsync` before accepting client and a client connects afterwards, it sends asynchronously and goes to the callback. There must be a delay in between calling `SendAsync` and actual `Send` to make it work! –  Nov 02 '19 at 04:24
  • See msdn sample for socket. Code with work with any class that inherits the socket list TCPClient and TCPListener. You can't send until the connection is made. You do not need a delay. Make sure you are not sending until the Accept is completed. https://learn.microsoft.com/en-us/dotnet/framework/network-programming/socket-code-examples – jdweng Nov 02 '19 at 11:07

0 Answers0