我有一个问题,接收套接字包排序使用SocketAsyncEventArgs。我问题的症结在于,当客户端向服务器发送数据包时,服务器将以非标准大小的片段接收数据包,并按随机顺序处理。这显然意味着数据包不能被我的应用程序解码,而应用程序则会破坏整个对话。
例如,客户机将使用Socket.NetworkStream.Write()方法发送一个完整的数据包:
[-------PACKET-------]使用SocketAsyncEventArgs的服务器将在两个单独的数据包上接收异步回调,但将首先处理数据包的最后一块:
First packet: ET-------]
Second packet: [-----PACK--这并不是在所有的数据包上都会发生,而且我还没有能够根据数据包大小或时间精确地再现它。我实现了send/ack通信协议,以便在服务器成功接收最后一个数据包之前,客户端不会再发送另一个数据包,所以不可能是我重载了服务器。
最令人沮丧的是,服务器上的Socket.Available总是为零,根据文档,这意味着没有任何可读取的东西。
If you are using a non-blocking Socket, Available is a good way to determine whether data is
queued for reading, before calling Receive. The available data is the total amount of data
queued in the network buffer for reading. If no data is queued in the network buffer,
Available returns 0.在零可用的情况下,SocketEventArgs.Count似乎没有提供任何有价值的东西,偏移量与接收缓冲区有关,而不是基于实际数据流的位置--我不知道如何将这些片段排序。
我猜问题是数据包的第一部分的异步回调被第二个回调抢占,第二个回调完全处理,然后返回到第一个部分。问题是我不能同步整个回调(希望.NET有一个像Java这样的同步函数)。即使我这么做了,这似乎从一开始就否定了异步回调的好处。
我做错了什么,使这些错误的顺序,或我可以做什么,使他们正确处理?
发布于 2015-03-20 07:22:35
我不太清楚你的一些发言是怎么做的。您编写时使用的是SocketAsyncEventArgs,但是正在尝试处理.Count或.Available之类的奇怪API。如果套接字类型是TCP,您可能做错了什么,因为数据包的顺序总是正确的。它们甚至可能被分割成一个字节大小的块,但顺序将是正确的。这几乎就是TCP的意义所在。
由于您没有提供任何代码,并且基于您的声明,我想最好为您提供一些SSCE来帮助您入门。
该示例在C#中,但应适用于VB.net。检查代码中的注释,看看从哪里可以正确地获取接收到的数据。实现将接收到的数据写入控制台并将其发送回客户端。回送服务器制作了很棒的样本!
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace SaeaSample
{
public class Program
{
static void Main()
{
var server = new Server(new IPEndPoint(IPAddress.Any, 12345));
// ugly sample clients
Parallel.For(0, 4, i =>
{
using (var client = new TcpClient("localhost", 12345))
using (var stream = client.GetStream())
using (var writer = new BinaryWriter(stream))
using (var reader = new BinaryReader(stream))
{
var text = "Hello Async-Server!";
var message = Encoding.UTF8.GetBytes(text);
Console.WriteLine("s: {0}: {1}", i, text);
writer.Write(message);
var roundtrip = reader.ReadBytes(message.Length);
Console.WriteLine("r: {0}: {1}", i, Encoding.UTF8.GetString(roundtrip));
}
});
Console.ReadLine();
}
}
public class Server
{
private const int readBufferSize = 8192;
private const int sendBufferSize = readBufferSize;
// just have a fixed number of clients instead of
// pooling for the sake of being an example
private const int maxClients = 4;
private const int maxQueue = 10;
private readonly byte[] buffer = new byte[maxClients * (readBufferSize + sendBufferSize)];
private readonly Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public Server(IPEndPoint localEndPoint)
{
socket.Bind(localEndPoint);
socket.Listen(maxQueue);
for (int i = 0; i < maxClients; i++)
{
var client = new UserToken(i);
client.RecvArgs.Completed += completed;
client.SendArgs.Completed += completed;
Console.WriteLine("accepting on client slot {0}", client.Slot);
if (!socket.AcceptAsync(client.RecvArgs))
{
completed(this, client.RecvArgs);
}
}
}
private void completed(object sender, SocketAsyncEventArgs e)
{
var client = (UserToken)e.UserToken;
// socket operation had success
if (e.SocketError == SocketError.Success)
{
// new client connected
if (e.LastOperation == SocketAsyncOperation.Accept)
{
onAccept(client);
}
// either send or received worked
else if (e.BytesTransferred > 0)
{
if (e.LastOperation == SocketAsyncOperation.Receive)
{
onReceived(client);
}
else if (e.LastOperation == SocketAsyncOperation.Send)
{
onSend(client);
}
// should never happen, handle gracefully
else
{
onOther(client);
}
}
// don't handle anything else
else
{
onOther(client);
}
}
// socket error occured
else
{
onOther(client);
}
}
private void onAccept(UserToken client)
{
Console.WriteLine("client slot {0} connected client from {1}", client.Slot, client.RecvArgs.AcceptSocket.RemoteEndPoint);
// once accepted, start receiving
client.RecvArgs.SetBuffer(buffer, client.Slot * (readBufferSize + sendBufferSize), readBufferSize);
if (!client.RecvArgs.AcceptSocket.ReceiveAsync(client.RecvArgs))
{
completed(this, client.RecvArgs);
}
}
private void onReceived(UserToken client)
{
// echo whatever we got
var builder = new StringBuilder();
// here is the important part
for (int i = 0; i < client.RecvArgs.BytesTransferred; i++)
{
// offset the buffer and echo in hex
builder.Append(client.RecvArgs.Buffer[client.Slot * (readBufferSize + sendBufferSize) + i].ToString("x2"));
}
Console.WriteLine("received {0} bytes from client slot {1}: {2}", client.RecvArgs.BytesTransferred, client.Slot, builder.ToString());
// send data back ... this is an echo server after all
client.SendArgs.SetBuffer(client.RecvArgs.Buffer, client.Slot * (readBufferSize + sendBufferSize) + readBufferSize, client.RecvArgs.BytesTransferred);
Buffer.BlockCopy(client.RecvArgs.Buffer, client.RecvArgs.Offset, client.SendArgs.Buffer, client.SendArgs.Offset, client.RecvArgs.BytesTransferred);
if (!client.RecvArgs.AcceptSocket.SendAsync(client.SendArgs))
{
completed(this, client.SendArgs);
}
}
private void onSend(UserToken client)
{
Console.WriteLine("sent {0} bytes back to client slot {1}", client.SendArgs.BytesTransferred, client.Slot);
// start receiving again
if (!client.RecvArgs.AcceptSocket.ReceiveAsync(client.RecvArgs))
{
completed(this, client.RecvArgs);
}
}
private void onOther(UserToken client)
{
Console.WriteLine("disconnecting client slot {0}", client.Slot);
// just close the connection and accept again
client.RecvArgs.SetBuffer(null, 0, 0);
if (client.RecvArgs.AcceptSocket != null) {
client.RecvArgs.AcceptSocket.Dispose();
client.RecvArgs.AcceptSocket = null;
}
Console.WriteLine("accepting on client slot {0}", client.Slot);
if (!socket.AcceptAsync(client.RecvArgs))
{
completed(this, client.RecvArgs);
}
}
}
public class UserToken
{
public readonly int Slot;
public readonly SocketAsyncEventArgs RecvArgs = new SocketAsyncEventArgs();
public readonly SocketAsyncEventArgs SendArgs = new SocketAsyncEventArgs();
public UserToken(int slot)
{
Slot = slot;
RecvArgs.UserToken = this;
SendArgs.UserToken = this;
}
}
}还请注意,由于这段代码是异步的,控制台输出可能总是有序的,也可能不是总是有序的。您可以将读和写缓冲区的大小常数从8192降到1。数据包将在两个方向逐字节发送,但肯定仍然是有序的。
对于更深入的解释,MSDN始终是一个很好的起点。
https://stackoverflow.com/questions/26685438
复制相似问题