我正在將一個AS3客戶端移植到C#,並且在完成登錄過程到服務器時遇到很大問題。將AS3轉換爲C#異步套接字不接收所有數據
我沒有訪問服務器,只有協議,並且不知道服務器是否期待有關套接字的任何特定行爲。
我設法連接到服務器並完成登錄過程。然後,我可以發送一條請求數據的消息,從而將一系列消息發送給我的客戶。
在客戶端的AS3版本中,我在三個不同的緩衝區中接收消息。
在我的C#中,我只獲得前兩個緩衝區,然後短時間重新連接。
該協議是二進制的。前2個字節告訴我消息的長度,第2個2字節的消息類型。其餘是數據。
在第一次讀取時,我得到了一個91字節的策略文件,我放棄了。之後,我收到了我能夠處理的數據,並且前20條消息都很好。雖然第三個緩衝區永遠不會到達。
任何想法?是我的錯誤AsyncSocket的實現,還是有一些我應該在我的套接字上使用的標誌?
任何指針將不勝感激。
public abstract class AsyncSocket
{
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 4096;
public byte[] buffer = new byte[BufferSize];
public byte[] messageBuffer = new byte[0];
}
public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e);
public delegate void ConnectedHandler(object sender, EventArgs e);
public event MessageReceivedHandler MessageReceived;
public event ConnectedHandler Connected;
private IPAddress[] addresses;
private int port;
private WaitHandle addressesSet;
private Socket socket;
private int failedConnectionCount;
private StateObject state;
public AsyncSocket(IPAddress address, int port) : this(new[] { address }, port) { }
public AsyncSocket(IPAddress[] addresses, int port) : this(port)
{
this.addresses = addresses;
}
public AsyncSocket(string hostNameOrAddress, int port) : this(port)
{
addressesSet = new AutoResetEvent(false);
Dns.BeginGetHostAddresses(hostNameOrAddress, GetHostAddressesCallback, null);
}
private void GetHostAddressesCallback(IAsyncResult result)
{
addresses = Dns.EndGetHostAddresses(result);
((AutoResetEvent)addressesSet).Set();
}
private AsyncSocket(int port)
{
this.port = port;
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.Encoding = Encoding.Default;
this.state = new StateObject();
state.workSocket = socket;
}
public Encoding Encoding { get; set; }
public Account Account { get; set; }
public void Connect()
{
if (addressesSet != null)
addressesSet.WaitOne();
Interlocked.Exchange(ref failedConnectionCount, 0);
socket.BeginConnect(addresses, port, ConnectCallback, socket);
}
private void ConnectCallback(IAsyncResult result)
{
try
{
Socket client = (Socket)result.AsyncState;
client.EndConnect(result);
if (Connected != null)
{
Connected(this, new EventArgs());
}
Receive(client);
}
catch
{
Interlocked.Increment(ref failedConnectionCount);
if (failedConnectionCount >= addresses.Length)
{
return;
}
}
}
public void Send(string data)
{
byte[] bytes = Encoding.GetBytes(data);
Send(bytes);
}
public void Send(MsgHead msg)
{
byte[] bytes = msg.write();
Send(bytes);
}
public void Send(byte[] bytes)
{
int messageLength = BitConverter.ToUInt16(bytes, 0);
int messageType = BitConverter.ToUInt16(bytes, 2);
Console.Out.WriteLine("Sending:len:{0} msg:{1}", messageLength, messageType);
socket.BeginSend(bytes, 0, bytes.Length, 0, new AsyncCallback(WriteCallback), socket);
}
private void WriteCallback(IAsyncResult result)
{
Socket client = (Socket)result.AsyncState;
int bytesSent = client.EndSend(result);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
}
private void Receive(Socket client)
{
try
{
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Account.Window.Exit(string.Format("Error on receive: {0}",e.Message));
}
}
private void ReceiveCallback(IAsyncResult result)
{
StateObject state = (StateObject)result.AsyncState;
Socket client = state.workSocket;
SocketError errorCode;
int bytesRead = client.EndReceive(result, out errorCode);
if (errorCode != SocketError.Success)
{
Account.Window.Exit(string.Format("Disconnected, {0}", errorCode.ToString()));
return;
}
if (bytesRead == 0)
{
Account.Window.Exit("Disconnected, zero bytes");
return;
}
state.messageBuffer = state.messageBuffer.Concat(state.buffer.Take(bytesRead).ToArray()).ToArray();
int messageLength = BitConverter.ToUInt16(state.messageBuffer, 0);
if (messageLength > 4096)
{
state.messageBuffer = state.messageBuffer.Skip(91).ToArray();
messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0);
}
while (messageLength > 0 && state.messageBuffer.Length >= messageLength)
{
int messageType = BitConverter.ToUInt16(state.messageBuffer, 2);
Console.Out.WriteLine("Received:len:{0} msg:{1}", messageLength, messageType);
if (MessageReceived != null)
{
MessageReceived(this, new MessageReceivedEventArgs(state.messageBuffer.Take(messageLength).ToArray()));
}
state.messageBuffer = state.messageBuffer.Skip(messageLength).ToArray();
messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0);
}
Receive(client);
}
}
感謝您的回覆。我已經添加了一個檢查,以確保在計算消息長度之前至少有2個字節。我也曾經使用BackgroundWorker來處理「完整」消息的ConcurrentQueue,但這沒有什麼區別。 讓我困惑的是,我只是不接受第三個緩衝區,我做錯了什麼?或者更可能是服務器期望的行爲? –
聽起來像你的數據請求正常工作。我會嘗試計算在兩個客戶端的初始數據請求後收到的總字節數。如果匹配,至少你知道問題出在C#客戶端的TCP消息框架中。 (我懷疑檢查> 4096和跳過91個字節,這太硬編碼了)。 –