[英]C# Asynchronous Socket client/server
我在使用套接字編程TCP客戶端/服務器時遇到麻煩。 我制作了一個小程序,使用我在這里找到的MSDN示例: http : //msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx,但做了一些修改,例如在發送功能上排隊:
public void Send(String data)
{
// Wait for server connection
if (connectDone.WaitOne())
{
// If already sending data, add to the queue
// it will be sent by SendCallback method
if (_sending)
{
_queue.Enqueue(data);
}
else
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
_sending = true;
SocketHolder.BeginSend(byteData, 0, byteData.Length, 0, SendCallback, SocketHolder);
}
}
}
服務器通過readcallback函數讀取數據:
public void ReadCallback(IAsyncResult ar)
{
string content = string.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Check for end of message tag & raise event
content = state.sb.ToString();
if (content.IndexOf("</MetrixData>") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
OnMessageReceived(EventArgs.Empty, content);
state.sb = new StringBuilder(); // Clear the message from state object string builder
}
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, ReadCallback, state);
}
}
好了,現在這是我的問題 :我的應用程序的重點是將變量高頻發送到服務器(每個變量最多每秒60次)。 但是,當我嘗試快速更新數據時,似乎我的狀態對象的字符串生成器沒有時間正確清除,因為我同時在OnMessageReceived函數中接收到多個數據(這是一個問題,因為我序列化我發送的每個數據,因此我在服務器接收到的消息中以多個根元素結尾)。
如果您想仔細看一下,可以在這里找到整個項目(不確定我的解釋是否清楚...)
預先感謝您的幫助和您的時間:)
編輯:對不起,我將嘗試對我的問題進行更好的解釋:p
這是我僅更新數據時正確發送和接收的消息示例。
<MetrixData>
<DataId>IntTest</DataId>
<Type>System.Int32</Type>
<Value TimeStamp="22/07/14 22:22:19">10</Value>
</MetrixData>
如果我在很短的時間內多次更新數據,這就是服務器收到的信息。
<MetrixData>
<DataId>IntTest</DataId>
<Type>System.Int32</Type>
<Value TimeStamp="22/07/14 22:25:06">12</Value>
</MetrixData><MetrixData>
<DataId>IntTest</DataId>
<Type>System.Int32</Type>
<Value TimeStamp="22/07/14 22:25:06">13</Value>
</MetrixData><MetrixData>
<DataId>IntTest</DataId>
<Type>System.Int32</Type>
<Value TimeStamp="22/07/14 22:25:06">14</Value>
</MetrixData>
(可以同時接收2、3 ...或最多10條消息)
我不知道為什么我的ReadCallback不能檢測到當前消息的結尾並且不重置緩沖區,這應該由ReadCallBack的此示例完成
// Check for end of message tag & raise event
content = state.sb.ToString();
if (content.IndexOf("</MetrixData>") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
OnMessageReceived(EventArgs.Empty, content);
state.sb = new StringBuilder(); // Clear the message from state object string builder
}
TCP為您提供字節流。 TCP中沒有消息。 您可能在一次讀取中收到了兩條邏輯消息。 當您希望處理運行兩次時,這會使處理執行一次。
如果需要消息語義,則必須自己實現它們。 通常,原始TCP連接一開始是錯誤的。 為什么不使用HTTP或Web服務這樣的高級原語? 至少要使用protopuf作為序列化格式。 如果您執行了任何這些操作,則不會發生此問題。 當您自己實施有線協議時,還有更多地雷在您面前。
另外,一旦超出ASCII范圍(換句話說,當此應用投入生產時),字符串編碼將使您失敗。 同樣,此問題僅存在是因為您正在執行所有這些低級工作。 通過網絡電纜發送消息和字符串已自動完成。 利用這項工作。
這是我的推測。 我認為沒有數據包定向(UDP)的情況下,您只有連續的TCP流。 您需要實現以下之一:
郵件信封很簡單:
這是一個快速的提示:
// NetworkStream ns; <-- already set up
using(MemoryStream memory = new MemoryStream())
using(BinaryWriter writer = new BinaryWriter(memory))
{
// all output to writer goes here
// now close the envelop and send it:
byte[] dataBuffer, sizeBuffer;
dataBuffer = memory.ToArray();
sizeBuffer = BitConverter.GetBytes(dataBuffer.Length);
ns.SendBytes(sizeBuffer, 0, 4); // send message length (32 bit int)
ns.SendBytes(dataBuffer, 0, dataBuffer.Length); // send message data
}
在該行的另一端,您將打開信封並按照相反的過程提取消息:
// NetworkStream ns; <-- already set up
byte[] sizeBuffer, dataBuffer;
int size;
sizeBuffer = new byte[4];
ns.ReadBytes(sizeBuffer, 0, 4); // read message length
size = BitConverter.ToInt(sizeBuffer);
dataBuffer = new byte[size];
ns.ReadBytes(dataBuffer, 0, size); // read message data
using(MemoryStream memory = new MemoryStream(dataBuffer))
using(BinaryReader reader = new BinaryReader(memory))
{
// all input from reader goes here
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.