基本上我已经建立了套接字连接。这是我的WaitData方法:
public void WaitForData()
{
State state = new State();
state.Client = socket;
//issue first receive
state.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), state);
}
每当我的套接字等待数据时就会调用它(我希望它一直等待数据)。 因此,每当开始接收新消息(调用.BeginReceive)时,我希望我的主线程再次调用WaitForData,这样它就会继续接收消息。 怎么会这样做?
(顺便说一下,我不能在ServerReadCallback中调用WaitForData,因为它会阻止实际接收消息)。
很抱歉,如果我的描述很混乱;我觉得很难描述。
答案 0 :(得分:1)
在您的ServerReadCallBack例程中,一旦您完成处理消息,请发出另一个BeginReceive,即:
State state = new State();
state.Client = socket;
//issue first receive
state.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), state);
请记住,如果您的消息长度可变,那么缓冲区中读取的数据可能是消息的一部分;整个信息;或整个消息加上另一条消息的一部分/全部。所以你的ServerReadCallback需要能够处理这个问题 - 这就是:
private void OnDataReceived(IAsyncResult asyn)
{
ReceiveState rState = (ReceiveState)asyn.AsyncState;
Socket client = rState.Client;
SocketError socketError = SocketError.TypeNotFound;
if (!client.Connected)
{
// Not Connected anymore ?
return;
}
_LastComms = DateTime.Now;
_LastIn = _LastComms;
int dataOffset = 0;
int restOfData = 0;
int dataRead = 0;
Boolean StreamClosed = false;
long rStateDataLength = 0;
long LastrStateDataLength = 0;
try
{
dataRead = client.EndReceive(asyn, out socketError);
}
catch (Exception excpt)
{
// Your code goes here..
}
if (socketError != SocketError.Success)
{
// Has Connection been lost ?
OnConnectionDropped(client);
return;
}
if (dataRead <= 0)
{
// Has connection been lost ?
OnConnectionDropped(client);
return;
}
while (dataRead > 0)
{
//check to determine what income data contain: size prefix or message
if (!rState.DataSizeReceived)
{
//there is already some data in the buffer
if (rState.Data.Length > 0)
{
restOfData = PrefixSize - (int)rState.Data.Length;
rState.Data.Write(rState.Buffer, dataOffset, restOfData);
dataRead -= restOfData;
dataOffset += restOfData;
}
else if (dataRead >= PrefixSize)
{ //store whole data size prefix
rState.Data.Write(rState.Buffer, dataOffset, PrefixSize);
dataRead -= PrefixSize;
dataOffset += PrefixSize;
}
else
{ // store only part of the size prefix
rState.Data.Write(rState.Buffer, dataOffset, dataRead);
dataOffset += dataRead;
dataRead = 0;
}
if (rState.Data.Length == PrefixSize)
{ //we received data size prefix
rState.DataSize = BitConverter.ToInt32(rState.Data.GetBuffer(), 0);
rState.DataSizeReceived = true;
//reset internal data stream
rState.Data.Position = 0;
rState.Data.SetLength(0);
}
else
{ //we received just part of the prefix information
//issue another read
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
return;
}
}
//at this point we know the size of the pending data
// Object disposed exception may raise here
try
{
rStateDataLength = rState.Data.Length;
LastrStateDataLength = rStateDataLength;
}
catch (ObjectDisposedException Ode)
{
StreamClosed = true;
}
if (!StreamClosed)
{
if ((rStateDataLength + dataRead) >= rState.DataSize)
{ //we have all the data for this message
restOfData = rState.DataSize - (int)rState.Data.Length;
rState.Data.Write(rState.Buffer, dataOffset, restOfData);
//Console.WriteLine("Data message received. Size: {0}",
// rState.DataSize);
// Is this a heartbeat message ?
// This is my own thing - you may not need this..
if (rState.Data.Length == 2)
{
// Yes
HeartBeatReceived();
}
else
{
// Handle the received messsage
DecodeMessageReceived(GetStringFromStream(rState.Data));
}
dataOffset += restOfData;
dataRead -= restOfData;
//message received - cleanup internal memory stream
rState.Data = new MemoryStream();
rState.Data.Position = 0;
rState.DataSizeReceived = false;
rState.DataSize = 0;
if (dataRead == 0)
{
//no more data remaining to process - issue another receive
if (_IsConnected)
{
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
return;
}
}
else
continue; //there's still some data to process in the buffers
}
else
{ //there is still data pending, store what we've
//received and issue another BeginReceive
if (_IsConnected)
{
rState.Data.Write(rState.Buffer, dataOffset, dataRead);
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack), rState);
dataRead = 0;
}
}
}
else
{
// Stream closed, but have we read everything ?
if (LastrStateDataLength + dataRead == rState.DataSize)
{
// We're equal, get ready for more
//no more data remaining to process - issue another receive
if (_IsConnected)
{
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
}
return;
}
else
{
// We should have more..
// report error ?
}
}
// If we've been disconnected, provide a graceful exit
if (!_IsConnected)
dataRead = -1;
}
}
在此代码中,我的rstate是:
public class ReceiveState
{
public byte[] Buffer = new byte[1024]; //buffer for network i/o
public int DataSize = 0; //data size to be received by the server
public bool DataSizeReceived = false; //whether prefix was received
public MemoryStream Data = new MemoryStream(); //place where data is stored
public Socket Client; //client socket
}
答案 1 :(得分:0)
你想做这样的事情:
private ManualResetEvent _connectionAccepted;
public void WaitForData()
{
while (true)
{
_connectionAccepted.Reset();
socket.BeginAccept(new AsyncCallback(AcceptCallback), state);
_connectionAccepted.WaitOne();
}
}
private void Acceptcallback(IAsyncResult ar)
{
_connectionAccepted.Set();
Socket socket = (Socket)ar.AsyncState;
Socket handler = socket.EndAccept(ar);
State state = new State();
state.Client = handler;
handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), handler);
}
所以,基本上,你是无限循环的。当你得到一个连接,你开始接受它;当您在代码中处理接受连接时,您会发出主循环信号,表明它可以继续运行。然后是EndAccept,它为您提供了第二个套接字 - 该套接字用于与客户端通信,从而允许初始套接字继续接受新连接。
只需要调用一次WaitForData() - 这就是循环所在的位置 - 所以你可能希望将它提升到命名或其他什么水平。但这是一般的想法。