C # Socket.BeginReceive / EndReceive
Dans quel ordre est le support.BeginReceive/EndReceive fonctions appelées?
Par exemple, j'appelle BeginReceive deux fois, une fois pour obtenir la longueur du message et la deuxième fois pour obtenir le message lui-même. Maintenant, le scénario est comme ça, pour chaque message que j'envoie, je commence à attendre la fin (en fait accusé de réception du message envoyé, aussi j'attends pour l'action de l'achèvement après réception de l'accusé de réception), je l'appelle BeginReceive avec chaque BeginSendmais dans chaque BeginReceive's de rappel, je vérifie si je suis la réception de la longueur ou le message. Si je reçois le message et l'ai reçu, je l'appelle un autre BeginReceive pour recevoir l'achèvement de l'action. Maintenant, c'est là que les choses deviennent hors de la synchronisation. Parce que l'un de mes recevoir de rappel est la réception d'octets il interprète comme la longueur de leur message, alors qu'en fait c'est le message lui-même.
Maintenant, comment puis-je le résoudre?
EDIT: C'est un C#.NET question 🙂
Voici le code, en gros, c'est trop gros, désolé pour cette
public void Send(string message)
{
try
{
bytesSent = 0;
writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;
clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void WaitForData()
{
try
{
if (!messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}
public void Send(string message)
{
try
{
bytesSent = 0;
writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;
clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void WaitForData()
{
try
{
if (! messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
else
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, messageLength - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void RecieveComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesReceived = socket.EndReceive(result);
if (! messageLengthReceived)
{
if (bytesReceived != MESSAGE_LENGTH_SIZE)
{
WaitForData();
return;
}
//unwrap message length
int length = BitConverter.ToInt32(receiveDataBuffer, 0);
length = IPAddress.NetworkToHostOrder(length);
messageLength = length;
messageLengthReceived = true;
bytesReceived = 0;
//now wait for getting the message itself
WaitForData();
}
else
{
if (bytesReceived != messageLength)
{
WaitForData();
}
else
{
string message = Encoding.ASCII.GetString(receiveDataBuffer);
MessageBox.Show(message);
bytesReceived = 0;
messageLengthReceived = false;
//clear buffer
receiveDataBuffer = new byte[AsyncClient.BUFFER_SIZE];
WaitForData();
}
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void SendComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesSent = socket.EndSend(result);
if (bytesSent != messageSendSize)
{
messageSendSize -= bytesSent;
socket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
return;
}
//wait for data
messageLengthReceived = false;
bytesReceived = 0;
WaitForData();
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
source d'informationauteur akif
Vous devez vous connecter pour publier un commentaire.
L'ordre dans le temps devrait être:
BeginReceive
pour la longueur du messageEndReceive
pour l'achèvement de #1BeginReceive
pour le corps du messageEndReceive
pour l'achèvement de #3E. g. pas à l'aide de rappels, vous pourriez avoir:
Mais alors, vous feriez mieux de simplement en utilisant
Receive
!Je pense que vous trouverez peut-être plus facile à utiliser gestionnaires distinct pour les deux différents reçoit:
En fin de compte mettre tous les associés en état d'objet et en le passant autour de (dans l'achèvement de l'accès délégué par
IAsyncResult.AsyncState
) à partir de BeginReceive peut rendre les choses plus facile, mais ne prendra un changement de la pensée linéaire de code impératif et à refouler, embrassant d'un événement.2012 Addendum:
.NET Version 4.5
Avec async support en C#5 il y a une nouvelle option. Il utilise le compilateur pour générer le manuel de suites (les méthodes de rappel distinctes) et de fermeture (état) de code en ligne. Il y a cependant deux choses à contourner:
Tout
System.Net.Sockets.Socket
a divers…Async
ces méthodes sont pour l'événement asynchrone basé sur le patron, pas laTask
base motif que le C#5await
utilise. Solution: utilisezTaskFactory.FromAsync
pour obtenir un seulTask<T>
à partir d'unBegin…
End…
paire.TaskFactory.FromAsync
prend uniquement en charge les passer jusqu'à trois arguments supplémentaires (en plus de la fonction de rappel et de l'état) àBegin…
. Solution: un lambda de prendre zéro des arguments supplémentaires a le droit de signature, et C# va nous donner le droit de la fermeture à passer les arguments à l'.Donc (et plus pleinement réalisé avec
Message
être un autre type qui gère la conversion d'un premier envoi de la longueur codé en un nombre fixe d'octets, alors le contenu des octets en longueur pour le contenu de la mémoire tampon):Peut-être ce que vous voulez faire est de la chaîne de vos rappels :
pseudo-code:
voir: http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx pour corriger des exemples
Généralement BeginXXX méthodes indiquent une opération asynchrone, et vous semblez vouloir le faire de façon synchrone.
En effet, si vous voulez un synchrones client/serveur peut-être que cela aidera http://sharpoverride.blogspot.com/2009/04/another-tcpip-server-client-well-it.html
Il vous sera utile de décrire la structure d'un message que vous envoyez.
Aussi longtemps que vous avez un seul BeginReceive() en circulation, il sera terminé et vous donner la prochaine octets de données sur le câble. Si vous avez plus d'un cours en même temps, alors tous les paris sont éteints, parce que .net ne garantit pas que la fin va être dans n'importe quel ordre donné.
Comme les autres l'ont dit, ne pas utiliser de variables globales ici - utiliser une classe pour la prise de l'état. Quelque chose comme:
Avant d'essayer de renvoyer le message, vous devez vérifier la prise... vous pourriez avoir une prise d'exception.
Vous devriez probablement avoir un retour; après votre WaitForData appel dans le bloc "if" de ReceiveComplete.
Timothy Pratley l'a dit ci-dessus, une erreur sera dans bytesRecieved la deuxième fois. Chaque fois que vous êtes seulement en mesure de la octetsreçus à partir de ce EndReceive et de comparer ensuite messageLength. Vous avez besoin de garder une somme de tous les bytesRecieved.
Et votre plus grande erreur est que dans votre premier appel à ReceiveComplete, vous prenez en compte le fait que le message peut être (probablement) contenir plus de données que juste la taille du message, il sera probablement contenir la moitié du message. Vous avez besoin de se décoller de la taille des données, et puis aussi stocker le reste du message dans votre message variable.