我正在开发一个云服务(工作者角色),用于从许多工具中收集数据。这些仪器每分钟左右随机报告数据。服务本身不是性能关键,不需要是异步的。在失败的连接尝试时,这些仪器能够将数据重新发送一个小时。
我已经为我的云服务尝试了几种实现,包括这个:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop(v=vs.110).aspx
但他们迟早都会挂起我的云端服务器(有时会在一小时内)。 我怀疑我的代码有问题。我的代码中有很多日志记录,但我没有错误。该服务只是停止接收传入连接。
在Azure门户中,似乎服务运行正常。没有错误日志,没有可疑的CPU使用等。
如果我重新启动服务,它将再次正常运行,直到下次挂起。
如果有人能帮助我,我将不胜感激。
public class WorkerRole : RoleEntryPoint
{
private LoggingService _loggingService;
public override void Run()
{
_loggingService = new LoggingService();
StartListeningForIncommingTCPConnections();
}
private void StartListeningForIncommingTCPConnections()
{
TcpListener listener = null;
try
{
listener = new TcpListener(RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["WatchMeEndpoint"].IPEndpoint);
listener.Start();
while (true)
{
_loggingService.Log(SeverityLevel.Info, "Waiting for connection...");
var client = listener.AcceptTcpClient();
var remoteEndPoint = client.Client != null ? client.Client.RemoteEndPoint.ToString() : "Unknown";
_loggingService.Log(SeverityLevel.Info, String.Format("Connected to {0}", remoteEndPoint));
var netStream = client.GetStream();
var data = String.Empty;
using (var reader = new StreamReader(netStream, Encoding.ASCII))
{
data = reader.ReadToEnd();
}
_loggingService.Log(SeverityLevel.Info, "Received data: " + data);
ProcessData(data); //data is processed and stored in database (all resources are released when done)
client.Close();
_loggingService.Log(SeverityLevel.Info, String.Format("Connection closed for {0}", remoteEndPoint));
}
}
catch (Exception exception)
{
_loggingService.Log(SeverityLevel.Error, exception.Message);
}
finally
{
if (listener != null)
listener.Stop();
}
}
private void ProcessData(String data)
{
try
{
var processor = new Processor();
var lines = data.Split('\n');
foreach (var line in lines)
processor.ProcessLine(line);
processor.ProcessMessage();
}
catch (Exception ex)
{
_loggingService.Log(SeverityLevel.Error, ex.Message);
throw new Exception(ex.InnerException.Message);
}
}
}
我刚才做了一个奇怪的观察:
我最近检查了日志,过去30分钟没有连接任何仪器(表明服务已关闭)。
我自己通过TCP客户端连接到服务,我自己编写并上传了一些测试数据。
这很好。
当我再次检查日志时,我的测试数据已经存储。
奇怪的是,其他4个仪器大约在同一时间连接并成功发送数据。
为什么在我与测试客户端连接之前他们无法自行连接?
另外,.csdef中的这个设置对于InputEndpoint,idleTimeoutInMinutes做了什么?
===============================================
编辑:
自从几天前,我的云服务已成功运行。
不幸的是,今天上午的最后一个日志条目来自这一行:
_loggingService.Log(SeverityLevel.Info, String.Format("Connected to {0}", remoteEndPoint));
此后无法建立其他连接。甚至不是我自己的测试TCP客户端(虽然没有收到任何错误,但没有存储数据,也没有新的日志)。
这让我觉得以下代码导致服务挂起:
var netStream = client.GetStream();
var data = String.Empty;
using (var reader = new StreamReader(netStream, Encoding.ASCII))
{
data = reader.ReadToEnd();
}
我读过StremReader的ReadToEnd()可以挂起的地方。这可能吗?
我现在已经将这段代码更改为:
int i;
var bytes = new Byte[256];
var data = new StringBuilder();
const int dataLimit = 10;
var dataCount = 0;
while ((i = netStream.Read(bytes, 0, bytes.Length)) != 0)
{
data.Append(Encoding.ASCII.GetString(bytes, 0, i));
if (dataCount >= dataLimit)
{
_loggingService.Log(SeverityLevel.Error, "Reached data limit");
break;
}
dataCount++;
}
另一种解释可能是数据库中出现的问题。我使用SqlConnection和SqlCommand类来读取和写入我的数据库。之后我总是关闭我的连接(最后阻止)。
SqlConnection和SqlCommand应该有默认超时,对吗?
===============================================
编辑:
经过一些调试后,我发现当服务没有响应时,它就“挂了”这行代码:
while ((i = netStream.Read(bytes, 0, bytes.Length)) != 0)
经过一番挖掘后,我发现NetStream类及其读取方法实际上可能会挂起。即使MS另有说明。
我现在已将代码更改为:
Thread thread = null;
var task = Task.Factory.StartNew(() =>
{
thread = Thread.CurrentThread;
while ((i = netStream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data.Append(Encoding.ASCII.GetString(bytes, 0, i));
}
streamReadSucceeded = true;
});
task.Wait(5000);
if (streamReadSucceeded)
{
//Process data
}
else
{
thread.Abort();
}
希望这会阻止悬挂。
答案 0 :(得分:0)
我会说你问题的一部分是你在侦听来自客户端的连接的线程上处理你的数据。如果另一个客户端已启动某种类型的长时间运行操作,这将阻止新客户端连接。我建议你将处理推迟到工作线程,从而释放“监听器”线程以接受新的连接。
您可能遇到的另一个问题是,如果您的服务引发错误,那么该服务也将停止接受连接。
private static void ListenForClients()
{
tcpListener.Start();
while (true)
{
TcpClient client = tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private static void HandleClientComm(object obj)
{
try
{
using(TcpClient tcpClient = (TcpClient)obj)
{
Console.WriteLine("Got Client...");
using (NetworkStream clientStream = tcpClient.GetStream())
using (StreamWriter writer = new StreamWriter(clientStream))
using(StreamReader reader = new StreamReader(clientStream))
{
//do stuff
}
}
}
catch(Exception ex)
{
}
}