Socket problem when using threading

Zvika picture Zvika · Jan 19, 2010 · Viewed 7k times · Source

Can someone please explain me why the following code doesn't work?

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace SocketThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(delegate()
            {
                BeginConnect(new IPEndPoint("some address"));
            });
            t.Start();

            Console.ReadKey();
        }

        public static void BeginConnect(IPEndPoint address)
        {
            try
            {
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.BeginConnect(address, ConnectCallback, socket);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        private static void ConnectCallback(IAsyncResult ar)
        {
            Socket sock = (Socket)ar.AsyncState;
            try
            {
                sock.EndConnect(ar);
                Console.WriteLine("Connected {0}", sock.LocalEndPoint);

                sock.Send(Encoding.UTF8.GetBytes("Hello"));

                Console.WriteLine("success");
                sock.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("send ex " + ex);

                if (sock != null)
                    sock.Close();
            }
        }
    }
}

The output is (note the local end point of the socket):

Connected 0.0.0.0:28142
send ex System.Net.Sockets.SocketException: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram
 socket using a sendto call) no address was supplied
   at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, So
cketFlags socketFlags)
   at System.Net.Sockets.Socket.Send(Byte[] buffer)
   at SocketThreadingTest.Program.ConnectCallback(IAsyncResult ar) in Program.cs:line 44

Of course when I don't use a thread and call BeginConnect directly it works fine. What's even more puzzling is that adding a Thread.Sleep that is long enough (1 sec) it also works fine. Any ideas? Thanks.

Answer

Sergey Teplyakov picture Sergey Teplyakov · Jan 19, 2010

Which makes sense to use separate Thread and BeginConnect? If you creating separate thread (with Thread pool preferably) why are you using asynchronous connection (in this case separate thread will be taken from the thread pool)?

There are several options: Use ThreadPool and Socket.Connect

class Program {

    static void Connect(object o)
    {
        IPEndPoint address = (IPEndPoint)o;
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Connect(address);
        Console.WriteLine("Connected {0}", socket.LocalEndPoint);
        socket.Send(Encoding.UTF8.GetBytes("Hello"));
        Console.WriteLine("success");
        socket.Close();
    }

    static void Main(string[] args)
    {
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 5111);
        ThreadPool.QueueUserWorkItem(Connect, endPoint);
        Console.ReadKey();
    }
}

Use BeginConnect without separate thread.

class Program {

static void Main(string[] args)
{
    BeginConnect(new IPEndPoint(IPAddress.Loopback, 5111));
    Console.ReadKey();
}

public static void BeginConnect(IPEndPoint address)
{
    try
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.BeginConnect(address, ConnectCallback, socket);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

private static void ConnectCallback(IAsyncResult ar)
{
    Socket sock = (Socket)ar.AsyncState;
    try
    {
        sock.EndConnect(ar);
        Console.WriteLine("Connected {0}", sock.LocalEndPoint);
        sock.Send(Encoding.UTF8.GetBytes("Hello"));
        Console.WriteLine("success");
        sock.Close();
    }
    catch (Exception ex)
    {
        Console.WriteLine("send ex " + ex);
        if (sock != null)
            sock.Close();
    }
}
}

Use BeginConnect with separate thread:

class Program
{

    static void Main(string[] args)
    {
        Thread t = new Thread(delegate()
        {
            BeginConnect(new IPEndPoint(IPAddress.Loopback, 5111));
        });
        t.Start();
        Console.ReadKey();
    }

    public static void BeginConnect(IPEndPoint address)
    {
        try
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.BeginConnect(address, ConnectCallback, socket);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    private static void ConnectCallback(IAsyncResult ar)
    {
        Socket sock = (Socket)ar.AsyncState;
        try
        {
            sock.EndConnect(ar);
            Console.WriteLine("Connected {0}", sock.LocalEndPoint);
            sock.Send(Encoding.UTF8.GetBytes("Hello"));
            Console.WriteLine("success");
            sock.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("send ex " + ex);
            if (sock != null)
                sock.Close();
        }
    }
}