在使用TCP创建远程控制cisco路由器的c#应用程序时,我遇到了等待路由器响应的问题。
对于应用程序,我必须使用TCP连接连接到Cisco路由器。建立连接后,网络流将把我的命令推送到Cisco路由器。要让路由器处理命令,我使用的是Thread.Sleep
。这不是最佳解决方案。
这是完整的代码,可以了解我的程序正在做什么。
string IPAddress = "192.168.1.1";
string message = "show running-config"; // command to run
int bytes;
string response = "";
byte[] responseInBytes = new byte[4096];
var client = new TcpClient();
client.ConnectAsync(IPAddress, 23).Wait(TimeSpan.FromSeconds(2));
if (client.Connected == true)
{
client.ReceiveTimeout = 3;
client.SendTimeout = 3;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
Console.WriteLine();
stream.Write(messageInBytes, 0, messageInBytes.Count()); //send data to router
Thread.Sleep(50); // temporary way to let the router fill his tcp response
bytes = stream.Read(responseInBytes, 0, responseInBytes.Length);
response = Encoding.ASCII.GetString(responseInBytes, 0, bytes);
return response; //whole command output
}
return null;
获得完整响应的好方法是什么? 感谢您的帮助或指挥。
更多信息:
网络团队总是充满了某些东西,大多数时候它充满了cisco IOS登录页面。最大的问题是确定路由器何时完成填充响应。 我大部分时间都得到了回应:
“?? \ u0001 ?? \ u0003 ?? \ u0018 \ u001f \ r \ n \ r \ n用户访问验证\ r \ n \ r \ n用户名:”
每次返回数据都是不同的,因为它是cisco命令的结果。这可以从短字符串到非常长的字符串。
答案 0 :(得分:1)
使用NetworkStream
从Stream.Read
进行阅读时,不能100%确定您将阅读所有预期数据。当只有少量数据包到达而不等待其他数据包时,Stream.Read
可以返回。
确保所有数据都使用BinaryReader
进行阅读
BinaryReader.Read
方法将阻止当前线程,直到所有预期数据到达
private string GetResponse(string message)
{
const int RESPONSE_LENGTH = 4096;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
bool leaveStreamOpen = true;
using(var writer = new BinaryWriter(client.GetStream()))
{
writer.Write(messageInBytes);
}
using(var reader = New BinaryReader(client.GetStream()))
{
byte[] bytes = reader.Read(RESPONSE_LENGTH );
return Encoding.ASCII.GetString(bytes);
}
}
答案 1 :(得分:0)
不要使用Thread.Sleep。我会async/await
整个事情,因为您根本不知道基于最近编辑的数据是什么。我就是这样做的(未经测试):
public class Program
{
// call Foo write with "show running-config"
}
public class Foo
{
private TcpClient _client;
private ConcurrentQueue<string> _responses;
private Task _continualRead;
private CancellationTokenSource _readCancellation;
public Foo()
{
this._responses = new ConcurrentQueue<string>();
this._readCancellation = new CancellationTokenSource();
this._continualRead = Task.Factory.StartNew(this.ContinualReadOperation, this._readCancellation.Token, this._readCancellation.Token);
}
public async Task<bool> Connect(string ip)
{
this._client = new TcpClient
{
ReceiveTimeout = 3, // probably shouldn't be 3ms.
SendTimeout = 3 // ^
};
int timeout = 1000;
return await this.AwaitTimeoutTask(this._client.ConnectAsync(ip, 23), timeout);
}
public async void StreamWrite(string message)
{
var messageBytes = Encoding.ASCII.GetBytes(message);
var stream = this._client.GetStream();
if (await this.AwaitTimeoutTask(stream.WriteAsync(messageBytes, 0, messageBytes.Length), 1000))
{
//write success
}
else
{
//write failure.
}
}
public async void ContinualReadOperation(object state)
{
var token = (CancellationToken)state;
var stream = this._client.GetStream();
var byteBuffer = new byte[4096];
while (!token.IsCancellationRequested)
{
int bytesLastRead = 0;
if (stream.DataAvailable)
{
bytesLastRead = await stream.ReadAsync(byteBuffer, 0, byteBuffer.Length, token);
}
if (bytesLastRead > 0)
{
var response = Encoding.ASCII.GetString(byteBuffer, 0, bytesLastRead);
this._responses.Enqueue(response);
}
}
}
private async Task<bool> AwaitTimeoutTask(Task task, int timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
public void GetResponses()
{
//Do a TryDequeue etc... on this._responses.
}
}
我没有公开公开读取取消,但您可以添加此方法来取消读取操作:
public void Cancel()
{
this._readCancellation.Cancel();
}
然后处理你的客户和所有有趣的东西。
最后,因为你说流上总是有数据,你在那里进行读取,你可能必须对上次读取的字节数做一些逻辑,以便在流中自行抵消数据不清楚。您将知道您获得的回复是否始终相同。
答案 2 :(得分:0)
这是我的工作代码。 它使用Fabio的解决方案, 如果响应已经改变,则结合while循环检查每X毫秒。
client.ReceiveTimeout = 3;
client.SendTimeout = 3;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
Console.WriteLine();
using (var writer = new BinaryWriter(client.GetStream(),Encoding.ASCII,true))
{
writer.Write(messageInBytes);
}
using (var reader = new BinaryReader(client.GetStream(),Encoding.ASCII, true))
{
while (itIsTheEnd == false)
{
bytes = reader.Read(responseInBytes, 0, responseInBytes.Count());
if (lastBytesArray == responseInBytes)
{
itIsTheEnd = true;
}
lastBytesArray = responseInBytes;
Thread.Sleep(15);
}
}
response = Encoding.ASCII.GetString(responseInBytes);
感谢所有建议解决方案的人。 并且感谢给定解决方案的Fabio。