Sword Art
Sword Art

Reputation: 29

C# Tcp is losing packets?

I wrote a C# chat software that uses a new (at least for me) system that I called request system. I don't know if that has been created before, but for now I think of it as my creation :P

Anyhow, this system works like this:

  1. soc receives a signal
  2. checks the signal
  3. if the data it just received is the number 2, the client software knows that the server is about to send a chat message. if the number is 3, so the client knows that the server is about to send the member list, and so on.

The problem is this: when I do step-by-step in VS2012 it works fine, the chat is working properly. When I use it on debug mode or just run it on my desktop, there seems to be missing data, and it shouldn't be because the code is working just fine...

Example of code for the sending&receiving message on client:

    public void RecieveSystem()
    {
        while (true)
        {
            byte[] req = new byte[1];
            soc.Receive(req);
            int requestID = int.Parse(Encoding.UTF8.GetString(req));
            if (requestID == 3)
            {
                byte[] textSize = new byte[5];
                soc.Receive(textSize);
                byte[] text = new byte[int.Parse(Encoding.UTF8.GetString(textSize))];
                soc.Receive(text);
                Dispatcher.Invoke(() => { ChatBox.Text += Encoding.UTF8.GetString(text) + "\r\n"; });
            }
        }
    }

    public void OutSystem(string inputText)
    {
        byte[] req = Encoding.UTF8.GetBytes("3");
        soc.Send(req);
        byte[] textSize = Encoding.UTF8.GetBytes(Encoding.UTF8.GetByteCount(inputText).ToString());
        soc.Send(textSize);
        byte[] text = Encoding.UTF8.GetBytes(inputText);
        soc.Send(text);
        Thread.CurrentThread.Abort();
    }

and on the server:

    public void UpdateChat(string text)
    {
        byte[] req = Encoding.UTF8.GetBytes("3");
        foreach (User user in onlineUsers)
            user.UserSocket.Send(req);
        byte[] textSize = Encoding.UTF8.GetBytes(Encoding.UTF8.GetByteCount(text).ToString());
        foreach (User user in onlineUsers)
            user.UserSocket.Send(textSize);
        byte[] data = Encoding.UTF8.GetBytes(text);
        foreach (User user in onlineUsers)
            user.UserSocket.Send(data);
    }
           public void RequestSystem(Socket soc)
        {
~~~
                    }
                    else if (request == 3)
                    {
                        byte[] dataSize = new byte[5];
                        soc.Receive(dataSize);
                        byte[] data = new byte[int.Parse(Encoding.UTF8.GetString(dataSize))];
                        soc.Receive(data);
                        UpdateChat(Encoding.UTF8.GetString(data));
                    }
                }
                catch
                {
                    if (!soc.Connected)
                    {
                        Dispatcher.Invoke(() => { OnlineMembers.Items.Remove(decodedName + " - " + soc.RemoteEndPoint); Status.Text += soc.RemoteEndPoint + " Has disconnected"; });
                        onlineUsers.Remove(user);
                        Thread.CurrentThread.Abort();
                    }
                }
            }
        }

What could be the problem?

Upvotes: 0

Views: 3179

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1499770

You're assuming that you'll have one packet for each Send call. That's not stream-oriented - that's packet-oriented. You're sending multiple pieces of data which I suspect are coalesced into a single packet, and then you'll get them all in a single Receive call. (Even if there are multiple packets involved, a single Receive call could still receive all the data.)

If you're using TCP/IP, you should be thinking in a more stream-oriented fashion. I'd also encourage you to change the design of your protocol, which is odd to say the least. It's fine to use a length prefix before each message, but why would you want to encode it as text when you've got a perfectly good binary connection between the two computers?

I suggest you look at BinaryReader and BinaryWriter: use TcpClient and TcpListener rather than Socket (or at least use NetworkStream), and use the reader/writer pair to make it easier to read and write pieces of data (either payloads or primitives such as the length of messages). (BinaryWriter.Write(string) even performs the length-prefixing for you, which makes things a lot easier.)

Upvotes: 4

Related Questions