我想通过async / await创建通过TAP发送Socket消息。
阅读this answer和this one后,我决定制作一个完整的样本:
所以我尝试了什么:
我从here获取了TAP扩展方法(一切正常):我在控制台cmd中测试它:
收件人代码
public static class SocketExtensions
{
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
public static async Task<byte[]> ReceiveExactTaskAsync(this Socket socket, int len)
{
byte[] buf = new byte[len];
int totalRead = 0;
do{
int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead);
if (read <= 0) throw new SocketException();
totalRead += read;
}while (totalRead != buf.Length);
return buf;
}
public static Task ConnectTaskAsync(this Socket socket, string host, int port)
{
return Task.Factory.FromAsync(
socket.BeginConnect(host, port, null, null),
socket.EndConnect);
}
public static Task SendTaskAsync(this Socket socket, byte[] buffer)
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndSend);
}
}
static void Main()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.ConnectTaskAsync("127.0.0.1", 443);
var buf1 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf1.Result));
var buf2 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf2.Result));
Console.ReadLine();
}
发件人代码:
// use same extension method class like above ....^
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
s.ConnectTaskAsync( "127.0.0.1" , 443);
s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
s.Close();
Console.ReadLine();
}
通知我从main中删除了async
,因为我在控制台中测试它。
问题,
根据上面的链接,代码应该正常工作
但是,我没有例外,只是挂在那条线上:
Console.Write(Encoding.UTF8.GetString(buf1.Result));
(首先我运行接收器,然后运行发送器)
我错过了什么?
答案 0 :(得分:4)
问题来自于“通知我从主程序中删除了异步,因为我在控制台中测试它。”。
在执行下一步之前,您需要等待操作完成。您作为示例使用的代码会在每个await
处暂停以完成操作,您的代码将直接执行。
您可以通过在每个具有.Wait()
的操作之后放置await
或通过Task.Run(
,在线程池线程中运行此函数来解决此问题。但我认为最好知道何时应该使用异步,何时不应该。
当你有其他可以让线程做的工作时,应该使用异步,非常普遍的是“其他工作”将是在WinForms项目上处理UI消息或接受新连接的事情。一个ASP.NET站点。在控制台应用程序中,程序在等待时没有其他工作,因此在这种情况下使用函数的同步版本会更合适。
P.S。在我发布“之后我发表了评论,这就是为什么我删除异步等待并使用了Task.result ”,只是因为你知道从来没有永远 1 合并使用await
的代码和阻止同步竞赛的代码(使用Task.Result
或Task.Wait()
等内容,您可能会导致代码死锁并停止运行!
这不是您当前示例的问题,因为控制台应用程序没有同步上下文,但如果您将此代码复制并粘贴到可以轻松锁定程序的内容中。
1:好的,您可以将await
与阻止代码结合起来,但是您需要遵循规则,但如果您足够了解我所说的内容,那么您应该知道我已经足够安全地执行此操作。如果您不知道如何安全地执行此操作,请避免这样做
答案 1 :(得分:1)
因为你没有等待线程完成他们的工作,然后调用s.Close()
套接字将在任何流量发送之前关闭。
您必须删除s.Close()
电话或等待电话完成,例如通过
Task connect = s.ConnectTaskAsync( "127.0.0.1" , 443);
connect.Wait(); // you have to connect before trying to send
Task sendData = s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
sendData.Wait(); // wait for the data to be sent before calling s.Close()
s.Close();
或者您可以在方法中将其打包并等待该方法完成。最终结果是在完成之前的呼叫之前不要呼叫Close
。
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
Task worker = DoWorkAsync(s);
worker.Wait();
s.Close();
Console.ReadLine();
}
private async Task DoWorkAsync(Socket s){
await s.ConnectTaskAsync( "127.0.0.1" , 443);
await s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
}