English 中文(简体)
Socket problem when using threading
原标题:

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.

问题回答

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

Your IPEndPoint should contain a port -- I m not even sure how your EndPoint will compile, as it s required. You can supply the port as the second parameter to your IPEndAddress or modify your BeginConnect method as follows:

 socket.BeginConnect(address, [port], ConnectCallback, socket); 

...where [port] represents the listening port on the Server.

is it possible that because you are not waiting for the initial thread, the OS is cancelling the I/O request? Windows will cancel the I/O request if the original thread that started the async I/O dies.

In this case, you are calling BeginConnect from a thread, and letting the thread die, so the I/O is getting cancelled. Now, there might be some situation where the I/O might not get cancelled if the thread you started did not actually die by the time you called Send() on the socket.

If you really want this to work, you could try the following variation:

       static void Main(string[] args) 
    { 
        Thread t = new Thread(delegate() 
        { 
            IAsyncResult ar = BeginConnect(new IPEndPoint("some address")); 
            // wait for the async connect to finish.
            ar.WaitOne();
        }); 
        t.Start(); 

        Console.ReadKey(); 
    } 

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




相关问题
Manually implementing high performance algorithms in .NET

As a learning experience I recently tried implementing Quicksort with 3 way partitioning in C#. Apart from needing to add an extra range check on the left/right variables before the recursive call, ...

Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

How do I compare two decimals to 10 decimal places?

I m using decimal type (.net), and I want to see if two numbers are equal. But I only want to be accurate to 10 decimal places. For example take these three numbers. I want them all to be equal. 0....

Exception practices when creating a SynchronizationContext?

I m creating an STA version of the SynchronizationContext for use in Windows Workflow 4.0. I m wondering what to do about exceptions when Post-ing callbacks. The SynchronizationContext can be used ...

Show running instance in single instance application

I am building an application with C#. I managed to turn this into a single instance application by checking if the same process is already running. Process[] pname = Process.GetProcessesByName("...

How to combine DataTrigger and EventTrigger?

NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger? I think I need to combine an EventTrigger and a DataTrigger to achieve what I m after: when ...

热门标签