/// ----------------------------------------------------------------------------------------
/// TCP Demo (Server side)
/// (c) by Vector Informatics GmbH
///
///
/// Hints:
/// - If reference to "System" is missing you have to add it manually.
///
/// - To use SystemVariables, you have to be sure the generated "...sysvars.dll" is within your references.
/// If not, add it by browsing to the file.
/// ----------------------------------------------------------------------------------------
///
using System;
using Vector.Tools;
using Vector.CANoe.Runtime;
using Vector.CANoe.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Collections.Generic;
///
/// ----------------------------------------------------------------------------------------
/// TcpServerNode Measurement Script class.
/// This represent the CANoe-Simulation node, that is a basic MeasurementScript
/// that will initialize a TcpServer, doing the networking stuff.
/// ----------------------------------------------------------------------------------------
///
public class TcpServerNode : MeasurementScript
{
///
/// initialize measurement
///
public override void Initialize()
{
this.Server = new TcpServer();
}
///
/// start measurement
///
public override void Start()
{
if (NSys.TcpServer.AutoStart.Value.Equals(1))
{
NSys.TcpServer.Status.Value = 1; // start server over sysvar to have the panel reflect the state.
}
}
///
/// stop measurement
///
public override void Stop()
{
this.Server.Stop();
}
///
/// shutdown measurement
///
public override void Shutdown()
{
}
///
/// on changing the server's state...
///
[OnChange(typeof(NSys.TcpServer.Status))]
public void OnSysVarStatusChanged()
{
if (NSys.TcpServer.Status.Value.Equals(1))
{
this.Server.Start();
}
else
{
this.Server.Stop();
}
}
///
/// server instance.
///
private TcpServer Server
{
get;
set;
}
}
///
/// ----------------------------------------------------------------------------------------
/// The main TCP server class.
///
/// Demonstrates how to work with the TCP networking classes.
/// ----------------------------------------------------------------------------------------
///
internal class TcpServer
{
///
/// constant integer for the TCP-Buffer's max size.
/// this has nothing to do with a MTU or something. Its just the buffer where the data of the
/// network stream can be buffered as byte array.
///
private const int TCP_DATA_SIZE = 8192;
///
/// constructor
///
public TcpServer()
{
this.Running = false;
}
///
/// start the server.
///
public void Start()
{
// stop server (if already running)
this.Stop();
// start listening on configured port...
var serverIP = System.Net.IPAddress.Parse(NSys.TcpServer.IP.Value);
var serverPort = NSys.TcpServer.Port.Value;
this.TcpListenSocket = new TcpListener(serverIP, serverPort);
this.TcpListenSocket.Start();
this.AwaitIncomingClient();
this.Running = true;
Output.WriteLine("Server Started.");
}
///
/// await incoming client on listen socket...
///
private void AwaitIncomingClient()
{
this.TcpListenSocket.BeginAcceptTcpClient(this.OnTcpClientConnectingCallback, null);
}
///
/// On TCP client connects to listen socket...
///
/// Operation result is given into this callback
private void OnTcpClientConnectingCallback( IAsyncResult result )
{
if (result != null)
{
Output.WriteLine("Server: Incoming client.");
// accept the client...
var clientEntry = new ClientEntry();
this.ConnectedClients.Add(clientEntry);
clientEntry.Client = this.TcpListenSocket.EndAcceptTcpClient(result);
// continue awaiting new clients on listen socket...
this.AwaitIncomingClient();
// say welcome to the client...
Output.WriteLine("Server: Welcome client...");
this.SendResponse(clientEntry, "WELCOME");
// start receiving data from the client's network stream...
this.ReceiveRequest(clientEntry);
}
}
///
/// stop the server
///
public void Stop()
{
if (this.Running)
{
if (this.TcpListenSocket != null)
{
this.TcpListenSocket.Stop();
this.TcpListenSocket = null;
}
this.Running = false;
if (this.ConnectedClients.Count > 0)
{
Output.WriteLine("Server: Stopping...");
}
foreach (var clientEntry in this.ConnectedClients.ToArray())
{
this.DisconnectClient(clientEntry);
}
Output.WriteLine("Server Stopped.");
}
}
///
/// on reading from nwtwork stream done.
///
/// The result of the network operation is given into this callback.
private void OnReadDoneCallback( IAsyncResult result )
{
if (result != null)
{
var clientEntry = (ClientEntry)result.AsyncState;
clientEntry.Client.GetStream().EndRead(result);
if (result.IsCompleted)
{
var dataString = System.Text.Encoding.UTF8.GetString(clientEntry.TcpData).Split('\0')[0];
Output.WriteLine("Server: Received data from client: " + dataString);
// react on request...
this.ReactOnRequest(clientEntry, dataString);
// continue reading from client...
this.ReceiveRequest(clientEntry);
}
else
{
// incomplete... disconnect client.
Output.WriteLine("Server: Reading from network stream not completed.");
this.DisconnectClient(clientEntry);
}
}
}
///
/// Disconnect client with given client info
///
///
private void DisconnectClient( ClientEntry clientEntry )
{
Output.WriteLine("Server: Closing server side client socket.");
clientEntry.Client.Close();
this.ConnectedClients.Remove(clientEntry);
}
///
/// simple server logics acting on received data.
///
///
/// the request to react on.
private void ReactOnRequest( ClientEntry clientEntry, string dataString )
{
// ---------------------------------------------------------------------------
// THIS IS A SIMPLE SERVER LOGICS THAT ACTS ON RECEIVED DATA...
// ---------------------------------------------------------------------------
// send back "OK" followed by the upper-string if the string can be converted to upper,
// else send "NOT CONVERTED".
if (dataString.ToUpper() == dataString)
{
Output.WriteLine("Server: Client data string is not convertible. Send NOT CONVERTED.");
this.SendResponse(clientEntry, "NOT CONVERTED");
}
else
{
Output.WriteLine("Server: converting client's data string and send it back...");
this.SendResponse(clientEntry, "OK");
this.SendResponse(clientEntry, dataString.ToUpper());
}
// ---------------------------------------------------------------------------
// DONE
// ---------------------------------------------------------------------------
}
///
/// send data to the client.
///
///
/// Data as string to send to the client.
private void SendResponse( ClientEntry clientEntry, string data )
{
if (clientEntry.Client != null)
{
try
{
clientEntry.Client.GetStream().BeginWrite(System.Text.Encoding.UTF8.GetBytes(data), 0, data.Length, this.OnWriteDoneCallback, clientEntry);
}
catch (Exception /*e*/)
{
Output.WriteLine("Server: Exception on writing to network stream.");
this.DisconnectClient(clientEntry);
}
}
}
///
/// receive from given client...
///
///
private void ReceiveRequest( ClientEntry clientEntry )
{
if (clientEntry.Client.Connected)
{
Output.WriteLine("Server: Awaiting request...");
Array.Clear(clientEntry.TcpData, 0, clientEntry.TcpData.Length);
try
{
clientEntry.Client.GetStream().BeginRead(clientEntry.TcpData, 0, clientEntry.TcpData.Length, this.OnReadDoneCallback, clientEntry);
}
catch(Exception /*e*/)
{
Output.WriteLine("Server: Exception on reading from network stream.");
this.DisconnectClient(clientEntry);
}
}
else
{
Output.WriteLine("Server: Can't receive from client socket due socket is not connected.");
this.DisconnectClient(clientEntry);
}
}
///
/// writing data to socket done.
///
/// Result of network operation is given into this callback
private void OnWriteDoneCallback( IAsyncResult result )
{
if (result != null)
{
var clientEntry = (ClientEntry)result.AsyncState;
clientEntry.Client.GetStream().EndWrite(result);
if (!result.IsCompleted)
{
// incomplete... disconnect client.
Output.WriteLine("Server: Writing to network stream not completed.");
this.DisconnectClient(clientEntry);
}
}
}
///
/// server running indicator.
///
private bool Running
{
get;
set;
}
///
/// client info. consists of a socket, and a Byte-Buffer for the Tcp data.
///
private class ClientEntry
{
public TcpClient Client = null;
public byte[] TcpData = new byte[TCP_DATA_SIZE];
}
///
/// tcp listen socket accepting incoming clients...
///
private TcpListener TcpListenSocket
{
get;
set;
}
///
/// keep track of all connected clients in case the server stops
/// it has to disconnect all connected clients...
///
private List ConnectedClients = new List();
}