1

I want to create Socket message sending via TAP via async/await.

After reading this answer and this one - I decided to create a fully working sample :

So what have I tried :

I took the TAP extenstion methods from here (all ok) : and I test it in console cmd :

Reciever Code

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();
}

Sender Code :

// 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();
    }

notice I removed the async from main since im testing it in console.

Question ,

According to link above , the code should work

However I'm getting no exception and it's just hangs on that line :

Console.Write(Encoding.UTF8.GetString(buf1.Result));

(First I run receiver , then I run sender)

What am I missing?

Community
  • 1
  • 1
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • Does it work with `127.0.0.1`? Are you sure that there is no firewall/router blocking the traffic to your ipaddress? – default Mar 26 '14 at 13:38
  • @Default this is my own ip , just like 127.0.0.1 ( im calling to myself) – Royi Namir Mar 26 '14 at 13:38
  • @Default tested also with 127.0.0.1 ....still not working. ( also updated in Question) – Royi Namir Mar 26 '14 at 13:40
  • You could download Wireshark and see if there is any traffic going out. Another tip is TcpView from Sysinternals which shows open ports and which program opens them. – default Mar 26 '14 at 13:41
  • I have a simplier code which does work but it doesnt use tap , async/await. so there is nothing wrong with it. I must be missing something with the Task<>. – Royi Namir Mar 26 '14 at 13:42
  • well, you don't wait for the threads, and then you do `s.Close`. I think you need to add `.Wait()` on the async calls so that the code doesn't continue and Close the socket (notice that in the code you link to he does `await` which will wait for the result to be available) – default Mar 26 '14 at 13:46
  • @Default Im using console. adding `static async void Main()` and awaits will return immediatly. ( that's why I remove the async awaits and used Task.result – Royi Namir Mar 26 '14 at 13:51
  • @RoyiNamir Default says `.Wait()` not `await`... – L.B Mar 26 '14 at 18:13

2 Answers2

4

the problem comes from the "notice I removed the async from main since im testing it in console.".

You need to wait for the operation to complete before doing the next step. The code you used as an example pauses at each await for the operation to complete, your code just goes straight through.

You may be able to fix this by putting a .Wait() after each operation that would have had a await or by running this function inside a threadpool thread via Task.Run(, however I think it is better to know when you should use async and when you should not.

Async should be used when you have other work you could have the thread be doing, very commonly that "other work" will be things like processing UI messages on a WinForms project or accepting new connections on a ASP.NET site. In a console application there is no other work your program could be doing while it waits, so in that situation it would be more appropriate to use the synchronous version of the functions instead.


P.S. You made the comment after I posted "that's why I remove the async awaits and used Task.result", just so you know never ever1 combine code that uses await and code that blocks the synchronization contest (by using things like Task.Result or Task.Wait(), you will likely cause your code to deadlock and stop functioning!

It is not a issue for your current example because console applications do not have a synchronization context, but if you copied and pasted this code to something that did you could easily lock up your program.

1: Ok, you could combine await and blocking code but there are rules you need to follow, but if you know enough to dispute my what I am saying you know enough to safely do it. If you don't know how to safely do it just avoid doing it

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • I tried applying await , but the code still hangs in http://i.stack.imgur.com/VjHvi.jpg . – Royi Namir Mar 26 '14 at 14:05
  • 1
    @RoyiNamir I would change your client to not use any functions that end in `Async` and check that you can make it work, once you get it working then try to make it async, you likely have a bug in how you wrote it that has nothing to do with `async` but making it `async` has made it harder to track down the problem. – Scott Chamberlain Mar 26 '14 at 14:08
1

since you do not wait for the threads to do their work and then call s.Close() the socket will be closed before any traffic is sent out. You would have to either remove the s.Close() call or wait until the calls are complete, for instance via

 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();

or you could box it in a method and Wait for that method to complete. The end result is to not call Close before completing the previous calls.

 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"));
}
default
  • 11,485
  • 9
  • 66
  • 102
  • I changed the code in the sender side to yours , but still reciever shows nothing..., can u please supply the fix for the reciever ? – Royi Namir Mar 26 '14 at 13:59
  • well, you would have to wait for the reciever `Connect` to complete the same way as well before trying to send anything. So `s.ConnectTaskAsync("127.0.0.1", 443).Wait();` would have to exist for the reciever as well. – default Mar 26 '14 at 14:01
  • I copied your code and actually I don't understand how it would work. Both the reciever and sender is trying to `Connect`. No one is listening. Are you sure that your working example worked with the provided code? Both my receiver and sender throw exceptions when waiting for the `ConnectTaskAsync` call, because there is no listener – default Mar 26 '14 at 14:13
  • I saw it [here](http://stackoverflow.com/questions/21539766/receive-correct-amount-of-data-tcp/21540278#21540278) and I know that both has to connect to the socket. so... – Royi Namir Mar 26 '14 at 14:15
  • p.s. im in linqpad and i dont see any exceptions – Royi Namir Mar 26 '14 at 14:16
  • wait, do you have a web server running which accepts https? port 443 is usually used for https traffic. I think waiting for > 100 bytes will lock because your web server isn't sending back 100 bytes. – default Mar 26 '14 at 14:16
  • well, no. both does not have to connect to the socket because there is no socket created. one has to create it, via a `listener` and the other has to `connect` to it. L.B. is missing that detail. since there is no [`Begin|Accept`](http://msdn.microsoft.com/en-us/library/5bb431f9%28v=vs.110%29.aspx) no connection will ever be created. – default Mar 26 '14 at 14:20
  • Is there _any_ way you can make this code works ? Im dealing with it for 6 hours. – Royi Namir Mar 26 '14 at 14:22
  • P.s. this is - http://i.stack.imgur.com/NVwrb.jpg - the regular non tap - non async code which is working . first screen is reciever running , second code is the sender ( notice ip) , and after sending , you see the result in the third screen - so allim trying to do is convert it to TAP async await. (linqpad) – Royi Namir Mar 26 '14 at 14:32
  • oh.. it seems that you are using UDP in your examples. They differ a lot from the code that you have tried to copy which uses TCP. Those are very different protocols. TCP **does** require a listener, UDP does not. UDP does however not care if the packets arrive or not which TCP does. Could you describe your actual problem a bit more, i.e. are you trying to send UDP packages async / trying to setup a TCP socket? I don't think that the code you copied is ideal to start from if you are to use UDP. – default Mar 26 '14 at 15:02