Implemented session layer as raw TCP client.

* NitroDebugger/GdbClient.cs:
* NitroDebugger/RSP/Presentation.cs:
* NitroDebugger/NitroDebugger.csproj:
  Refactoring to Presentation layer

* NitroDebugger.UnitTests/SessionTests.cs:
  Created test for Session layer (TCP socket)

* NitroDebugger/RSP/Session.cs:
  Implemented session layer as raw TCP client.
This commit is contained in:
Benito Palacios Sánchez 2014-12-07 18:56:17 +01:00
parent 0ca8a61353
commit 49705ea722
5 changed files with 259 additions and 66 deletions

View File

@ -21,6 +21,7 @@
using NUnit.Framework;
using System;
using NitroDebugger.RSP;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization.Formatters;
@ -31,6 +32,8 @@ namespace UnitTests
public class SessionTests
{
private const int DefaultPort = 10101;
private const int BadPort = 10102;
private TcpListener server;
[TestFixtureSetUp]
@ -59,11 +62,119 @@ namespace UnitTests
Assert.DoesNotThrow(() => new Session("localhost", DefaultPort));
}
[Test]
public void ConnectBadPort()
{
Assert.Throws<SocketException>(() => new Session("localhost", BadPort));
}
[Test]
public void ConnectCloseLocalhostDefaultPort()
{
Assert.DoesNotThrow(() => new Session("localhost", DefaultPort).Close());
}
private void AcceptAndSendBytes(byte[] data)
{
TcpClient client = server.AcceptTcpClient();
client.GetStream().Write(data, 0, data.Length);
client.Close();
}
[Test]
public void CanSendByte()
{
byte expected = 0xCA;
Session session = new Session("localhost", DefaultPort);
session.Write(expected);
TcpClient client = server.AcceptTcpClient();
int actual = client.GetStream().ReadByte();
Assert.AreEqual(expected, actual);
client.Close();
session.Close();
}
[Test]
public void CanSendBytes()
{
byte[] expected = new byte[] { 0xCA, 0xFE };
Session session = new Session("localhost", DefaultPort);
session.Write(expected);
TcpClient client = server.AcceptTcpClient();
byte[] actual = new byte[2];
client.GetStream().Read(actual, 0, 2);
Assert.AreEqual(expected, actual);
client.Close();
session.Close();
}
[Test]
public void CanReceiveByte()
{
byte expected = 0xCA;
Session session = new Session("localhost", DefaultPort);
AcceptAndSendBytes(new byte[] { expected });
byte actual = session.ReadByte();
session.Close();
Assert.AreEqual(expected, actual);
}
[Test]
public void ReceiveEndOfStream()
{
Session session = new Session("localhost", DefaultPort);
TcpClient client = server.AcceptTcpClient();
client.Close();
Assert.Throws<EndOfStreamException>(() => session.ReadByte());
session.Close();
}
[Test]
public void CanReceiveBytes()
{
byte[] expected = new byte[] { 0xCA, 0xFE };
Session session = new Session("localhost", DefaultPort);
AcceptAndSendBytes(expected);
byte[] actual = session.ReadBytes();
session.Close();
Assert.AreEqual(expected, actual);
}
[Test]
public void HasDataAvailable()
{
Session session = new Session("localhost", DefaultPort);
AcceptAndSendBytes(new byte[] { 0xCA, 0xFE });
session.ReadByte();
Assert.IsTrue(session.DataAvailable);
}
[Test]
public void HasNoDataAvailable()
{
Session session = new Session("localhost", DefaultPort);
AcceptAndSendBytes(new byte[] { 0xCA, 0xFE });
session.ReadByte();
session.ReadByte();
Assert.IsFalse(session.DataAvailable);
}
}
}

View File

@ -28,46 +28,46 @@ namespace NitroDebugger.RSP
/// </summary>
public class GdbClient
{
private Session session;
private Presentation presentation;
public GdbClient(string hostname, int port)
{
this.session = new Session(hostname, port);
this.presentation = new Presentation(hostname, port);
}
public void Close()
{
this.session.Close();
this.presentation.Close();
}
public StopType GetHaltedReason()
{
session.Write("?");
string response = session.Read();
presentation.Write("?");
string response = presentation.Read();
return this.ParseStopResponse(response);
}
public StopType Stop()
{
string response = this.session.Break();
string response = this.presentation.Break();
return this.ParseStopResponse(response);
}
public void Continue()
{
this.session.Write("c");
this.presentation.Write("c");
}
public string Test()
{
this.session.Write("n2000800,4");
return this.session.Read();
this.presentation.Write("n2000800,4");
return this.presentation.Read();
}
public StopType NextStep()
{
this.session.Write("s");
string response = session.Read();
this.presentation.Write("s");
string response = presentation.Read();
return this.ParseStopResponse(response);
}

View File

@ -41,6 +41,7 @@
<Compile Include="GdbClient.cs" />
<Compile Include="RSP\TargetSignals.cs" />
<Compile Include="RSP\TcpClientAdapter.cs" />
<Compile Include="RSP\Presentation.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

View File

@ -0,0 +1,112 @@
//
// Presentation.cs
//
// Author:
// Benito Palacios Sánchez <benito356@gmail.com>
//
// Copyright (c) 2014 Benito Palacios Sánchez
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Text;
namespace NitroDebugger.RSP
{
/// <summary>
/// Represents the Presentation layer.
/// </summary>
public class Presentation
{
private const int MaxWriteAttemps = 10;
private Session session;
private StringBuilder buffer;
public Presentation(string hostname, int port)
{
this.session = new Session(hostname, port);
this.buffer = new StringBuilder();
}
public void Close()
{
this.session.Close();
}
public string Break()
{
this.session.Write(0x03);
return this.Read();
}
public void Write(string message)
{
int count = 0;
int response;
do {
if (count == MaxWriteAttemps)
throw new Exception("Can not send packet successfully");
count++;
Packet packet = new Packet(message);
byte[] data = packet.GetBinary();
this.session.Write(data);
// Get the response
do
response = this.session.ReadByte();
while (response == -1);
// Check the response is valid
if (response != Packet.Ack && response != Packet.Nack)
throw new Exception("Invalid ACK/NACK");
} while (response != Packet.Ack);
}
public string Read()
{
do
this.UpdateBuffer();
while (buffer.Length == 0);
string command = null;
try {
// Get packet
Packet response = Packet.FromBinary(buffer);
command = response.Command;
// Send ACK
this.session.Write(Packet.Ack);
} catch (FormatException ex) {
// Error... send NACK
this.session.Write(Packet.Nack);
}
return command;
}
private void UpdateBuffer()
{
while (this.session.DataAvailable) {
byte[] data = this.session.ReadBytes();
buffer.AppendFormat("{0}", Encoding.ASCII.GetString(data));
}
}
}
}

View File

@ -19,6 +19,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
@ -30,17 +31,17 @@ namespace NitroDebugger.RSP
/// </summary>
public class Session
{
private const int MaxWriteAttemps = 10;
private ITcpClient client;
private Stream stream;
private StringBuilder buffer;
public Session(string hostname, int port)
{
this.client = new TcpClientAdapter(hostname, port);
this.stream = this.client.GetStream();
this.buffer = new StringBuilder();
}
public bool DataAvailable {
get { return this.client.DataAvailable(); }
}
public void Close()
@ -48,69 +49,37 @@ namespace NitroDebugger.RSP
this.client.Close();
}
public string Break()
public void Write(byte data)
{
this.stream.WriteByte(0x03);
return this.Read();
this.stream.WriteByte(data);
}
public void Write(string message)
public void Write(byte[] data)
{
int count = 0;
int response;
do {
if (count == MaxWriteAttemps)
throw new Exception("Can not send packet successfully");
count++;
Packet packet = new Packet(message);
byte[] data = packet.GetBinary();
this.stream.Write(data, 0, data.Length);
// Get the response
do
response = this.stream.ReadByte();
while (response == -1);
// Check the response is valid
if (response != Packet.Ack && response != Packet.Nack)
throw new Exception("Invalid ACK/NACK");
} while (response != Packet.Ack);
this.stream.Write(data, 0, data.Length);
}
public string Read()
public byte ReadByte()
{
do
this.UpdateBuffer();
while (buffer.Length == 0);
int result = this.stream.ReadByte();
if (result == -1)
throw new EndOfStreamException("No more data to read!");
string command = null;
return (byte)result;
}
try {
// Get packet
Packet response = Packet.FromBinary(buffer);
command = response.Command;
public byte[] ReadBytes()
{
List<byte> received = new List<byte>();
// Send ACK
this.stream.WriteByte(Packet.Ack);
} catch (FormatException ex) {
// Error... send NACK
this.stream.WriteByte(Packet.Nack);
byte[] buffer = new byte[1024];
while (this.DataAvailable) {
int read = this.stream.Read(buffer, 0, buffer.Length);
Array.Resize<byte>(ref buffer, read);
received.AddRange(buffer);
}
return command;
}
private void UpdateBuffer()
{
byte[] data = new byte[1024];
while (this.client.DataAvailable()) {
int read = this.stream.Read(data, 0, data.Length);
buffer.AppendFormat("{0}", Encoding.ASCII.GetString(data, 0, read));
}
return received.ToArray();
}
}
}