I have a project with a server and a client using asynchronous socket connections.
I want to transmit an object from server to client. Unfortunately I have the problem, that sometimes the object isn't fully transmitted in one go. Therefore I need a way to determine when an object was fully transmitted. So I added a four-byte-head to the data transmitted to tell, how long the stream will be.
Server
private void Send(Socket handler, Packet packet)
{
byte[] byteData = ByteHelper.Serialize(packet).Data;
byte[] byteDataLength = BitConverter.GetBytes(byteData.Length);
byte[] transmissionData = new byte[byteDataLength.Length + byteData.Length];
byteDataLength.CopyTo(transmissionData, 0);
byteData.CopyTo(transmissionData, byteDataLength.Length);
if (debug)
{
Status = "Sending "+packet.Type+"-packet to client.";
}
try
{
handler.BeginSend(transmissionData, 0, transmissionData.Length, 0, new AsyncCallback(SendCallback), handler);
}
catch (Exception ex)
{
Status = "[EXCEPTION]" + ex.Message.ToString();
}
}
The client receives the stream and evaluates the first four bytes to create a StateObject which has the right size. But I have the feeling that this is not really a good way to solve my problem. Is there a better way to do this?
Client
private void ReceiveCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
try
{
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
do
{
state = AdjustState(state);
if (state != null)
{
if (state.buffer.Length == state.bufferSize)
{
ProcessPacket(state);
receiveDone.Set();
}
}
else
{
receiveDone.Set();
}
}
while (state != null && state.tempBuffer.Length >= state.bufferSize);
if (state != null)
{
client.BeginReceive(state.tempBuffer, 0, state.tempBuffer.Length, 0, new AsyncCallback(ReceiveCallback), state);
}
}
}
catch (Exception ex)
{
MessageBox.Show("ReceiveCallback: " + ex.ToString(), "Client-Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static StateObject AdjustState(StateObject state)
{
StateObject tempState = state;
if (tempState.tempBuffer.Length >= 4)
{
byte[] sizeBytes = tempState.tempBuffer.Take(4).ToArray();
int bufferSize = BitConverter.ToInt32(sizeBytes, 0);
if (bufferSize == 0)
{
return null;
}
byte[] temp = tempState.tempBuffer.Skip(4).ToArray();
Socket tempSocket = tempState.workSocket;
tempState = new StateObject(bufferSize);
tempState.BufferSet();
if (temp.Length >= bufferSize)
{
tempState.buffer = temp.Take(bufferSize).ToArray();
tempState.tempBuffer = temp.Skip(bufferSize).ToArray();
}
tempState.workSocket = tempSocket;
}
return tempState;
}
Solution
Thanks to usr I've changed the bytesRead-Part in the ReceiveCallbackCode of the client to this. It seems to work now.
if (bytesRead > 0)
{
if (!state.bufferSet)
{
state = AdjustState(state);
client.BeginReceive(state.buffer, 0, state.bufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
if (state.buffer.Length == state.bufferSize)
{
ProcessPacket(state);
receiveDone.Set();
}
else
{
client.BeginReceive(state.buffer, state.buffer.Length, state.bufferSize - state.buffer.Length, 0, new AsyncCallback(ReceiveCallback), state);
}
}
}
You are not making use of the return value from EndReceive
( bytesRead
). It signals how many bytes were received.
And in case bytesRead == 0
you just do nothing which is probably not a proper response to the connection having ended.
And then, there is this unsynchronized busy-loop:
while (state != null && state.tempBuffer.Length >= state.bufferSize);
That's going to burn one CPU core for each client connection. Must solve this differently.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.