有人可以解释一下为什么以下代码不起作用吗?
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它也可以正常工作。 有任何想法吗? 感谢。
答案 0 :(得分:1)
使用单独的Thread和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();
}
}
}
答案 1 :(得分:0)
您的IPEndPoint应该包含一个端口 - 我甚至不确定您的EndPoint将如何编译,因为它是必需的。您可以将端口作为IPEndAddress的第二个参数提供,或者按如下方式修改BeginConnect方法:
socket.BeginConnect(address, [port], ConnectCallback, socket);
...其中[port]代表服务器上的监听端口。
答案 2 :(得分:0)
是否可能因为您没有等待初始线程,操作系统正在取消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;
}