5

次のコードが機能しない理由を誰かに説明してもらえますか?

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

出力は次のとおりです(ソケットのローカルエンドポイントに注意してください)。

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

もちろん、スレッドを使用せずにBeginConnectを直接呼び出すと、正常に機能します。さらに不可解なのは、十分な長さ(1秒)のThread.Sleepを追加しても問題なく機能することです。何か案は?ありがとう。

4

3 に答える 3

1

別のスレッドと BeginConnect を使用する意味があるのはどれですか? 別のスレッドを作成する場合 (できればスレッド プールを使用)、非同期接続を使用するのはなぜですか (この場合、スレッド プールから別のスレッドが取得されます)。

いくつかのオプションがあります: ThreadPool と 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();
    }
}

別のスレッドなしで BeginConnect を使用します。

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

別のスレッドで BeginConnect を使用します。

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();
        }
    }
}
于 2010-01-19T15:24:17.727 に答える
0

初期スレッドを待っていないため、OS が I/O 要求をキャンセルしている可能性はありますか? 非同期 I/O を開始した元のスレッドが終了すると、Windows は I/O 要求をキャンセルします。

この場合、スレッドから BeginConnect を呼び出し、スレッドを終了させて​​いるため、I/O がキャンセルされています。ここで、開始したスレッドがソケットで Send() を呼び出した時点で実際に終了していない場合、I/O がキャンセルされない状況が発生する可能性があります。

これを本当に機能させたい場合は、次のバリエーションを試すことができます。

       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;
    } 
于 2010-01-20T02:40:20.687 に答える
0

IPEndPoint にはポートが含まれている必要があります。必須であるため、EndPoint がどのようにコンパイルされるかさえわかりません。IPEndAddress の 2 番目のパラメーターとしてポートを指定するか、BeginConnect メソッドを次のように変更できます。

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

...ここで、[ポート] はサーバーのリッスン ポートを表します。

于 2010-01-19T14:48:31.117 に答える