I’m building a simple tcp/ip chat program and I’m having difficulty sending messages separately. If for example I send two messages and both have content larger than the buffer which can hold 20 characters, 20 characters of the first message is sent and then 20 characters of the next message is sent and then the the rest of the first message and then the rest of the last message. So when I parse and concatenate the strings I get two messages, the beginning of the first message and the beginning of the second and the end of the first and end of the second, respectively. I want to know how to send a message, and queue the next messages until the first message has already been sent. As a side note I’m using asynchronous method calls and not threads.
My code:
Client:
protected virtual void Write(string mymessage)
{
var buffer = Encoding.ASCII.GetBytes(mymessage);
MySocket.BeginSend(buffer, 0, buffer.Length,
SocketFlags.None,EndSendCallBack, null);
if (OnWrite != null)
{
var target = (Control) OnWrite.Target;
if (target != null && target.InvokeRequired)
{
target.Invoke(OnWrite, this, new EventArgs());
}
else
{
OnWrite(this, new EventArgs());
}
}
}
and the two calls that get mixed:
client.SendMessage("CONNECT",Parser<Connect>.TextSerialize(connect));
client.SendMessage("BUDDYLIST","");
and finally the read function (I use a number at the beginning of every message to know when the message ends surround by brackets):
private void Read(IAsyncResult ar)
{
string content;
var buffer = ((byte[]) ar.AsyncState);
int len = MySocket.EndReceive(ar);
if (len > 0)
{
string cleanMessage;
content = Encoding.ASCII.GetString(buffer, 0, len);
if (MessageLength == 0)
{
MessageLength = int.Parse(content.Substring(1, content.IndexOf("]", 1) - 1));
cleanMessage = content.Replace(content.Substring(0, content.IndexOf("]", 0) + 1), "");
}
else
cleanMessage = content;
if(cleanMessage.Length <1)
{
if(MySocket.Connected)
MySocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Read), buffer);
return;
}
MessageLength = MessageLength > cleanMessage.Length? MessageLength - cleanMessage.Length : 0;
amessage += cleanMessage;
if(MessageLength == 0)
{
if (OnRead != null)
{
var e = new CommandEventArgs(this, amessage);
Control target = null;
if (OnRead.Target is Control)
target = (Control)OnRead.Target;
if (target != null && target.InvokeRequired)
target.Invoke(OnRead, this, e);
else
OnRead(this, e);
}
amessage = String.Empty;
}
MySocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Read), buffer);
return;
}
}
TCP do not guarantee that you will receive your entire message in one read. Therefore you need to be able to detect where a message starts and where it ends.
You normally do that by adding some special characters at the end of the message. Or use a length header before the actual message.
You normally do not need to use
BeginSendin a client. Send should be fast enough and will also reduce complexity. Also, I usually do not useBeginSendin servers either unless the server should be really performant.Update
The actual socket implementation will never ever mix your messages, only your code can do that. You cannot send a message by calling multiple sends, since then your messages will be mixed if your application is multi threaded.
In other words, this will not work:
You have to send everything with one send.
Update 2
Your read implementation do not take into account that two messages can come in the same Read. It’s most likely that that’s the cause to your mixed messages.
If you send:
They can arrive as:
In other words, part of the second message can arrive in the first
BeginRead. You should always build a buffer with all received contents (useStringBuilder) and remove the handled parts.Pseudo code:
Do you see what I’m doing? I’m always appending the stringbuilder with the received data and then remove complete messages. I’m using a loop since multiple messages can arrive at once.