本文介绍了异步套接字和线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,


使用异步套接字我遇到了一个奇怪的问题,在某些机器上只发生了
。我写了一个小的演示应用程序,可用于
重现问题。您可以从这里下载:


如果按连接新线程,来自测试的按钮

应用程序您可能会导致System.IO.IOException:Unable

将数据写入传输连接。而没有新线程的另一个按钮

执行相同的代码工作正常。


异步BeginWrite在

套接字。因此套接字必须连接,而异常告诉我

不是。

我已调试并研究了我的代码几个小时但却找不到问题。

我有什么问题吗?


如果你可以运行代码告诉我你是否可以导致我会很好/>
例外与否,以及您正在运行的OS和Framework版本。


谢谢,

Alex

Hello,

while using async sockets I ran into a strange problem which occurs only
on some machines. I wrote a small demo application which can be used to
reproduce the problem. You can download it from here:
http://alex.ag-software.de/SocketTest.zip

If you press the "Connect in new Thread" button from the test
application you may be able to cause the System.IO.IOException: Unable
to write data to the transport connection. While the other button which
executes the same code without a new thread works fine.

The async BeginWrite gets executed in the async EndConnect of the
socket. So the socket must be connected while the exception tells me it
isn''t.
I''ve debugged and studied my code for hours but can''t find the problem.
Is there anything I am doing wrong?

I would be great if you can run the code and tell me if you can cause
the exception or not, and which OS and Framework version you are running.

Thanks,
Alex

推荐答案



请在此处发布简洁但完整的代码示例。为了存档目的,

代码不仅应该伴随您的问题(这个帖子将由许多提供商存档,但是你的代码样本不会是你的代码样本

只在一个单独的Web服务器上提供它),事实是,如果你的代码

足够简洁,是一个有效的例子,它就足够简洁了

包含在您的帖子中。如果你觉得用你的

问题发帖太长了,那么成为一个真正简洁但完整的代码样本太长了。


我应该指出,仅仅因为你的连接回调已经被调用了,这并不一定意味着你的套接字已被连接。它

只意味着操作已经完成。你需要调用

EndConnect()并且只有在没有异常的情况下继续写入套接字

发生并且你实际上回到了一个有效的套接字。


根据你的描述,_seems_很可能这一切都很好(在

之后,如果不是
返回的那个,你会写什么插座?
EndConnect()?),但是如果没有实际代码,则无法确定

肯定。


Pete

Please post a concise-but-complete code sample here. Not only should the
code accompany your question for archival purposes (this thread will be
archived by a number of providers, but your code sample won''t be if you
only provide it at a separate web server), the fact is that if your code
is concise enough to be a valid example, it''s concise enough to be
included in your post. If you think it''s too long to post with your
question, then it''s too long to be a true concise-but-complete code sample.

I should point out that just because your connect callback has been
called, that doesn''t necessarily mean your socket has been connected. It
just means that the operation has been completed. You need to call
EndConnect() and only proceed with writing to the socket if no exception
occurs and you do in fact get back a valid socket.

Based on your description, it _seems_ likely that this is all fine (after
all, to what socket would you be writing if not the one returned by
EndConnect()?), but without the actual code it''s not possible to say for
sure.

Pete




ok,这是一个例子:


使用System;

使用System.IO ;

使用System.Text;


使用System.Threading;

使用System.Net;

使用System.Net.Sockets;


命名空间测试

{

class program

{

static void Main(string [] args)

{

Thread connectThread = new Thread(new

ParameterizedThreadStart(ConnectThread));

connectThread.Start();


Console.ReadLine();

}


static void ConnectThread(对象o)

{

协议p =新协议();

p .Open();

}


公共类协议

{

private static Socket _socket ;

私有静态NetworkStream _stream;

private const int BUFFERSIZE = 10240;

private static byte [] m_ReadBuffer = new byte [BUFFERSIZE];


公共礼议()

{

}


public void Open()

{

IPHostEntry ipHostInfo =

Dns.GetHostEntry(" www.google.com");


IPAddress ipAddress = ipHostInfo.AddressList [0];

IPEndPoint endPoint =新IPEndPoint(ipAddress,80);


_socket = new Socket(AddressFamily.InterNetwork,

SocketType.Stream,ProtocolType.Tcp);

_socket.BeginConnect(endPoint,new

AsyncCallback(EndConnect),null);

}


private static void EndConnect(IAsyncResult ar)

{

try

{

_socket.EndConnect(ar);

_stream = new NetworkStream(_socket,false);


string header =" GET / HTTP / 1.1 \\ nn" ;;

header + ="接受:* / * \\ nn" ;;

header + =" User-Agent:myAgent\r\\\
" ;;

header + =" Host:www.google.de\r \\ n \\ n" ;;


发送(Encoding.UTF8.GetBytes(标题));

}

catch(Exception ex)

{

Console.WriteLine(ex.Message);

}

}


public static void发送(byte [] bData)

{

尝试

{

_stream.BeginWrite(bData,0,bData.Length,new

AsyncCallback(EndSend),null); //<< ==异常在这里被提出

}

catch(例外情况)

{

Console.WriteLine(ex.Message);

}

}


private static void EndSend(IAsyncResult ar)

{

_stream.EndWrite(ar);

}

}

}

}

ok, here is the example:

using System;
using System.IO;
using System.Text;

using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace Test
{
class Program
{
static void Main(string[] args)
{
Thread connectThread = new Thread(new
ParameterizedThreadStart(ConnectThread));
connectThread.Start();

Console.ReadLine();
}

static void ConnectThread(object o)
{
Protocol p = new Protocol();
p.Open();
}

public class Protocol
{
private static Socket _socket;
private static NetworkStream _stream;
private const int BUFFERSIZE = 10240;
private static byte[] m_ReadBuffer = new byte[BUFFERSIZE];

public Protocol()
{
}

public void Open()
{
IPHostEntry ipHostInfo =
Dns.GetHostEntry("www.google.com");

IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddress, 80);

_socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
_socket.BeginConnect(endPoint, new
AsyncCallback(EndConnect), null);
}

private static void EndConnect(IAsyncResult ar)
{
try
{
_socket.EndConnect(ar);
_stream = new NetworkStream(_socket, false);

string header = "GET / HTTP/1.1\r\n";
header += "Accept: */*\r\n";
header += "User-Agent: myAgent\r\n";
header += "Host: www.google.de\r\n\r\n";

Send(Encoding.UTF8.GetBytes(header));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

public static void Send(byte[] bData)
{
try
{
_stream.BeginWrite(bData, 0, bData.Length, new
AsyncCallback(EndSend), null); // <<== exception gets raised here
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

private static void EndSend(IAsyncResult ar)
{
_stream.EndWrite(ar);
}
}
}
}



我在EndConnect中没有例外。在EndConnect中我还创建了

网络流,因为我必须使用流来在以后的连接上启动TLS

安全层。


Alex

I get no exception in EndConnect. In EndConnect I also create the
network stream because I have to work with streams to start the TLS
security layer on the connection later.

Alex




ok,这是示例:[...]


ok, here is the example: [...]



感谢您的示例。有了它,它看起来好像

问题与垃圾收集有关。特别是,似乎JIT编译器发现,一旦你开始退出的线程,你就不会引用_socket或

_stream类成员了。

有资格进行垃圾收集的成员。当然,当那个

发生时(好吧,一旦终结器运行),连接就会关闭。


我添加了一些同步来确保在执行EndSend()回调之前,Open()方法不会返回
,并且可靠地

会使问题消失。这似乎证实了GC是罪魁祸首。


现在,无论是'_rectrect_行为,我都不完全确定。它_seems_

对我来说就像一个.NET错误。特别是那些是静态成员,我认为所有类静态成员都会被认为是垃圾收集目的的根。事实上,关于.NET垃圾收集的权威

文章之一似乎只是说:应用程序中的所有

全局和静态对象指针都被认为是应用程序根的一部分。 (来自



所以,至少,对于这个代码示例,在我看来,GC

过早收集对象。如果你的真实场景也是

将对象保存在静态成员中,那么同样的bug会影响你的b $ b $

当然,如果在您的真实场景中,这些对象最终会被类实例中的实例成员引用,而这些实例成员本身因为使用而无法访问
的线程,然后_that_将是b $ b预期的行为。当然,修复将保存一个引用

到存储套接字和流实例的实例,这样它们就可以保持可访问和未收集状态。


在任何一种情况下,我认为基于静态的行为版本是一个

错误,如果您要发布错误报告,这将非常有用
网站, .NET Framework。


Pete

Thanks for the example. With that in hand, it looks to me as though the
problem is related to garbage collection. In particular, it appears that
the JIT compiler figures out that you don''t refer to the _socket or
_stream class members once the thread you started exits, and it makes
those members eligible for garbage collection. And of course, when that
happens (well, once the finalizer is run), the connection winds up closed.

I added some synchronization to ensure that the Open() method doesn''t
return until the EndSend() callback has been executed, and that reliably
makes the problem go away. That seems to confirm the GC as the culprit.

Now, whether that''s _correct_ behavior, I''m not entirely sure. It _seems_
like a .NET bug to me. In particular, those are static members, and I
would have thought all class static members would be considered a root for
the purpose of garbage collection. In fact, one of the authoritative
articles on .NET garbage collection seems to say just that: " all the
global and static object pointers in an application are considered part of
the application''s roots" (from
http://msdn.microsoft.com/en-us/magazine/bb985010.aspx).

So, at the very least, for this code example, it seems to me that the GC
is prematurely collecting the objects. If your real-world scenario also
keeps the objects in static members, then the same bug would be affecting
you.

Of course, if in your real-world scenario, these objects wind up
referenced by instance members that are in a class instance that itself
becomes unreachable due to the use of the thread, then _that_ would be
expected behavior. The fix there would be, of course, to keep a reference
to the instance storing the socket and stream instances, so that they
remain reachable and uncollected.

In either case, I think that the static-based version of the behavior is a
bug, and it would be very helpful if you would post a bug report to the
http://connect.microsoft.com/ web site, for the .NET Framework.

Pete


这篇关于异步套接字和线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-06 04:01