我目前有一个涉及 Silverlight 客户端和 .NET 服务器的客户端-服务器应用程序。.NET 部分使用 System.Net.Sockets 命名空间中提供的 Tcp 类,而 Silverlight 客户端使用原始套接字。我从当前使用 HttpListener 类的代码移植到这个,因为它不适合我的需要。不过,Http 类在 SL 方面具有使用 Begin* 和 End* 样式的异步方法的能力,这些方法允许我在操作完成后指定处理程序。我无法让它与新系统一起使用。我目前的策略是将处理程序包含在 UserToken 中。但是,这个令牌似乎没有得到更新。
这是一些经过编辑的代码。我能够让双方相互交谈,但似乎没有发送正确的 UserToken。
public class ClientUserToken
{
public Handler Handler { get; set; }
public string Test { get; set; }
public ClientUserToken(Handler handler, string test)
{
Handler = handler;
Test = test;
}
}
public class SocketClient
{
private Socket _clientSocket;
private string _ipAddress;
private int _port;
private void OpenSocket()
{
var endPoint = new DnsEndPoint(_ipAddress, _port);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(null, "foo");
args.RemoteEndPoint = endPoint;
args.Completed += OnSocketCompleted;
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.ConnectAsync(args);
}
void OnSocketCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect: ProcessConnect(e); break;
case SocketAsyncOperation.Receive: ProcessReceive(e); break;
case SocketAsyncOperation.Send: ProcessSend(e); break; // this never gets called
}
}
void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
byte[] response = new byte[1024];
e.SetBuffer(response, 0, response.Length);
_clientSocket.ReceiveAsync(e);
}
}
void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
var userToken = e.UserToken as ClientUserToken; // this token is always the one set in ProcessConnect
// process the data
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is never called, for some reason
void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var userToken = e.UserToken as ClientUserToken;
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is the public API that users use to actually send data across
public void SendToServer(byte[] data, int len, Handler handler)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(handler, "bar");
args.SetBuffer(data, 0, len);
if (!_clientSocket.SendAsync(args))
{
ProcessReceive(args);
}
}
}
正如上面的评论所暗示的,在 ProcessReceive 中,userToken.Test 始终为“foo”,而 userToken.Handler 始终为 null。
到目前为止,我还无法进入 ProcessSend,所以我看不到它实际发送了什么 SocketAsyncEventArgs。任何人都知道为什么这个事件没有触发(发送后完成)?我搞砸了我的 SendToServer 功能吗?
我意识到同步等可能存在其他问题,但我认为这不是问题所在。我尝试过的一件事是设置 ManualResetEvent 以确保在连接完成(ProcessConnect)之前没有人向服务器发送数据,但这也没有解决问题。
任何帮助将不胜感激!
编辑:所以发生这种情况的原因是因为当我在 ProcessConnect 函数中调用 ReceiveAsync 时,它在服务器发送回我的数据的响应时被使用。因此,处理程序中存在 UserToken “foo”。下次服务器发送数据时,ReceiveAsync 使用带有 UserToken “bar”的参数。所以它有点不同步,对于双工通信位。我无法确保从客户端发送的 SocketAsyncEventArgs 与响应中使用的相同。似乎唯一的解决方案是让 SL 客户端打开两个套接字——一个用于服务器启动的数据,另一个用于客户端启动的请求。但是,这意味着我没有利用双工性质。